C语言编程代码架构搭建——代码分层
2017-10-09 22:52
381 查看
底层驱动
初始化配置表
硬件抽象层
对GPIO抽象化
中间交换层
接收缓存区设置
变量标志位
系统任务调用层
基本检测事件任务
软件协议处理任务
硬件控制处理任务
DEBUG调试模式
像Linux一样打印系统运行时间
编程代码前遵循结构设计,大体分为三部分,底层驱动,硬件抽象层,系统任务调用层,程序设计按照这部分来进行设计。
底层GPIO.c部分代码。
硬件抽象层将获取的资源封装在函数里面供系统任务调用
硬件抽象层System_Hardware.c部分代码
设定一个全局变量
extern uint32 u32System_time;
假设设定定时器中断时间为1ms,在定时器中断服务函数里面添加
u32System_time++;
在相应的头文件里面添加宏定义
在多任务的地方添加如下打印:DEV_DBG(“1000 ms is called once\r\n”);
在多任务里面测试时间戳效果如下
初始化配置表
硬件抽象层
对GPIO抽象化
中间交换层
接收缓存区设置
变量标志位
系统任务调用层
基本检测事件任务
软件协议处理任务
硬件控制处理任务
DEBUG调试模式
像Linux一样打印系统运行时间
编程代码前遵循结构设计,大体分为三部分,底层驱动,硬件抽象层,系统任务调用层,程序设计按照这部分来进行设计。
底层驱动
底层驱动是对应相应的MCU而制定的,与MCU的库函数,开发环境搭建有关,底层驱动是将项目所需要的功能进行一系列的初始化,并将基础的功能封装成一个个函数供顶层任务层调用。以STM32为例,底层驱动设计框架如下图所示。初始化配置表
配置表的设置需要根据项目的需求而添加,例如系统初始化WiFi模块时需要配置IP地址,RTC时间初始化需要给定一个具体时间等,这些可以通过写一个初始化配置表,在初始化驱动的时候将里面的值传进驱动的代码中。例如//网络配置表 struct Network_status Dev_Network_status = { .u8gateway = "192.168.1.1", .u8DevIP_addr = "192.168.1.101", .u8Target_host_addr = "192.168.1.100", .u32port = 9090, .u8net_mask = "255.255.255.0", }; //RTC时间配置表 struct RTC_Timer Dev_RTC_Timer_config = { .u32Year = 2016, .u32Month = 7, .u32Day = 15, .u32Hour = 23, .u32Minute = 45, .u32Second = 30, .u32DayOfWeek = RTC_SATURDAY, .u32TimeScale = RTC_CLOCK_24, };
硬件抽象层
硬件抽象层将任务与底层驱动区分开来,将硬件驱动功能抽象化对GPIO抽象化
在底层,GPIO模块子程序中将GPIO管脚配置的函数赋值给probe结构体,然后调用硬件抽象层获取函数,使得硬件抽象层获得驱动信息。底层GPIO.c部分代码。
static void Dev_GPIO_Set_Value(const uint8_t Lock_number,const uint8_t value); static uint8_t Dev_GPIO_Detect_Value(const uint8_t Lock_number); void Dev_GPIO_probe(void *PGPIO_struct) { ………………………………………………….. PGPIO_info->GPIO_Set_Value = Dev_GPIO_Set_Value; //底层驱动接口 PGPIO_info->GPIO_Detect_Value = Dev_GPIO_Detect_Value; Get_Dev_gpio_info(PGPIO_info); //硬件抽象层获取底层驱动资源配置 ……………………………………………… }
硬件抽象层将获取的资源封装在函数里面供系统任务调用
硬件抽象层System_Hardware.c部分代码
static struct GPIO_data *PDirver_to_GPIO_data = NULL; void Get_Dev_gpio_info(void *PDev_gpio_info) //获取底层驱动资源函数 { PDirver_to_GPIO_data = (struct GPIO_data *)PDev_gpio_info; } void gpio_set_value(const uint8_t gpio,const uint8_t value) //任务调用层调用函数 { PDirver_to_GPIO_data->GPIO_Set_Value(gpio,value); } uint8_t gpio_get_value(const uint8_t gpio) { return PDirver_to_GPIO_data->GPIO_Detect_Value(gpio); }
中间交换层
仿照Linux的模块风格,在硬件抽象层设置变量,通过这些变量进行底层与任务之间的信息交换。 交换层不设置全局变量,对变量的访问只能通过硬件抽象层提供的API进行访问。接收缓存区设置
数据接收在交换层里面设置,处理数据信息只在驱动层处理,由相应的驱动设备处理接收到的信息,并将信息保存进缓存区的接收数据中,等待顶层任务调用来处理信息。uint8_t Receive_Uart[100]; uint8_t Receive_SPI[100];
变量标志位
标志位记录着系统的运行状态,顶层任务都是通过这个标志位来进行判断处理。标志位的基本写法如下struct Basic_info { uint8_t Protocol_status; uint8_t SPI_ready; uint8_t I2C_ready; };
系统任务调用层
顶层的任务调度是系统运行起来的基本功能集合,来实现整个系统的功能,任务基本分为四个部分。顶层的任务调度采用FreeRTOS系统进行轮询的调度,没有涉及到优先级强占的问题。基本检测事件任务
该任务采用轮询的方式进行扫描,查询系统标志位的状态。任务的基本涉及如下static void Basic_Detection_task(void) { portTickType xLastExecutionTime; LastExecutionTime = xTaskGetTickCount(); while(1) { Scanning_System_Status() vTaskDelayUntil(&xLastExecutionTime, (( portTickType ) 1 / portTICK_RATE_MS)); } }
软件协议处理任务
任务在一开始就处于挂起状态,激活该任务由系统基本检测事件任务判断的标志位激活。static void Software_processing_tasks(void *pvParameters) { portTickType xLastExecutionTime; xLastExecutionTime = xTaskGetTickCount(); while(1) { vTaskSuspend( NULL ); Protocol_decoding(&Dev_Config_Message); Keybad_control(&Dev_key_config); vTaskDelayUntil(&xLastExecutionTime, (( portTickType ) 1 / portTICK_RATE_MS)); } }
硬件控制处理任务
该任务有唯一操作硬件的功能,访问和调用底层驱动API来具体实现硬件的功能,所有有关硬件的操作都包含在里面,该任务一开始也是出于挂起状态,由软件协议处理任务来激活调度,通过更新处理结果的标志位,让硬件控制任务来处理相应的命令static void Hardware_control_tasks( void *pvParameters ) { portTickType xLastExecutionTime; xLastExecutionTime = xTaskGetTickCount(); while(1) { vTaskSuspend( NULL ); Hardware_setup_function(); vTaskDelayUntil(&xLastExecutionTime, (( portTickType ) 1 / portTICK_RATE_MS)); } }
DEBUG调试模式
在模块内适当的地方加载打印,但打印需要从新定义printf函数。在调试模式下,如果函数中有很多printf打印,编译出来的镜像会很大,但实际上调试完毕后就不需要输出这些打印。所以重新定义printf关键字以至于可以统一打开和关闭,关闭后会将代码中所有的printf删除,编译出来的镜像会小很多。节省空间重定义printf函数的实例为:#if (DEBUG == 1) #define Dev_DBG(format...) printf(format) #else #define Dev_DBG (format...) do { } while (0) #endif
像Linux一样打印系统运行时间
在嵌入式Linux内核运行起来后,每个打印的前面会有一个时间戳,可以方便地看各个任务以及模块运行的时间间隔,在嵌入式C语言中可以将printf修改,与定时器结合,为调试添加时间戳。设定一个全局变量
extern uint32 u32System_time;
假设设定定时器中断时间为1ms,在定时器中断服务函数里面添加
u32System_time++;
在相应的头文件里面添加宏定义
#define OPEN_DEV_DBG 1 #if (OPEN_DEV_DBG == 1) #define DEV_DBG(format,...) printf("[ %d.%03d ] "format, \ (u32System_time)/1000,(u32System_time)%1000, \ ##__VA_ARGS__) #else #define DEV_DBG(format,...) do { } while (0) #endif
在多任务的地方添加如下打印:DEV_DBG(“1000 ms is called once\r\n”);
在多任务里面测试时间戳效果如下
[ 0.208 ] 200 ms is called once [ 0.408 ] 200 ms is called once [ 0.506 ] 500 ms is called once [ 0.608 ] 200 ms is called once [ 0.703 ] 700 ms is called once [ 0.808 ] 200 ms is called once [ 1.000 ] 1000 ms is called once [ 1.006 ] 500 ms is called once [ 1.009 ] 200 ms is called once [ 1.208 ] 200 ms is called once [ 1.403 ] 700 ms is called once [ 1.408 ] 200 ms is called once [ 1.506 ] 500 ms is called once [ 1.608 ] 200 ms is called once [ 1.808 ] 200 ms is called once [ 2.000 ] 1000 ms is called once [ 2.006 ] 500 ms is called once [ 2.009 ] 200 ms is called once [ 2.103 ] 700 ms is called once [ 2.208 ] 200 ms is called once
相关文章推荐
- App工程结构搭建:几种常见Android代码架构分析
- App工程结构搭建:几种常见Android代码架构分析
- 在c语言编程中怎么用代码解除关机命令
- frame buffer编程--重新架构代码,实现动态效果
- Maven中的archetype快速搭建一个代码开发架构 Spring-MVC
- Spark编程环境搭建-超详细代码(可下载)
- POJ “顶嵌杯”全国嵌入式系统C语言编程大赛初赛 题目代码
- 编程精粹--编写高质量C语言代码(5):为子系统设防(二)
- App工程结构搭建:几种常见Android代码架构分析
- App工程结构搭建:几种常见Android代码架构分析
- Spark编程环境搭建-超详细代码(可下载)
- Linux下C语言高级编程必备!(附多文件编程代码)
- App工程结构搭建:几种常见Android代码架构分析
- App工程结构搭建:几种常见Android代码架构分析
- 编程精粹--编写高质量C语言代码(4):为子系统设防(一)
- NHibernate+Oracle10G搭建项目架构示范代码下载(http://d.download.csdn.net/down/1813293/jiangtongcn)
- 编程精粹--编写高质量C语言代码(1):假想编译程序
- Spark编程环境搭建-超详细代码(可下载)
- (三层架构入门)最基本的三层架构搭建方法(详细过程及所有代码)
- C语言项目开发-项目架构和编程命名规范