嵌入式单片机软件分层

嵌入式软硬分层

声明

在学习本文之前需要去了解一下keil的sct文件以及gcc的ld文件。学习指令__attribute__和 __section__的语法规则,并且编译器需要支持c99。

前言

很多时候我们在开发一款芯片的时候,可能代码会这样子写:

#include "gpio.h"
#include "uart.h"
#include "dma.h"
...//省略
int main()
{
	GPIO_INIT(); //初始化gpio
	UART_INIT();//初始化串口
	DMA_INIT();//初始化dma
	...//省略
	
	while(1)
	{
		//主循环做一些逻辑
		dosomething();
	}
}

每一个要用到外设驱动的c文件都必须把他的.h文件包含 进来,这种做法,假如需要切换另外一颗芯片,又需要把别的硬件层包含进来,无限嵌套,代码耦合度十分低,影响开发效率。为此需要做一些软件分层。

linux底层驱动分离

linux的驱动注册,如下所示,通过##链接符号把函数重新命名,再把函数入口地址放在.initcall6.init这个内存模块这里。(注意:#的意思是把6变成字符串),且c语言支持char *string = {“a”“b”“c”}这种写法。

#define module_init(x)	__initcall(x);
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn)     device_initcall(fn, 6)
#define __define_initcall(fn, id) \
	static initcall_t __initcall_##fn##id __used \
	__attribute__((__section__(".initcall" #id ".init"))) = fn

例如

module_init(gpio_init);

实际等价于:

static initcall_t __initcall_gpio_init6 
__attribute__((__section__(".initcall6.init"))) = gpio_init;

他的实际意义是我把gpio_init这个函数接口放在了自己定义的.initcall6.init这个内存模块里面。.initcall6.init这个模块内存空间可以自己定义。基于这个思路我们自己可以写一个单片机的驱动接口注册,实现软件硬件的完全分离。

正文

1.首先声明一个设备驱动结构体。

typedef void (*module_function)();
typedef struct 
{
    const char *node_name;
    module_function func;
}MODULE_NODE;

2.section关键字的使用,进行重定义

#ifndef SECTION
    #ifdef __CC_ARM     /* ARM C Compiler */
        #define SECTION(x)  __attribute__((used,__section__(x)))
#elif defined (__ICCARM__) || defined(__ICCRX__)      /* for IAR Compiler */
        #define SECTION(x)      @ x
    #elif defined(__GNUC__)
        #define SECTION(x)      __attribute__((section(x)))
    #else
        #define SECTION(x)
    #endif
#endif

3.定义我们的驱动注册模块,模块注册在moduleTab这个内存模块里面

#define module_init(name)                                            \
        const char module##name[] = #name;                           \
        const MODULE_NODE module_##name SECTION("moduleTab") =       \
        {                                                            \
            module##name,                                            \
            (module_function)&name                                   \
        };

4.在函数调用,找到模块的内存地址以及注册的驱动个数。保存在 module_base和 module_cnts 里面。


static MODULE_NODE *module_base;
static unsigned int module_cnts = 0;
void driver_init(void)
{

#ifdef __CC_ARM     /* ARM C Compiler */
    extern const unsigned int moduleTab$$Base;
    extern const unsigned int moduleTab$$Limit;
    
    module_base = (MODULE_NODE *)&moduleTab$$Base;
    module_cnts = ((unsigned int)(&moduleTab$$Limit) - ((unsigned int)(&moduleTab$$Base))) / sizeof(MODULE_NODE);
    
#elif defined (__ICCARM__) || defined(__ICCRX__)      /* for IAR Compiler */
    module_base = (MODULE_NODE *)(__section_begin("moduleTab"));
    module_cnts = ((unsigned int)(__section_end("moduleTab")) - (unsigned int)(__section_begin("moduleTab"))) / sizeof(MODULE_NODE);
    
#elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__)    /* GNU GCC Compiler and TI CCS */
    extern const unsigned int module_start;
    extern const unsigned int module_end;
    
    module_base = (MODULE_NODE *)(&module_start);
    module_cnts = ((unsigned int)(&module_end) - (unsigned int)(&module_start)) / sizeof(MODULE_NODE);
    
    #else
    #error not supported compiler
#endif
}
//可变参数宏定义
#define SEARCH_DEVICE(fmt,...) \
do \
{               \
    static uint8_t init_ = 1; \
    static module_function funtion = 0; \
    if(init_ == 1) \
    { \
        init_ = 0; \
        funtion = searchDrive(fmt); \
    } \
    funtion(__VA_ARGS__); \
}while(0); 

5.查找以及设备的设备,如果设备找不到,就把设备信息打印出来


static char *string = NULL;
static void searchDriveError()
{
    printf(">> Driver: \"%s\" can not be found \n\r",string);   
}

module_function searchDrive(char *name)
{
    int i = 0;
   module_function ret = NULL;
    
    MODULE_NODE *temp = module_base;
    
    if(name == NULL || module_cnts == 0)
    {
        return ret;
    }
    
    for(i = 0;i < module_cnts;i++)
    {
        if(strcmp(temp->node_name,name) == 0)
        {
            ret = temp->func;
            break;
        }
        temp++;
    }
    if(ret == NULL)
    {
        string = name;
        ret = searchDriveError;  
    }
    return ret;
}

我们就把模块注册在了moduleTab模块。这样子我们就完成了软硬的分离。下面是分离的例子。

void GPIO_INIT(void)
{
	...//省略
}
module_init(GPIO_INIT);

void URAT_INIT(void)
{
	...//省略
}
module_init(URAT_INIT);

void DMA_INIT(void)
{
	...//省略
}
module_init(DMA_INIT);

int main()
{
	driver_init();//初始化驱动模块
	SEARCH_DEVICE("GPIO_INIT",LED0); //初始化gpio
	SEARCH_DEVICE("URAT_INIT",115200);//初始化串口
	SEARCH_DEVICE("DMA_INIT");//初始化dma
	...//省略
	
	while(1)
	{
		//主循环做一些逻辑
		dosomething();
	}
}

至此,分析完毕。错漏之处,还望各位读者指出!
有什么疑惑也可以私信本人。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值