Monday, February 28, 2022

[Shell] Linux: shell实现多线程, Forking / Multi-Threaded Processes | Bash, shell 线程池

 https://justcode.ikeepstudying.com/2018/02/linux-shell%E5%AE%9E%E7%8E%B0%E5%A4%9A%E7%BA%BF%E7%A8%8B-forking-multi-threaded-processes-bash/

[Linux Kernel] Semaphore Sample

#include<stdio.h>

#include<string.h>

#include<pthread.h>

#include<stdlib.h>

#include<unistd.h>

#include <semaphore.h>  /* Semaphore */


pthread_t tid[2];

int counter;

pthread_mutex_t lock;

sem_t mutex;


void* doSomeThing(void *arg)

{

    sem_wait(&mutex);       /* down semaphore */

    unsigned long i = 0;

    counter += 1;

    printf("\n Job %d started\n", counter);


    for(i=0; i<(0xFFFFFFFF);i++);

    printf("\n Job %d finished\n", counter);

    sem_post(&mutex);       /* up semaphore */

    return NULL;


}


int main(void)

{

    int i = 0;

    int err;


    sem_init(&mutex, 0, 1);      /* initialize mutex to 1 - binary semaphore */

    while(i < 2)

    {

        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);

        if (err != 0)

            printf("\ncan't create thread :[%s]", strerror(err));

        i++;

    }

    pthread_join(tid[0], NULL);

    pthread_join(tid[1], NULL);

    sem_destroy(&mutex);

    return 0;

}

======================================

Job 1 started

 Job 1 finished

 Job 2 started

 Job 2 finished

[Linux Kernel] Lets take an example code to study synchronization problems

 #include<stdio.h>

#include<string.h>

#include<pthread.h>

#include<stdlib.h>

#include<unistd.h>


pthread_t tid[2];

int counter;


void* doSomeThing(void *arg)

{

    unsigned long i = 0;

    counter += 1;

    printf("\n Job %d started\n", counter);


    for(i=0; i<(0xFFFFFFFF);i++);

    printf("\n Job %d finished\n", counter);


    return NULL;

}


int main(void)

{

    int i = 0;

    int err;


    while(i < 2)

    {

        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);

        if (err != 0)

            printf("\ncan't create thread :[%s]", strerror(err));

        i++;

    }


    pthread_join(tid[0], NULL);

    pthread_join(tid[1], NULL);


    return 0;

}

==========================================


 Job 1 started

 Job 2 started

 Job 2 finished

 Job 2 finished

[Linux Kernel] Mutex Sample

 Mutex Sample

==========================================================

#include<stdio.h>

#include<string.h>

#include<pthread.h>

#include<stdlib.h>

#include<unistd.h>


pthread_t tid[2];

int counter;

pthread_mutex_t lock;


void* doSomeThing(void *arg)

{

    pthread_mutex_lock(&lock);

    unsigned long i = 0;

    counter += 1;

    printf("\n Job %d started\n", counter);


    for(i=0; i<(0xFFFFFFFF);i++);


    printf("\n Job %d finished\n", counter);


    pthread_mutex_unlock(&lock);


    return NULL;

}

int main(void)

{

    int i = 0;

    int err;


    if (pthread_mutex_init(&lock, NULL) != 0)

    {

        printf("\n mutex init failed\n");

        return 1;

    }

    while(i < 2)

    {

        err = pthread_create(&(tid[i]), NULL, &doSomeThing, NULL);

        if (err != 0)

            printf("\ncan't create thread :[%s]", strerror(err));

        i++;

    }

    pthread_join(tid[0], NULL);

    pthread_join(tid[1], NULL);

    pthread_mutex_destroy(&lock);

    return 0;

}

==========================================================

 Job 1 started

 Job 1 finished

 Job 2 started

 Job 2 finished

[Visual Studio Code] Index

Visual Studio Code settings

使用vscode閱讀C代碼outline不顯示問題

移除行尾空白

Sunday, February 27, 2022

Editor Index

Visual Studio Code

Vim

[靜態檢查工具] cppcheck

Cppcheck是一種用於C和C ++ 程式語言的靜態代碼分析工具。 它是一個多功能工具,可以檢查非標準代碼


首先必須安裝cppcheck

sudo apt-get install cppcheck

cppcheck xxx.c --enable=all

cppcheck -DA xxx.c --enable=all

變數宣告

1
2
3
4
5
6
7
8
int a;
int b;
int c = 1;
int d = 2;

b++;
c++;
d = 4;

(style) Variable 'd' is reassigned a value before the old one has been used.

(style) Unused variable: a

(style) Variable 'b' is not assigned a value.

(style) Variable 'd' is assigned a value that is never used.

(error) Uninitialized variable: b

記憶體管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int *ptra;
int *ptrb;
int *ptrc;
int *ptrd = NULL;

ptra = (int*)malloc(100);
ptrb = (int*)malloc(100);

if (NULL == ptra) {
return *ptrd;
}

*(ptrc + 1) = 5;

free(ptrb);

*(ptrb + 2) = 6;

(error) Null pointer dereference: ptrd

(style) Variable 'ptrc' is not assigned a value.

(error) Memory leak: ptrb (error) Memory leak: ptra

(error) Dereferencing 'ptrb' after it is deallocated / released

(error) Null pointer dereference

(error) Uninitialized variable: ptrc

Buffer邊界

1
2
3
4
5
int i;
int a[10];

for (i = 0; i <= 10; i++)
a[i] = i;

(error) Array 'a[10]' accessed at index 10, which is out of bounds.

目前市面上的靜態程式檢查工具眾多,例如splint, coverity, cppcheck等等,每個工具都有其擅長的部分
就使用過splint和cppcheck的經驗來看例如對於不需要使用或不需要給初始值的狀況cppcheck > splint,但對於buffer overwrite的檢查splint > cppchek,因此若想讓程式更加完善,可以使用多些檢查工具達成目標

Thursday, February 24, 2022

[TV] DVB-S (卫星扫台)

 两个波段

C频段

C波段是频率从 3.4~4.2GHZ的一段频带,作为通信卫星下行传输信号的频段;

由于频率较低,故在阴雨天气不会出现KU波段的雨衰现象

-

KU波段

KU波段频率较高,一般在 10.7~12.75GHZ之间,不易受微波辐射干扰;

KU波段比C波段信号发射功率更集中,所以KU波段的锅尺寸小于C波段的锅

-

波段中的相关概念

IF:中频参数(950M~2150MHZ)

RF:射频信号,即天空中卫星发射的信号

- LOF:局部振荡频率,即LNB的本振频率

这三者中RF的信号是固定的,即扫台出来的TP的频率,LOF是由客户使用的高频头来决定的。IF是由用户设定的LNB TYPE 以及 LNB Frequency等计算出来的。


扫台和设置

盲扫(Blind Scan)

盲扫是一种全频段步进的扫台方式,即根据用户的设置,计算出该LNB的中频参数(IF = |RF - LOF|)然后下给Demod去检测TP;

盲扫的关键在于卫星的天线设置,如:LNB Type, LNB Frequency, LNB Power, 0/22K等;

