热度 26
2013-3-19 16:28
1760 次阅读|
9 个评论
做MCU开发已经快将近三年了,多多少少理解了一些东西,我现在来讲讲关于如何封转模块和如何合理的分层。 从学MCU开始,就从论坛上和身边的学长听到了“模块化”一词,这一路走来,总算是摸了一些门道,或许是我实践的不够,由于工作上软硬件都要做,做软件也做的不算太多。从最初封装外设开始,用一个C文件和H文件来集成一个外设的所有功能,到现在的分层包装。其实这些都是软件工程上的东西,只不过对于我们这些学信息工程的人来说没有接触。 当然包装的越好的模块消耗不必要的资源就会越多,对于有限的MCU来说,适当即可。来看一个简单的SPI外设的例子: struct SPI_Driver { //-------controller mode -----// HANDLER01 SPI_Init; HANDLER03 RdBusyFlag; HANDLER03 WrBusyFlag; //----------io mode-----------// HANDLER02 SPI_Clk; HANDLER03 SPI_InputData; HANDLER02 SPI_OutputData; HANDLER02 SPI_Cs; }; //spi max clk fre is 1000000 hz. struct SPI_Device { struct SPI_Driver SpiDriver_mode; struct head_list SpiHead_list; UINT16 SpiNum; UINT16 SpiData; UINT8 SpiCtlMode; //spi 模式. UINT8 SpiTriggerMode; //spi trigger mode .for example up or down. UINT8 SpiSendFSM; //send fsm. UINT8 SpiRECEFSM; //receive fsm. }; //函数声明区 UINT8 ConfigSpiModule(UINT8 num, UINT32 fcy); UINT8 RegisterSpiDeviceDriver(UINT8 device_id, struct SPI_Driver *spi_operation); UINT8 SetSpiDeviceMode(UINT8 device_id, UINT8 ctl_mode, UINT8 trigger_mode); UINT8 SpiDeviceRdData(UINT8 device_id, UINT16 *data, UINT8 data_bit_num); UINT8 SpiDeviceWrData(UINT8 device_id, UINT16 *data, UINT8 data_bit_num); 这是SPI协议层的头文件,我们通过一些变量来描述该协议所具有的属性和所需要的功能,有点类似于类的包装。由于目前多数MCU都是有SPI模块的,为了构成通用,用函数指针来分别表示需要的函数接口,如果是用IO模拟SPI设备,则将用到的IO功能用函数指针来表示。 通过对SPI的理解,我们将模块包装成可以同时管理多个SPI设备,而第对他们有序的操作,由于每种SPI外设可能在读取数据位数和距离命令方面有区别,所以我列出了上述几个函数接口。 下面实现模块注册和配置函数。 UINT8 ConfigSpiModule(UINT8 num, UINT32 fcy) { _spi_num = num; _spi_fcy = fcy; _spi_delay_count = _spi_fcy / SPI_MAX_FCY; _spi_device_info = (struct SPI_Device*)My_malloc(sizeof(struct SPI_Device) * num); if(_spi_device_info == 0) return 1; My_memset((UINT8 *)_spi_device_info, sizeof(struct SPI_Device) * num, 0x00); return 0; //error return. } /* register spi device driver. */ UINT8 RegisterSpiDeviceDriver(UINT8 device_id, struct SPI_Driver *spi_operation) { if(device_id _spi_num) { _spi_device_info .SpiDriver_mode = *spi_operation; } else return 1; return 0; } /* set spi device mode. */ UINT8 SetSpiDeviceMode(UINT8 device_id, UINT8 ctl_mode, UINT8 trigger_mode) { if(device_id _spi_num) { _spi_device_info .SpiCtlMode = ctl_mode; _spi_device_info .SpiTriggerMode = trigger_mode; } else return 1; return 0; } 当使用上述三个函数接口对SPI外设进行注册和配置后,就可以独立操作每一个外设了,下面是一个SPI交换数据的DA例子。 void Reigster_TLV5620_Drv(struct SPI_Driver *TLV 5620_operation, HANDLER02 LdacPort, HANDLER02 LoadPort, UINT8 driver_mode) { _TLV5620_Info.LdacPort = LdacPort; _TLV5620_Info.LoadPort = LoadPort; RegisterSpiDeviceDriver(TLV5620_DEVICE_ID, TLV5620_operation); SetSpiDeviceMode(TLV5620_DEVICE_ID, driver_mode, DOWN_TRIGGER); TLV5620_ref_voltage = REF_VOLTAGE; //defalut value. } 这样就完成了外设DA的配置了,只需要使用这个读数据函数 SpiDeviceWrData(TLV5620_DEVICE_ID, temp, 11); //11为11位数,即通过spi传输数据位数 ,就可以将数据读到temp的变量中了。 这样一来我的这个SPI模块无论在哪里都可以使用了,实现了通用型。 当然讲了那么多的废话,其实包装模块就是抽取共性,当开始编程时,感觉到代码中有重复的东西,你可以不妨试试抽取共性,然后包装成模块,当需要使用底层的IO或者资源时,全部使用函数接口或者函数指针方式代替那部分功能,这样就实现了模块通用。 欢迎大家交流。