@richey
2020-09-23T03:01:31.000000Z
字数 11123
阅读 989
物联网 讲义 嵌入式软件
考虑这样一个常见的系统:、
经典方法:前后台系统(超级循环)
问题
如何解决以上问题?

参考代码
#include <reg51.h>#include "common.h"#define FOSC 11059200ul#define T0_H (65536-(10*FOSC)/(12*1000))/256#define T0_L (65536-(10*FOSC)/(12*1000))%256uint16_t tick = 0;bit bFlag1 = 0;bit bFlag2 = 0;bit bFlag3 = 0;bit bFlag4 = 0;void initSys();sbit LED0 = P1^0;sbit LED1 = P1^1;sbit LED2 = P1^2;sbit LED3 = P1^3;void main(){initSys();while(TRUE){if(bFlag1){bFlag1 = 0;LED0 = !LED0;}if(bFlag2){bFlag2 = 0;LED1 = !LED1;}if(bFlag3){bFlag3 = 0;LED2 = !LED2;}if(bFlag4){bFlag4 = 0;LED3 = !LED3;}}}void initSys(){TMOD = 0x01;TH0 = T0_H;TL0 = T0_L;EA = 1;ET0 = 1;TR0 = 1;}void timer0() interrupt 1 {TH0 = T0_H;TL0 = T0_L;tick++;if((tick % 100) == 0){bFlag1 = 1;}if((tick % 150) == 3){bFlag2 = 1;}if((tick % 20) == 5){bFlag3 = 1;}if((tick % 300) == 7){bFlag4 = 1;}}
新的问题?
有限状态机的概念:FSM
以下都是状态机程序其实就是状态机
状态机的要素