-

自动扫台/卫星扫台(Auto Scan)

自动扫台也称为卫星扫台,其根据该卫星的已有频点逐一计算出IF下给Demo进行扫台;

IF的计算先根据该TP找到其所属的卫星,再根据该卫星的参数以及该TP的频率计算出对应于此TP的IF;

-

单频点扫台(Transponder Scan)

单频点扫台也是自动扫台中的一部分

-

Antenna Setting(天线设置)

SateliteName (卫星名字)

LNB Type(LNB 类型): C/Ku/User

LNB Frequency(LNB频率)

0/22K:On/Off/Auto

13/18V :On/Off (比较旧的LNB会用到)

LNB Power(LNB 电源):On/Off

DisEqC Mode(DisEqC 模式)

DisEqC1.0: Port A/Port B/Port C/Port D

- DisEqC1.1: Port 0~Port 15

Scan Setting (扫台设置)

Network Search(网络搜索): On/Off

Polarization(极化): H/V (H-水平极化,V-垂直极化)

Channel type(信道类型): FTA/Scramble/All

- Scan Type(搜索类型):Auto/Blind/Manual

卫星信号的介绍

卫星参数中有三个重要的参数:频率、符号率、极化方式

频率:卫星里面的转发器(TP),自上向下传输给用户的频率,也称为下行频率,即我们平时用于沟通的卫星频率;如 10714H 22000,其频率就是10714Hz;

符号率:单位时间内转发器传输的数据量,即当某个TP携带的节目数据越多,该数值越大;如10714H 22000,22000参数即为该TP的符号率;

极化方式:为了增大无线频段的利用率,防止相互干扰,卫星信号传输采用了垂直极化与水平极化两种方式,这样可以使特定范围的频段使用率翻倍;如10714 H 22000和10714 V 22000两个转发器,其频率是一样的,但是由于采用了两种不同的极化方式,因此可以同时在同一个频率点上传输,提高了使用率;

-

卫星信号的传输与接收

LNB

低噪声下变频器,可以实现信号降频。LNB在接收卫星信号后,会先对卫星信号进行缩放,然后在进行放大,以此实现低噪声信号的过滤;另外LNB内部会自动产生一个高频率的振动电平,成为本振,待接收到卫星信号后,该振动信号会与卫星信号进行叠加,类似相对运动,叠加后的信号频率即两者的相对频率,以此达到降频的目的。

LNB除了降低卫星频率之外,还用于切换水平极化和垂直极化信号,达到两种极化方式的节目可以随意切换的目的;


-

-

相关设置条目

Tone Burst

该条目是一个2切1的切换开关,用于两个卫星之间的相互切换;使用该条目必须建立在有两个锅同时接在一个2切1的切换开关基础上,通过该条目选择两个卫星中的其中一个,选择原理是使用22k信号的开关来切换;


SID (服務信息描述符)

原文链接:https://blog.csdn.net/super_marie/article/details/82430598

Monday, February 21, 2022

[c] delay vs. sleep

delay() 是循环等待,该进程还在运行,占用处理器。 

sleep() 不同,它会被挂起,把处理器让给其他的进程。


sleep() 参数指定暂停时间,单位是 s 

delay() 参数指定暂停时间,单位是 ms


C/C++ sleep 用法與範例


Sunday, February 20, 2022

[Linux Kernel] Linux IO模式及 select、poll、epoll 详解

select,poll,epoll本質上都是同步I/O,因爲他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的

select

本质上是通过设置或检查存放fd标志位的数据结构进行下一步处理

select 的缺點

1. 需要維護一個用來存放大量fd的數據結構,這樣會使得用戶空間和內核空間在傳遞該結構時複製開銷大

2. 對socket進行掃描時是線性掃描,即採用輪詢的方法,效率較低

3. select 支持的文件描述符數量太小了,32位機默認是1024個。64位機默認是2048.

poll

poll 的缺點:

1. 大量的fd的數組被整體複製於用戶態和內核地址空間之間,而不管這樣的複製是不是有意義

2. 如果報告了fd後,沒有被處理,那麼下次poll時會再次報告該fd

epoll

1. epoll支持水平觸發和邊緣觸發,最大的特點在於邊緣觸發,並且只會通知一次。

2. 還有一個特點是,epoll使用“事件”的就緒通知方式,通過epoll_ctl註冊fd,一旦該fd就緒,內核就會採用類似callback的回調機制來激活該 fd,epoll_wait 便可以收到通知

沒有最大併發連接的限制,能打開的FD的上限遠大於1024(1G的內存上能監聽約10萬個端口

內存拷貝,利用mmap()文件映射內存加速與內核空間的消息傳遞;即epoll使用mmap減少複製開銷

參考資料:

IO多路复用之select总结

IO多路复用之epoll总结

IO多路复用之poll总结


Wednesday, February 16, 2022

[c] notify_all

 #include <iostream>

#include <condition_variable>

#include <thread>

#include <chrono>

 

std::condition_variable cv;

std::mutex cv_m; // This mutex is used for three purposes:

                 // 1) to synchronize accesses to i

                 // 2) to synchronize accesses to std::cerr

                 // 3) for the condition variable cv

int i = 0;

 

void waits()

{

    std::unique_lock<std::mutex> lk(cv_m);

    std::cerr << "Waiting... \n";

    cv.wait(lk, []{return i == 1;});

    std::cerr << "...finished waiting. i == 1\n";

}

 

void signals()

{

    std::this_thread::sleep_for(std::chrono::seconds(1));

    {

        std::lock_guard<std::mutex> lk(cv_m);

        std::cerr << "Notifying...\n";

    }

    cv.notify_all();

 

    std::this_thread::sleep_for(std::chrono::seconds(1));

 

    {

        std::lock_guard<std::mutex> lk(cv_m);

        i = 1;

        std::cerr << "Notifying again...\n";

    }

    cv.notify_all();

}

 

int main()

{

    std::thread t1(waits), t2(waits), t3(waits), t4(signals);

    t1.join(); 

    t2.join(); 

    t3.join();

    t4.join();

}


Waiting... 
Waiting... 
Waiting... 
Notifying...
Notifying again...
...finished waiting. i == 1
...finished waiting. i == 1
...finished waiting. i == 1

Sunday, February 13, 2022

[Linux Kernel] Mutex Semaphore

Mutex Semaphore 2 個不同情境

Semaphore 在意共享資源數量控管 (不限制哪個thread)

Mutex 持有者 (thread) 控管

Lets take an example code to study synchronization problems

Mutex

Semaphore 


[Linux Kernel] atomic

需要解決什麼問題?

讀一個位於 memory 中的變數的值到暫存器中

修改該變數的值(也就是修改暫存器中的值)

將暫存器中的數值寫回memory中的變數值

如果這個操作序列是序列化的操作(在一個thread中序列執行),那麼一切OK,然而,世界總是不能如你所願。

在多CPU體系結構中,執行在兩個CPU上的兩個核心控制路徑同時並行執行上面操作序列,有可能發生下面的場景:

兩個 thread, 一個對 sum 加 1, 一個對 sum 減 1, 兩個都做三次, sum 初始值為 0. 正常來說最後 sum 值應該還是 0.




我們故意在計算完後, 呼叫 schedule() 強迫切換 thread, 可以發現, 這樣執行起來, sum 的值並非預期. 最後變成了 -2, 而非 0

[90334.635512] hello_init: pid=14949

[90334.635699] hello_init: pid=14949, before sum=0

[90334.635714] work_handler1: pid=12886, sum=1

[90334.635717] work_handler1: pid=12886, sum=2

[90334.635719] work_handler2: pid=13336, sum=0

[90334.635722] work_handler1: pid=12886, sum=1

[90334.635724] work_handler2: pid=13336, sum=-1

[90334.635727] work_handler2: pid=13336, sum=-2

[90334.635773] hello_init: pid=14949, after sum=-2



參考:

https://njiot.blogspot.com/2019/12/linux-kernel-spinlock-atomic-ameba.html

https://www.itread01.com/content/1544597825.html


[Linux Kernel] pipeline

沒有管線化(pipeline)執行跟有管線化執行的差別

1. 沒有管線化執行不會發生危障(hazard

2. 沒有管線化執行的效能較差.

3. 管線化執行能增加指令同時執行的數量

4. 單一指令的執行時間都是固定的

Linux 效能分析工具: Perf

[c] popcnt指令简介

popcnt是“population count”的缩写,该操作一般翻译为“位1计数”,即统计有多少个“为1的位”。例如,十六进制数“FF”,它有8个为1的位,即“popcnt(0xFF) = 8”。

popcnt主要应用在密码学与通信安全,例如计算汉明重量(Hamming weight)

 

https://www.cnblogs.com/zyl910/archive/2012/11/02/testpopcnt.html

[c++] Overloading class member access (C++ only)

 #include <iostream>

using namespace std;


struct Y {

  void f() {

    cout << "call f()" << endl;

  };

};


struct X {

 Y* ptr;

 Y* operator->() {

   return ptr;

 };

};


int main() {

  X x;

  x->f();

}

call f()

[c++] Overloading subscripting (C++ only)

 #include <iostream>

using namespace std;


template <class T> class MyArray {

private:

  T* storage;

  int size;

public:

  MyArray(int arg = 10) {

    storage = new T[arg];

    size = arg;

  }


  ~MyArray() {

    delete[] storage;

    storage = 0;

  }


  T& operator[](const int location) throw (const char *);

};


template <class T> T& MyArray<T>::operator[](const int location)

  throw (const char *) {

    if (location < 0 || location >= size) throw "Invalid array access";

    else return storage[location];

}


int main() {

  try {

     MyArray<int> x(13);

     x[0] = 45;

     x[1] = 2435;

     cout << x[0] << endl;

     cout << x[1] << endl;

     x[13] = 84;

  }

  catch (const char* e) {

    cout << e << endl;

  }

}

================================================

45

2435

Invalid array access

[c++] Overloading binary operators (C++ only)

 #include <iostream>

using namespace std;

struct X {

  // member binary operator

  void operator*(int) { 

    cout << "void operator*(int)" << endl;

  }

};

// non-member binary operator

void operator*(X, float) { 

    cout << "void operator*(X, float)" << endl;

}

int main() {

  X x;

  int y = 10;

  float z = 10;


  x * y;

  x * z;

}



void operator*(int)

void operator*(X, float)

Saturday, February 12, 2022

[c++] Overloading increment and decrement operators (C++ only)

 In the following example, the increment operator is overloaded in both ways:

=========================================================

class X {

public:

  // member postfix x++

  void operator++(int) { };

};

class Y { };

// nonmember postfix y++

void operator++(Y&, int) { };


int main() {

  X x;

  Y y;


  // calls x.operator++(0)

  // default argument of zero is supplied by compiler

  x++;

  // explicit call to member postfix x++

  x.operator++(0);

  // calls operator++(y, 0)

  y++;

  // explicit call to non-member postfix y++

  operator++(y, 0);

}

[c++] Overloading unary operators (C++ only)

An overloaded unary operator may return any type. The following example overloads the ! operator:


#include <iostream>

using namespace std;


struct X { };


void operator!(X) {

  cout << "void operator!(X)" << endl;

}


struct Y {

  void operator!() {

    cout << "void Y::operator!()" << endl;

  }

};


struct Z { };


int main() {

  X ox; Y oy; Z oz;

  !ox;

  !oy;

//  !oz;

}

  !oy;

//  !oz;

}

Friday, February 11, 2022

example code

 #include <iostream>

#include <vector>

using namespace std;


typedef struct _AppsData

{

    _AppsData(const _AppsData &) = default;

    _AppsData() = default;

    void clear() {

        cout << "AppsData clear" << endl;

        appId = "String APP ID";

    }

    int operator == (struct _AppsData & appData) {

      std::string tagAppId;

      tagAppId = appData.appId;

      cout << "_AppsData operator == appData:" << tagAppId << endl;

      return 0;

    }

    struct _AppsData & operator = (struct _AppsData & appData) {

      cout << "_AppsData operator=" << endl;

      appId = appData.appId;

      return *this;

    }

    std::string appId;

} AppsData;


typedef struct _APPInfo

{

    std::string strAppCRID;


}APPInfo, *PAPPInfo;


int test1(const std::string appid)

{

    std::vector<AppsData>::iterator cc;

    if(appid.size() == 0) {

      cout << "appid size = 0" << endl;

    }

    return 0;

}


int main()

{

   

    bool b = false;

    PAPPInfo    pAppInfo;

    AppsData appData;

    cout << "Start main" << endl;

    appData.clear();

    cout << "Do pAppInfo->strAppCRID appData.appId:" << appData.appId << endl;

    pAppInfo->strAppCRID = appData.appId;

    cout << "Start test1" << endl;

    b = !(bool)test1(std::string(pAppInfo->strAppCRID));

    cout << "end b" << b;

    return 0;

}

[c++] Overloading assignments (C++ only)

struct X {

  int data;

  X& operator=(X& a) { return a; }

  X& operator=(int a) {

    data = a;

    return *this;

  }

};


int main() {

  X x1, x2;

  x1 = x2;      // call x1.operator=(x2)

  x1 = 5;       // call x1.operator=(5)

}

==============================================================

#include <iostream>

using namespace std;


struct A {

  A& operator=(char) {

    cout << "A& A::operator=(char)" << endl;

    return *this;

  }

  virtual A& operator=(const A&) {

    cout << "A& A::operator=(const A&)" << endl;

    return *this;

  }

};


struct B : A {

    B& operator=(char) {

      cout << "B& B::operator=(char)" << endl;

      return *this;

    }

    virtual B& operator=(const A&) {

      cout << "B& B::operator=(const A&)" << endl;

      return *this;

    }

};


struct C : B { };


int main() {

  B b1;

  B b2;

  A* ap1 = &b1;

  A* ap2 = &b1;

  *ap1 = 'z';

  *ap2 = b2;


  C c1;

//  c1 = 'z';

}

A& A::operator=(char)

B& B::operator=(const A&)


The assignment *ap1 = 'z' calls A& A::operator=(char). Because this operator has not been declared virtual, the compiler chooses the function based on the type of the pointer ap1.

The assignment *ap2 = b2 calls B& B::operator=(const &A). Because this operator has been declared virtual, the compiler chooses the function based on the type of the object that the pointer ap1 points to. 

The compiler would not allow the assignment c1 = 'z' because the implicitly declared copy assignment operator declared in class C hides B& B::operator=(char).


[c++] struct和class的區別

C++中的struct是对C中的struct进行了扩充,所以增加了很多功能,主要的区别如下图所示:

cc++
成員函數不能可以
靜態成員不能可以
防控屬性預設 public,不能修改public/private/protected
繼承關係不可以繼承可以有類或其它結構體繼承
初始化不能直接初始化數據成員可以

使用過程:

在C中使用结构体时需要加上struct,或者对结构体使用typedef取别名,而C++可直接使用,例如:

================================================================

结构体声明,C和C++使用同一个

struct Student

{

int  iAgeNum;

string strName;

}

typedef struct Student Student2;//C中取别名


struct  Student  stu1; //C中正常使用

Student2   stu2; //C中通过取别名的使用

Student    stu3; //C++使用

================================================================

C与C++实际上编程思想的区别,C是面向过程,C++面向对象

所以在C中结构体就是不同类型数据的集合,并不涉及算法和操作。

而C++是把数据变量及对这些数据变量的相关算法和操作给封装起来


區別 1:内部成员变量及成员函数的默认防控属性

struct 默认防控属性是 public 的,而 class 默认的防控属性是 private 的,例如:

================================================================

struct A

{

int iNum;

}

class B

{

int iNum;

}

A a;

a.iNum = 2; //没有问题,默认防控属性为public

B b;

b.iNum = 2; //编译出错,默认防控属性为private

================================================================

區別 2:默认的继承权限

在继承关系,struct默认是public的,而class是private,例如:

================================================================

struct A

{

int   iAnum;

}

struct B : A

{

int   iBnum;

}

A a;

a.iAnum = 1; //在struct情况下是正确的,在class情况下是错误的

================================================================

struct的情况下B是默认public继承A的。如果将上面的struct改成class,那么B是private继承A的。

上面的列子都是struct继承struct,class继承class,那么class与struct继承会怎样呢?结论是:默认的防控属性取决于子类而不是基类,例如:

================================================================

struct A{};

class B : A {}; //默认为private继承

struct C : B{}; //默认为public继承

================================================================

所以我们在写代码的时候,为了不引起歧义,最好指明继承的方式,而不要用默认的继承,例如:

================================================================

class B : public A{};

struct B : public A{};

================================================================

區別 3:模板中的使用

class这个关键字还可用于定义模板参数,就像typename。但是strcut不用与定义模板参数,例如:

template<typename T> ok

template<class T> ok

template<struct T> not ok

================================================================

template< typename T, typename Y > //可以把typename 换成 class

int  Func( const T& t, const Y& y )

{

//TODO

}

================================================================

區別 4:模板中的使用

struct在C语言中:

在C语言中,我们知道struct中是一种数据类型,只能定义数据成员,不能定义函数,这是因为C语言是面向过程的,面向过程认为数据和操作是分开的,所以C语言中的struct可以直接使用大括号对所有数据成员进行初始化

================================================================

struct test

{

    int a;

    int b;

};

//初始化

test A={1,2};//完全可以

================================================================

在C++中:

在C++中对struct的功能进行了扩展,struct可以被继承,可以包含成员函数,也可以实现多态,当用大括号对其进行初始化需要注意:

当struct和class中都定义了构造函数,就不能使用大括号对其进行初始化
若没有定义构造函数,struct可以使用{ }进行初始化,而只有当class的所有数据成员及函数为public时,可以使用{ }进行初始化
所以struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。
加入一个构造函数或是一个虚函数会使strcut更体现出一种对象的特性,而使{}操作不在有效

Thursday, February 10, 2022

[c++] struct 當成 class 寫法

當成 class 寫法 


typedef struct _nike_recommend_data{


    void clear() {

        strTitle = "";

        if (!vecTitle.empty()) {

             vecTitle.erase (vecTitle.begin(), vecTitle.end() - 1);

        }

        std::vector<TitleDataT>().swap(vecTitle);


    }

    

    int operator == (const struct _nike_recommend_data & recData) {

         return (strTitle == recData.strTitle) ? 1 : 0;

    }


    struct _nike_recommend_data & operator = (struct _nike_recommend_data & recData) {

        strTitle = recData.strTitle;

        if ((vecTitle.size() == 0)  || (vecTitle.size() != recData.vecTitle.size())) {

            std::vector<TitleDataT>().swap(vecTitle);

            for (std::vector<TitleDataT>::iterator it = recData.vecTitle.begin(); 

                    it != recData.vecTitle.end(); it++) {

                vecTitle.push_back(*it);

           }

        } else {

            std::vector<TitleDataT>::iterator it = recData.vecTitle.begin();

            std::vector<TitleDataT>::iterator itThis = vecTitle.begin(); 

            for (;  it != recData.vecTitle.end(); it++, itThis++) {

                *itThis = *it;

            }

        }

        return *this;

    }

    std::string strTitle;

    std::vector<TitleDataT> vecTitle;

}NikeRecommendData;

Monday, February 7, 2022

[C++] 為何要用泛型

1 個项目里,一套代码支持了

不同网络通讯;

不同应用层协议;

不同IDL;

不同数据结构;

不同压缩类型;

不同功能的插件;

纵向可以增加新的层次,横向也可以随便扩展新功能。

这都是用各种天花乱坠的泛型方式来实现的代码统一,有些实现方式系统来说也许不叫泛型编程,但目的都是为了动态可扩展地解耦合

项目在这里↓↓↓ 然后我们开始看看为什么、和怎么用:https://github.com/sogou/srpc


Sunday, February 6, 2022

[c++] 命名空间

C++ 標準程式庫中的所有識別符號都被定義於一個名為 std 的 namespace中。

早些的編碼將標準庫功能定義在全域性空間裡(所以並不需要註明使用什麼名稱空間),宣告在帶 .h 字尾的標頭檔案裡。

C++標準為了和 C 區別開,也為了正確地使用名稱空間,規定標頭檔案不使用字尾 .h。

因此,當使用<iostream.h>時,相當於在 C 中呼叫庫函式,使用的是全域性名稱空間,也就是早期的 C++ 實現。當使用<iostream>時,該標頭檔案沒有定義全域性名稱空間,必須使用 namespace std,指明在哪裡的名稱空間,這樣才能使用類似於 cout 這樣的 C++ 識別符號。

由於 namespace 的概念,使用 C++ 標準程式庫的任何識別符號時,可以有三種選擇:

1.直接指定識別符號

例如: std::iostream 而不是 iostream。

完整語句如下: std::cout << std::hex << 3.4 << std::endl;


2. 定义命名空间

命名空间的定义使用关键字 namespace,后跟命名空间的名称,如下所示:

namespace namespace_name {

   // 代码声明

}

为了调用带有命名空间的函数或变量,需要在前面加上命名空间的名称,如下所示:

name::code;  // code 可以是变量或函数

让我们来看看命名空间如何为变量或函数等实体定义范围:

================================================

#include <iostream>

using namespace std;


// 第一个命名空间

namespace first_space{

   void func(){

      cout << "Inside first_space" << endl;

   }

}

// 第二个命名空间

namespace second_space{

   void func(){

      cout << "Inside second_space" << endl;

   }

}

int main ()

{

   // 调用第一个命名空间中的函数

   first_space::func();

   // 调用第二个命名空间中的函数

   second_space::func(); 

   return 0;

}

================================================

当上面的代码被编译和执行时,它会产生下列结果:

================================================

Inside first_space

Inside second_space

================================================

3. using 指令

您可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。

================================================

#include <iostream>

using namespace std;


// 第一个命名空间

namespace first_space{

   void func(){

      cout << "Inside first_space" << endl;

   }

}

// 第二个命名空间

namespace second_space{

   void func(){

      cout << "Inside second_space" << endl;

   }

}

using namespace first_space;

int main ()

{

   // 调用第一个命名空间中的函数

   func();

   return 0;

}

================================================

当上面的代码被编译和执行时,它会产生下列结果:

================================================

Inside first_space

================================================

using 指令也可以用来指定命名空间中的特定项目。例如,如果您只打算使用 std 命名空间中的 cout 部分,您可以使用如下的语句:

================================================

using std::cout;

================================================

随后的代码中,在使用 cout 时就可以不用加上命名空间名称作为前缀,但是 std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀,如下所示:

================================================

#include <iostream>

using std::cout;

int main ()

{

   cout << "std::endl is used with std!" << std::endl;

   return 0;

}

================================================

当上面的代码被编译和执行时,它会产生下列结果:

std::endl is used with std!

不连续的命名空间

命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。

所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。下面的命名空间定义可以是定义一个新的命名空间,也可以是为已有的命名空间增加新的元素:

================================================

namespace namespace_name {

   // 代码声明

}

================================================

嵌套的命名空间

命名空间可以嵌套,您可以在一个命名空间中定义另一个命名空间,如下所示:

================================================

namespace namespace_name1 {

   // 代码声明

   namespace namespace_name2 {

      // 代码声明

   }

}

================================================

您可以通过使用 :: 运算符来访问嵌套的命名空间中的成员:

// 访问 namespace_name2 中的成员

using namespace namespace_name1::namespace_name2;


// 访问 namespace:name1 中的成员

using namespace namespace_name1;

================================================

在上面的语句中,如果使用的是 namespace_name1,那么在该范围内 namespace_name2 中的元素也是可用的,如下所示:

================================================

#include <iostream>

using namespace std;


// 第一个命名空间

namespace first_space{

   void func(){

      cout << "Inside first_space" << endl;

   }

   // 第二个命名空间

   namespace second_space{

      void func(){

         cout << "Inside second_space" << endl;

      }

   }

}

using namespace first_space::second_space;

int main ()

{

   // 调用第二个命名空间中的函数

   func();

   

   return 0;

}

================================================

当上面的代码被编译和执行时,它会产生下列结果:

Inside second_space


Tuesday, February 1, 2022

[Car ] AUTOSAR 汽車開放系統架構

一、AutoSar 的背景介紹

AUTOSAR是AUTOmotive Open System Architecture(汽车开放系统架构)的首字母缩写,是由全球各大汽车整车厂、汽车零部件供应商、汽车电子软件系统公司联合建立的一套标准协议,是对汽车技术开发一百多年来的经验总结

AUTOSAR目前分为两种:

Classic Platform AUTOSAR

Adaptive Platform AUTOSAR,也称为 CP 和 AP

獲取AUTOSAR的規範文件:https://www.autosar.org//standards

在 AutoSAR 之前,軟件耦合嚴重如下:

1. 開發效率低下

2. 開發週期長

3. 代碼合作開發難,維護難

4. 重用性差 (換平台,必需重寫)

5. 代碼量增加,代碼質量下降

進一步修正後,還是有缺點:

1. 對不同客戶,由於各家需求不同,重用性依然不好

2. 耦合也存在,同時該方法的優劣和架構師的能力直接掛鉤


AUTOSAR的计划目标主要有三个:

1)建立分层的体系架构