状态机的实现方法
void Task1Function(void) {If(taskState==STATE1) {执行动作1();taskState=STATE2;return;}If(taskState==STATE2) {执行动作2();taskState=STATE3;return;}If(taskState==STATE3) {执行动作3();taskState=STATE4;return;}If(taskState==STATE4) {执行动作4();taskState=STATE1;return;}}
void Task1Function(void) {switch(taskState) {case STATE1: {执行动作1();taskState=STATE2;break;}case STATE2: {执行动作2();taskState=STATE3;break;}case STATE3: {执行动作3();taskState=STATE4;break;}case STATE4: {执行动作4();taskState=STATE1;break;}default:{执行动作5();taskState=STATE1;}}
void fun1(void) {}
编译器会如何处理?(汇编代码 )
调用该函数的方法:
fun1();void (*pFun1)() = fun1;//调用该函数的方法:(*pFun1)();//或pFun1();
用函数指针代替switch case语 句实现一个计算器
代码片段
double caculate(int oper, doubleop1, double op2)switch( oper) {case ADD:result = add( op1, op2);break;case SUB:result = sub( op1, op2);break;case MUL:result = mul( op1, op2);break;case DIV:result = div( op1, op2);break;}}
对于一个新奇的具有上百个操作符的计算器,这条switch语句将非常长。
编译器的问题:可能会将多个case语句的代码编译为if/else 结构,当然也可能优化为函数指针结构。
//使用函数指针实现:代码片段//函数声明double add (double,double);double sub (double,double);double mul(double,double);double div (double,double);......double (*oper_func[ ] )( double, double)={ add,sub,mul,div,... };//用下面这条语句替换前面整条switch 语句!result = oper_func[oper](op1, op2);//或result = (*oper_func[ oper])(op1, op2);
状态机的函数指针实现:
typedef void(*State)(Fsm *me, unsigned const sig);/*状态定义:函数指针,状态迁移后直接调用对应的处理函数*/struct Fsm{State state_;/* 状态机本身由函数指针实现*/};#define FsmDispatch(me_, sig_) ((*(me_)->state_)((me_), sig))/*状态分发,代替任务函数*/#define TRAN(target_) (((Fsm*) me)->state_ = (State)(target_)) /*状态迁移*/ /*状态函数(子任务)*/void FsmFunction1(Fsm *me, unsigned const sig) {switch (sig) {case SLASH_SIG://do sth here;TRAN(FsmFunction2); /* 迁移到下一个状态*/break;}//...}
嵌入式软件架构设计之 合作式调度器+有限状态机架构的实现方法
main() {A_taskInit();//任务的初始化B_taskInit();...while(1) {A_taskProc();//对于长任务,用状态机切割B_taskProc();}}
关于延时的实现方法,以下为OS支持下任务的写法
void xxxTask(void) {while(1){//waitEvent//do step_1/*操作系统延时,让出CPU*/rt_thread_sleep(rt_tick_t tick);rt_thread_delay(rt_tick_t tick);rt_thread_mdelay(rt_int32_t ms);//do step2}}void xxxTask(void) {static unsigned inttaskStat= STAT_GENERAL;//任务状态变量 static rt_tick_t startTick;static rt_tick_t tick currentTick;if (taskStat == STAT_GENERAL) {//check event//if no eventreturn;//do step_1startTick = rt_tick_get(); //rt_tick_get()就是察看系统时间taskStat = STAT_WAIT;return;}else if (taskStat == STAT_WAIT) {currTick= rt_tick_get();if ((currTick-startTick) >= TIME_OUT_TICK_NUM) {//do step_2taskStat= STAT_GENERAL;return;}else return;}
方法:将8通道采集切割为8个子任务
//不切割任务的方法uint8_t ad_chnnel;for(ad_chnnel = 0; ad_chnnel < 8; ad_chnnel++) {//AD转换,通道号ad_chnnel由for语句自动加1,连续转换8次temperature = read_ad(ad_chnnel);ad_result[ad_chnnel] = temperature;//存入数组}
//切割任务的方法,每次只进行一次转换static uint8_t ad_chnnel = 0; //声明为全局变量if(ad_chnnel < 8) {temperature = read_ad(ad_chnnel);//AD转换,ad_result[ad_chnnel] = temperature;//存入数组ad_chnnel++;//通道号加1}elsead_chnnel = 0;


这种方法的主体思想是:利用一个独立的定时器,在其中断处理程序中根据状态的不同 ,动态的修改TCNTn的值,并执行相应状态的代码
typedef void(*lfsm_func_t)(void);typedef struct {uint8_t delta_t;lfsm_func_t fsm_func;lfsm_t *next_fsm;//一定要形成一个环形链表}lfsm_t;//初始化lfsm fsm_18B20[]= { …如何初始化? }void timer0_ISR(void) {static lfsm_t * current_fsm = fsm_18B20;lfsm_t *temp_fsm = current_fsm;TCNT0 = current_fsm->delta_t;//修改到达下一次中断的时间current_fsm = current_fsm->next_fsm;temp_fsm->fsm_func();//调用状态机}
在RTOS环境下,一般提供抢占式调度。RTOS环境下的任 务,一般处于一个while(1)循环中。
while(1){从消息队列接收消息。如果没有,将阻塞。 处理消息。}
//范例1:void Fun1(int iCh) {intI,j,iTmp;i=0;if(iCh<8) {GetAdValue();}}//范例2:void Fun2(int iCh){switch(iCh){case 0:GetAdValue(iCH);break;case 1: …}}
关于命名的风格,应用比较广的有三种。
那么,该选用哪种命名风格呢?建议是“取百家之长”,混用这几种中能够让名字辨识度最高的那些优点:
#define MAX_PATH_LEN 256 //常量,全大写#define BUAD_RATE 19200int g_sys_flag; // 全局变量,加g_前缀float g_currentValue;//类型定义加_t后缀typedef struct {WSAOVERLAPPED ovlp;ngx_event_t *event;int error;} ngx_event_ovlp_t;//结构体定义加_s后缀struct ngx_chain_s {ngx_buf_t *buf;ngx_chain_t *next;};namespace linux_sys { // 名字空间,全小写void get_rlimit_core(); // 函数,全小写}class FilePath final // 类名,首字母大写{public:void set_path(const string& str); // 函数,全小写private:string m_path; // 成员变量,m_前缀int m_level; // 成员变量,m_前缀};
命名另一个相关的问题是“名字的长度”,一个被普遍认可的原则是:变量 / 函数的名字长度与它的作用域成正比,也就是说,局部变量 / 函数名可以短一点,而全局变量 / 函数名应该长一点。
define 用预处理指令#define声明一个常数
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)ul #define 语法的基本知识(不能以分号结束,括号的使用)l,告诉编译器这个常数是的长整型数。ul(表示无符号长整型),那 么你有了一个好的起点。MIN #define MIN(A,B) ((A)<= (B) ? (A) : (B))if-then-else 更优化的代码。least = MIN(*p++, b); //思考:有什么副作用?static
void fun1(void) {static int counts= 0 ;counts++;}fun1();fun1();fun1();
static int counts2;void fun1(void) {counts2= 0 ;counts2++;}void fun2(void) {counts2++;}fun1();fun2();
static void fun1(void) {counts3= 0 ;counts++;}
- 作用:仅供本模块函数调用
- 以上第2,3种用法: 本地化数据和代码范围的好处和重要性
3. const
const int tmp;int const tmp;const int* ptr1; //问题:pData值是否可变?int* const ptr2;//问题:ptr2值是否可变?int const * ptr3 const;//问题:ptr3值是否可变?
思考:编译器如何处理?地址分配在什么地方?
使用const关键字的好处:
- 给读你代码的人传达非常有用的信息.
- 给优化器一些附加的信息,能产生更紧凑的代码。
- 使编译器很自然地保护那些不希望被改变的参数 ,防止其被无意的代码修改。
4. volatile
#define rNCACHBE0 (*(volatile unsigned *)0x1c00004) #define rNCACHBE1 (*(volatile unsigned *)0x1c00008)5.typedef:类型定义
#define mys_s struct s*typedef struct s* mys_t;mys_s p1,p2;mys_t p3,p4;
以上有什么区别? typedef的作用
6. extern 声明外部变量 跨模块的全局变量
moudule1.cint iState;moudule2.cextern int iState;
变量(函数)声明与定义的区别:
声明:不分配存储空间,定义:分配存储空间 以下那个是申明,那个是定义?
extern int x = 1024;int y;extern void reset (void *p) {}extern const int *pi;void print(const matrix &);
__asm
int result;void Add(long a, long *b) {__asm{MOV AX, aMOV BX, bADD AX, [BX]MOV result, AX}}__asm void wait() {nopnopnopnopnopnopBX lr}
0x67a9内存赋值为0xaa55
int *ptr;ptr = (int *)0x67a9;*ptr = 0xaa55;
一个较晦涩的方法是:
*(int * const)(0x67a9) = 0xaa55;
#define PI (3.1415926)float EXTI0_IRQHandler(float fRadius ){double area = PI * fRadius * fRadius ; //printf("\nArea = %f", area);return area;}
错误4:printf()是不可重入的
知识点:函数的可重入性(reentrant)
总的来说,如果一个函数在重 入条件下使用了未受保护的共
享的资源,那么它是不可重入 的。 示例:
int tmp;void func1(int* x, int* y) {tmp = *x;*x = *y;*y = tmp;}void swap_char2(char *lpcX, char *lpcY) {static char cTemp;cTemp = *lpcX;*lpcX = *lpcY;lpcY = cTemp;}void func2(int* x, int* y) {int tmp;tmp = *x;*x = *y;*y = tmp;}
共享数据的问题(中断的数据保护)
static int temperatures[2];void main(void) {int temp0,temp1;while(TURE){temp0 = temperatures[0];temp1 = temperatures[1];if(iTemp0 != iTemp1){...}}}void interrupt reead_temperatures(void) {temperatures[0] = ??;//从硬件读取温 度值??//temperatures[1] = ??;//从硬件读取温 度值}
思考:如果第1条赋值语句被中断会发生什么?
解决方案:
disable_interrupt;temp0 = temperatures[0];temp1 = temperatures[1];enable_interrupt;
共享数据的问题(中断的数据保护)
以下代码有什么问题?如何修改?
static int iSeconds , iMinutes , iHours;

/*module1.h*/int a = 5; /* 在模块1的.h文件中定义int a *//*module1 .c*/#include "module1.h" /* 在模块1中包含模块1的.h文件 *//*module2 .c*/#include "module1.h" /* 在模块2中包含模块1的.h文件 *//*module3 .c*/#include "module1.h" /* 在模块3中包含模块1的.h文件 */
以上代码有什么问题?
正确的方法
/*module1.h*/extern int a; /* 在模块1的.h文件中声明int a *//*module1.c*/#include "module1.h" /* 在模块1中包含模块1的.h文件 */int a = 5; /* 在模块1的.c文件中定义int a *//*module2 .c*/#include "module1.h" /* 在模块2中包含模块1的.h文件 *//*module3 .c*/#include "module1.h" /* 在模块3中包含模块1的.h文件 */
划分模块的原则:以下是最简单的划分方法 一个嵌入式系统通常包括两类模块:
1. 硬件驱动模块
一种特定硬件对应一个模块;
一个硬件驱动模块通常应包括如下函数:
2.软件功能模块
其模块的划分应满足低耦合、高内聚的要求。
例如:主程序1个模块
如何避免重复引用头文件:
module1.h#ifndef _module1_h#define _module1_h....//头文件具体内容#endif