2)为应用程序的开发提供方法论

3)制定各种应用接口规范


有了以上了解,拿到規範文件後,你需要明確你的工作內容在整個產品生命週期的位置。簡單介紹下幾個流程概念。

OEM

TIER1

TIER2

整車廠

一級供應商

二級供應商

賓士、寶馬等(做整車的裝配工作)

大陸、博世等(給OEM供應ECU等)

英飛凌、NXP等(為TIER1供應零件,比如ECU上的晶片、電路板等)

按產品開發流程的順序大致梳理:

1. 整車廠以EE架構設計和應用層功能設計為主,所以如果你身在OEM中,你只需要著重瞭解AUTOSAR的方法論和基於方法論的SWC設計即可。這兩點說著簡單,其實並非我們想象中那麼簡單。方法論本身就是非常巨集觀的概念,想要把控產品流程,能為TIER1提供開啟需求文件,這本身就要對功能和下游工作十分了解,才能有高質量的輸出;

2. TIER1涉及AUTOSAR的工作分工就比較多了。

如果你是系統工程師,著重研究功能演算法的實現,那麼你需要對SWC的升級瞭如指掌,深入理解;如果你是軟體架構工程師,對於上游OEM提供的需求文件要有巨集觀概念,所以也要對方法論和SWC審計十分了解;

如果你是基礎軟體工程師,需要整個團隊協同實現:底層驅動工程師要深入學習晶片的抽象層MCAL應用;BSW協議棧工程師要熟悉OS,ComStack,DiagStack,Memory Stack,WgdStack等協議棧應用細節;複雜驅動工程師,要對AUTOSAR針對CDRV的介面定義方式等深入研究;如果整合工程師,要十分清楚RTE的執行整合和相關應用配置;

3. TIER2要深入研究的內容和TIER1的BSW工程師側重內容相似,主要圍繞晶片MCAL和基礎軟體協議棧展開。

4. 除了以上三類產品開發流程上的角色外,其實還有一個重要角色的存在:工具供應商。瞭解了AUTOSAR架構和實現過程後,大家可能會看到很多arxml格式的配置檔案的製作都離不開工具的支援,以及編譯環境、建模工具等,都離不開一直走在超前道路上的工具供應商。

AUTOSAR的開發流程:






二、AUTOSAR的分层模型

为了实现应用程序和硬件模块之间的分离,AUTOSAR架构被抽象成四层,由上至下依次为:

应用层(Application Layer)

运行时环境层(Run Time Environment,即 RTE)

基础软件层(Basic Software,即BSW)

微控制器层(Microcontroller)

目的:

OEM可以专注于开发特定的、有竞争力的应用层软件(位于RTE之上),另一方面,它使OEM所不关心的基础软件层(位于RTE之下)得到标准化。

三、AUTOSAR的方法论

AUTOSAR设计和开发流程分为三个阶段:

第一阶段:定义系统配置文件

这是系统设计者或架构师的任务。

包括选择硬件和软件组件,定义整个系统的约束条件。AUTOSAR通过使用信息交换格式和模板描述文件来减少初始系统设计时的工作量。系统配置的输入是XML类型的文件,输出是系统配置描述文件,系统配置的主要作用是把软件组件的需求映射到ECU上

第二阶段:根据系统配置

描述文件提取单个ECU资源相关的信息,提取出来的信息生成ECU提取文件。根据这个提取文件对ECU进行配置,例如操作系统任务调度,必要的BSW模块及其配置,运行实体到任务的分配等,从而生成ECU配置描述文件。该描述文件包含了特定ECU的所有信息。

第三阶段:生成代码

基于ECU配置描述文件指定的配置来产生代码、编译代码,并把相关代码链接起来形成可执行文件。

具体的开发流程如下

1.编写系统配置输入描述文件

在AUTOSAR中,所有的描述文件都是XML类型的文件。系统配置输入文件包含三部分内容: 

1)软件组件描述,定义了每个涉及的软件组件的接口内容,如数据类型,端口,接口等。 

2)ECU资源描述,定义了每个ECU的资源需求,如处理器、存储器、外围设备、传感器和执行器等。

 3)系统约束描述,定义了总线信号,软件组件间的拓扑结构和映射关系。

2.系统配置

系统配置的功能主要是在资源和时序关系的前提下,把软件组件映射到各个ECU上,然后借助系统配置生成器生成系统配置描述文件。这个描述文件包括总线映射之类的所有系统信息以及软件组件与某个ECU的映射关系。

3. 提取特定ECU的描述

从系统配置描述文件中提取出与各个ECU相关的系统配置描述信息,提取的信息包括ECU通信矩阵、拓扑结构、映射到该ECU上的所有软件组件,并将这些信息放在各个ECU的提取文件中。

4. ECU配置

ECU配置主要是为该ECU添加必要的信息和数据,如任务调度、必要的基础软件模块及其配置、运行实体及任务分配等,并将结果保存在ECU配置描述文件中,该文件包含了属于特定ECU的所有信息,换言之,ECU上运行的软件可根据这些信息构造出来。

5. 生成可执行文件

根据ECU配置描述文件中的配置信息,生成RTE和基础软件配置代码,完成基础软件和软件组件的集成,最终生成ECU的可执行代码。

四、AUTOSAR的接口类型

通过RTE实现AUTOSAR软件组件之间以及应用层与基础软件之间的通信前提是:

软件组件之间必须有标准的AUTOSAR接口。

AUTOSAR规范把汽车电子领域内的一些典型的应用划分为若干个由一个或多个软件组件组成的模块,并详细定义了这些软件组件相关的参数,例如名称、范围、类型等。

AUTOSAR定义了三种接口:

1. 标椎化接口(Standardized Interface)

标椎化接口是AUTOSAR规范中用C语言定义的API。这些接口用于ECU内部BSW模块之间,RTE和操作系统之间或者RTE和COM模块之间;

2. AUTOSAR接口(AUTOSAR Interface)

AUTOSAR接口是一种与应用相关的接口,与RTE一并生成。

基于AUTOSAR接口的端口可以用于软件组件(Software Component,SWC)之间或者软件组件与ECU固件之间(例如复杂驱动)的通信;

3. 标准化的AUTOSAR接口(Standardized AUTOSAR Interface)

标准化AUTOSAR接口是一种特殊的AUTOSAR接口。

这些在AUTOSAR规范中定义过的接口被SWC用于访问AUTOSAR BSW模块提供的服务,比如ECU管理模块或者诊断事件管理模块;




五、AUTOSAR的基础软件层

对基础软件层BSW进行进一步的细分,划分为4层:

1. 微控制器抽象层(MicroController Abstraction Layer,即MCAL),它位于BSW的最底层,包含了跟硬件相关的驱动程序、软件模块与直接访问微控制器内部和外围的设备,可以用来访问内存、通信和I/O等;

2. ECU抽象层(ECU Abstraction Layer),位于微控制器抽象层之上,对接微控制器抽象层所提供的驱动程序,并同时包含对外部设备的驱动程序,然后负责向上提供统一的访问接口实现对通信、内存或者I/O的访问,从而使得上层模块无须考虑这些资源由微处理器提供还是由外部设备提供

3. 服务层(Service Layer),提供各种类型的后台服务,例如网络服务、内存管理和总线通信服务等,操作系统就位于这一层。服务层是基础软件的最高层,同时与应用程序也有关联。虽然对I/O信号的访问由ECU抽象层覆盖,但服务层负责提供以下接口:操作系统的功能、车辆网络通信管理服务、存储器服务(NVRAM管理)、诊断服务(包括UDS通信、错误存储和故障处理)、ECU状态管理,模式管理、逻辑和时间程序流监控(Wdg管理器)、密码服务(密码服务管理);

4. 复杂驱动层(Complex Drivers Layer,即CCD),跨越于微控制器硬件层和RTE之间,其主要任务是整合具有特殊目的且不能用MCAL进行配置的非标准功能模块,将该部分功能嵌入到AUTOSAR基础软件层中,从而实现处理复杂传感器以及执行器的特定功能和时间要求,提供集成特殊用途的功能,例如设备驱动程序,在AUTOSAR中未规定的、具有非常高的时间限制或用于迁移等目的 


所以,总结一下,基础软件层BSW的组件及其功能对应如下:

1. 系统:提供标准化的规定(针对操作系统、定时器以及错误存储器)、ECU特定的服务(ECU状态管理、看门狗管理)和库函数

2. 内存:对内部和外部的内存(非易失性存储器)的访问入口进行标准化

3. 通信:对汽车网络系统、ECU通信系统以及ECU内部软件的访问入口进行标准化;

4. 输入/输出:对传感器、执行器以及ECU外设的访问入口进行标准化

同时,对BSW中的各个子层,还可以从功能上将其中的各个模块划分为4种类型

1. 驱动模块Driver

  1.1 内部驱动

内部器件位于微控制器(单片机)的内部,例如内部EEPROM、内部CAN控制器、内部ADC模块等。
内部驱动程序就是针对单片机内部器件资源的驱动程序,这部分驱动程序属于微控制器抽象层(MCAL)。

  1.2 外部驱动

外部器件是指单片机外部的ECU硬件,比如外部EEPROM、外部看门狗、外部Flash等。

外部驱动程序就是针对单片机外部硬件资源的驱动程序,属于ECU抽象层。外部驱动程序需要通过微控制器抽象层(MCAL)驱动程序来实现对外部器件的驱动。

这种方法下AUTOSAR也支持嵌入在系统基础芯片(SBCs)中的组件,像收发器以及看门狗等。例如,使用SPI通信接口的外部EEPROM驱动程序是通过SPI总线处理程序来驱动外部EEPROM的。

但是有一种例外,对于和内存映射相关的外部器件(如外部Flash存储器),其驱动程序是可以直接对微控制器进行存取访问的,所以这部分驱动程序属于微控制器抽象层(MCAL)。 

2. 接口模块Interface

接口模块包含了对其次级模块进行抽象的功能,比如对一个特定功能的硬件进行抽象。它提供一个通用的接口函数(API)来访问一种特定的器件类型,且与该类型器件的数目无关,同时也与器件的具体硬件实现无关。

接口模块不会改变数据的内容。一般来说,接口属于ECU抽象层。例如,CAN通信系统的接口模块提供一个通用的接口函数来访问CAN通信网络,并且与ECU上CAN控制器的数目以及硬件实现无关。

3. 处理模块Handler

处理模块是一个专用的接口,它控制一个或多个客户端对一个或多个驱动程序进行并行、多重以及异步地访问。也就是说,它起着缓冲、队列、仲裁以及多路复用的功能。同时,处理程序也不会改变数据本身的内容。处理模块通常会并入驱动程序或是接口模块中(如SPIHandlerDriver、ADC Driver等)

4. 管理器Manager

管理器为多重的客户端提供特定的服务。当单纯的处理程序不能满足对多重的客户端进行抽象时,就需要用到管理器来进行处理。除了处理功能外,管理器还可以对数据内容进行评估、改变或是适应数据内容。

一般而言,管理器属于服务层。例如,非易失性随机存储器(NVRAM)的管理器负责对内部或是外部存储设备进行并行的访问,如Flash、EEPROM存储器等。同时,它也可以完成分布式并且可靠的数据存储、数据校验以及默认值的规定等。


1. 微控制器抽象层

微控制器器抽象层位于AUTOSAR分层模型中的BSW的最底层,它包含内部驱动,可以直接访问微控制器和片内外设。进一步的,MCAL又可分为微控制器驱动模块组(Microcontroller Drivers)、存储器驱动模块组(Memory Drivers)、通信驱动模块组(Communication Drivers)、以及I/O 驱动模块组(I/O Drivers)。各个部分又由具体的与微控制器硬件相对应的驱动模块组成,如下图:


1.1、微控制器驱动

微控制器驱动由通用定时器驱动(General Purpose Driver,GPT Driver)、看门狗驱动(Watchdog Driver,WDG Driver)、微控制器单元驱动(Microcontroller Unit Driver,MCU Driver)和内核测试(Core Test)四个部分组成。

1.1.1、GPT Driver

在AUTOSAR中有两类定时器,操作系统定时器和硬件定时器。该模块使用通用定时器单元的硬件定时器通道,为操作系统或者其他基础软件模块提供计时功能。一个典型的时间周期是50us-5ms。

GPT驱动的作用是:

1. 启动和停止硬件定时器;

2. 得到定时器数值;

3. 控制时间触发的中断;

4. 控制时间触发的中断唤醒。

1.1.2、WDG Driver

WDG Driver的功能主要是初始化和触发看门狗。WDG Driver有内部WDG Driver和外部WDG Driver。内部WDG Driver控制MCU的内部看门狗定时器,提供触发功能和模式选择服务;外部WDG Driver控制外部硬件看门狗,与内部WDG Driver一样,提供触发功能和模式选择服务。

1.1.3、MCU Driver

MCU Driver位于MCAL层,可以直接访问微控制器硬件,它的主要功能是初始化、休眠、复位微控制器以及提供其他MCAL软件模块所需的与微控制器相关的特殊功能。MCU Driver还能够使能并设置MCU时钟,例如CPU时钟、外围器件时钟、预分频器等参数。

1.1.4、Core Test

Core Test(内核测试)模块包含周期性测试和启动测试。内核测试模块可以对CPU所有寄存器进行测试,提供中断控制和异常检测。该模块还对算术逻辑单元、存储保护单元和缓存控制器等进行检测。

1.2、存储器驱动

存储器驱动由内部EEPROM驱动、内部Flash驱动、RAM测试和Flash测试四部分组成。

1.2.1、内部EEPROM驱动

内部EEPROM驱动提供初始化服务,以及对内部EEPROM的读写、写、擦除等操作。该驱动模块一次只能接受一个任务。

1.2.2、内部Flash驱动

内部Flash驱动提供内部Flash初始化服务,以及对内部Flash的读、写、擦除等操作。该驱动还可以将Flash访问代码下载到RAM中,如果需要的话,也可以执行写、擦除操作。

1.2.3、RAM测试

RAM测试模块通过软件对RAM存储进行测试。该模块包含后台测试和前台测试。其中,后台测试是异步服务,前台测试是同步服务。

1.2.4、Flash测试

flash测试模块提供算法来测试诸如数据/程序闪存、程序SRAM等非易失性存储器,这些存储器可以是集成在微控制器内部的,也可以是外部映射到微控制器的存储器。

1.3、通信驱动

通信驱动由以太网(Ethernet)驱动、FlexRay驱动、CAN驱动、LIN驱动和SPI驱动五部分组成。

1.3.1、Ethernet驱动

Ethernet驱动模块使用以太网驱动层访问某些控制器,对所使用的以太网控制器的硬件特性进行抽象,为上层的以太网使用提供统一的接口。可由由若干个以太网驱动模块复合起来组成。

1.3.2、FlexRay驱动

FlexRay驱动用来抽象不同的FlexRay通信控制器及其硬件相关的特性。通信控制器的FlexRay协议强制特性经过封装后只能通过统一的API进行访问。API提供了映射到基于实际通信控制器的硬件访问序列的抽象功能操作。因此,使用FlexRay驱动可以保证FlexRay接口独立于硬件。

对内部或外部FlexRay通信控制器的驱动来说,需要进行下列处理:

FlexRay控制器的初始化;

1. 配置数据处理单元;

2. 控制指令向通信控制器的传递;

2. 从协议引擎到控制器主接口状态数据的规定;

4. 通信控制器和主处理机之间信息数据的传输。

1.3.3、CAN驱动

CAN驱动针对的是微控制器内部的CAN控制器,它可以实现以下功能:

1. 对CAN控制器进行初始化;

2. 发送和接收报文;

3. 对报文的数据和功能进行通知(对接收报文的指示、对发送报文的确认);

4. 溢出和错误处理;

5. 唤醒检测;

此外,CAN驱动还具有以下特性:

1. 单个或多个CAN通道;

2. CAN驱动的多重实例化;

3. 对接收报文的中断/轮询模式;

CAN驱动是MCAL的一部分,可以执行硬件访问、向上层提供独立于硬件的API,而仅有的能够访问CAN驱动的上层是CAN接口(CAN Interface)。CAN驱动也可以为数据传输的初始化和通知接收事件的回调函数提供服务,该服务也是独立于硬件的。除此之外,CAN驱动也可以控制从属于同一个CAN硬件单元的CAN控制器的行为和状态。

1.3.4、LIN驱动

LIN驱动使用标准的通用异步收发器(Universal Asynchronous Receiver Transmitter,UART)或者串行通信接口(Serial Communication Interface,SCI)进行通信。

该模块可以完成下列任务:

1. LIN硬件的初始化;

2. 调度表的处理;

3. LIN报文的发送(通过标志位和函数接口确认);

4. LIN报文的接收(通过标志位和函数接口指示);

5. 睡眠和唤醒;

6. 协议差错的处理;

7. 报文的超时监测;

LIN驱动也是MCAL的一部分,可以执行硬件访问、向上层提供独立于硬件的API。仅有的能够访问LIN驱动的上层是LIN接口(LIN Interface)。一个LIN驱动可以支持多个通道,但是这些通道要属于同一个LIN硬件单元。

1.3.5 SPI驱动

SPI驱动模块是微控制器内部同步通信串行接口的驱动。SPI驱动为SPI总线上不同的设备(如EEPROM/Watchdog等)提供读写访问服务。一个SPI设备可以被所使用的SPI硬件和相关的片选信号识别。该模块可以在主、从或者主-从模式下运行。

配置 SPI 驱动应遵循以下步骤:

1. 选择SPI驱动的功能级别,配置可选择的功能特性;

2. 根据数据用途来定义SPI通道,它们可以是SPI驱动的内部缓冲器,或者是由用户提供的外部缓冲器;

3. 根据硬件属性来定义SPI任务,它们会包含一系列使用这些属性的通道;

4. 最后定义任务序列,以优先级排序的方式来传递数据

1.4 I/O驱动

I/O驱动由PORT驱动、DIO驱动、ADC驱动、PWM驱动、ICU驱动、OCU驱动六部分组成。

1.4.1 PORT驱动

PORT驱动初始化就是对微控制器的整个PORT模块进行初始化配置。

很多端口和管脚被分配有多种不同的功能,即可以进行引脚功能复用,比如通用I/O、模数转换、脉宽调制等功能。因此,对PORT必须有一个整体的配置和初始化,对各管脚的具体配置和使用取决于微控制器和ECU的引脚功能分配。PORT初始化数据应当尽可能高效地写到每个端口。DIO驱动中所用到的端口的配置和初始化都是在PORT驱动模块中完成的。因此,在使用DIO功能之前,应先进行PORT的初始化。

1.4.2 DIO驱动

DIO驱动对微控制器硬件管脚的访问进行了抽象,除此之外,还可以对管脚进行分组。该模块通过DIO通道、DIO端口以及DIO通道组来读写数据,而且这类操作是同步的。

1.4.3 ADC驱动

ADC驱动对微控制器内部模数转换单元进行初始化和控制。它可以提供启动和停止模数转换的服务,分别用来开启和禁用模数转换的触发源。

1.4.4 PWM驱动

PWM驱动为微控制器PWM模块提供初始化和控制服务,可生成周期和占空比都可变的脉冲。

1.4.5 ICU驱动

ICU驱动控制的是微控制器的输入捕获单元(Input Capture Unit),有两种模式:正常模式和休眠模式。

ICU驱动可以提供以下服务:

1. 信号边沿检测及通知;
2. 中断唤醒;
3. 周期性信号时间的测量;
4. 边沿时间戳捕获;
5. 边沿/脉冲计数;

1.4.6 OCU驱动

OCU驱动的作用是对微控制器内部的输出比较单元(Output Compare Unit)进行初始化和控制。当计数器的值到达某个阈值时,OCU模块会自动开始比较并执行相应的操作。

OCU驱动还可以为下列功能提供服务:

1. 启动或停止输出通道;
2. 设定某个阈值;
3. 启用或禁用某个通道的通知函数;
4. 获取计数器数值;

2、ECU抽象层

ECU抽象层负责提供统一的访问接口,实现对通信、内存或者IO口的访问,从而无须考虑这些资源是由微处理器提供的还是由外部设备提供的。外部设备的驱动就位于这一层。ECU抽象层主要包括板载设备抽象、存储器硬件抽象、通信硬件抽象以及IO硬件抽象4个部分。

....

3.2、存储器服务

Memory Services由一个模块组成,即 NVRAM 管理器。它负责非易失性(non volatile)数据的管理(从不同的内存驱动读/写)。向应用程序提供非易失性数据。提供非易失性数据管理机制,如保存、加载、校验和保护验证、可靠存储等。

3.3、系统服务

System Services 包含一组模块,并且模块的函数可以被所有层的模块使用。例如实时操作系统(包括定时器服务)和错误管理器。
其中一些服务是:µC相关的(如操作系统:OS),并可能支持特殊µC功能(如加密服务管理:Crypto Service Manager),部分ECU硬件和应用程序相关(如ECU状态管理器:ECU State Manager)等。

这些服务为应用程序和基本软件(BSW)模块提供基本服务。

4、复杂驱动层

复杂驱动(CCD)层跨越于微控制器硬件层和RTE之间,其主要任务是整合具有特殊目的且不能用MCAL进行配置的非标准功能模块,将该部分功能嵌入到AUTOSAR基础软件层中,从而实现处理复杂传感器以及执行器的特定功能和时间要求。

复杂驱动程序跟单片机和ECU硬件紧密相关。其上层程序接口是根据AUTOSAR指定并且实施的;其下层程序接口受标准接口程序的限制。

复杂驱动可以使用特定的中断或是复杂的微控制器外设(如PCP/TPU)来直接访问微控制器,从而实现对复杂传感器的评估和执行器的控制,比如喷油控制,电磁阀控制,增量位置检测等。




參考文件:

AutoSAR入门到精通系列讲解

Autosar从入门到精通-实战篇》总目录_培训教程持续更新中...

https://zh.wikipedia.org/wiki/AUTOSAR

https://zh.wikipedia.org/wiki/File:Autosar_architecture.png

原文链接:https://blog.csdn.net/LEON1741/article/details/105847992


https://blog.csdn.net/xyfx_fhw/article/details/94633994?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7EPayColumn-1.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7EPayColumn-1.pc_relevant_aa&utm_relevant_index=1

Car index

車用影片

AUTOSAR 汽車開放系統架構

MISRA C


基本知識

聚焦晶片:GPU,CPU,SOC,DSP,FPGA,MCU,MPU,GPP,ECU等都是什麼?

ADAS

ADAS高階輔助駕駛之:AEB自動緊急剎車

ADAS高階輔助駕駛之:LDW車道偏離預警

ADAS高階輔助駕駛之:LCDA併線輔助

ADAS高階輔助駕駛利器之:TJA交通擁堵輔助

物聯網V2X技術—智慧駕駛的又一次偉大變革

深入研究

無人駕駛感測器技術-GPS

無人駕駛必學|多感測器資訊融合系統功能和結構模型

「無人駕駛」:CH32以A級標準成全球第二個車規認證鐳射雷達

什麼是汽車高精地圖,高精地圖行業“諸侯混戰”現狀(上篇)

相關報導

無人駕駛技術將“送貨上門”變為現實,亞馬遜已申請專利

n8n index

 【n8n免費本地端部署】Windows版|程式安裝x指令大補帖  【一鍵安裝 n8n】圖文教學,獲得無限額度自動化工具&限時免費升級企業版功能