之前一期文章里面讲了一个使能GPIO端口对应时钟的小技巧,结合这个技巧,合理地使用宏,可以提高STM32开发中程序的可移植性。
PS.链接:https://segmentfault.com/a/11...
首先我们来进行一个STM32中最简单的操作——“点灯”。
LED是一个外设,连在某个GPIO上,我们可以对它进行一定的操作。为了让思路更清晰,先来做点简单的分析:
第一,我们能够对LED进行哪些操作?
最基础的当然是点亮和关闭,如果和延时等等组合的话还可以实现亮度调节、闪烁等等。
第二,使用之前需要初始化吗?
我们通过GPIO来驱动LED,使用GPIO前要进行时钟使能、引脚配置等初始化操作。
基于这两点,我们可以写出如下的程序来驱动板载的LED:
void Board_LED_Init(void)
{
GPIO_InitTypeDef GPIOInitStructure; //定义一个GPIO初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); //使能GPIOC时钟
GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽模式
GPIOInitStructure.GPIO_Pin = GPIO_Pin_13;
GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIOInitStructure);
}
void Board_LED_ON(void)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,0);
}
void Board_LED_OFF(void)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,1);
}
这样修改之后,代码的可读性也会提升:
/**
* @brief: Initial the pin of led on coreboard
* @example: None
* @param: None
* @return: None
*/
void Board_LED_Init(void)
{
RCC_APB2PeriphClockCmd((uint32_t)(1<<(((uint32_t)BOARD_LED_PORT - APB2PERIPH_BASE)>>10)),ENABLE);
GPIO_InitTypeDef GPIOInitStructure;
GPIOInitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIOInitStructure.GPIO_Pin = BOARD_LED_PIN;
GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_LED_PORT,&GPIOInitStructure);
}
首先,为了方便之后的移植,把时钟使能函数的参数改一下,如下:
RCC_APB2PeriphClockCmd((uint32_t)(1<<(((uint32_t)GPIOC - APB2PERIPH_BASE)>>10)) ,ENABLE);
然后,我们来瞅瞅看,这样写的话,可移植性很差啊,如果我们想在其他开发板上运行程序,而板载LED所连接的引脚又不同的话,所有的GPIOC、GPIO_Pin_13这些参数都需要修改,究其原因,是因为没有采用分层的设计,硬件连接的定义和驱动程序混杂在了一起,导致硬件变化以后需要做的修改较多。
所以,如果能把硬件连接的定义从驱动程序中分离出来,移植程序就会容易许多,如下:
#define BOARD_LED_PORT GPIOC
#define BOARD_LED_PIN GPIO_Pin_13
当然,为了进一步节省时间,可以预先对常见的情形进行一些处理,在这里举一个STMF103C8T6的例子。在某宝上搜C8T6,最常见的两种核心板是叫做“Blue Pill”的蓝色小板和叫做“Black Pill”的黑色小板,两种核心板的板载LED分别连接到PC13和PB12,我们可以定一些宏用于选择不同的情况,如下:
#define BLACK_PILL 0 //黑色34Pin的C8T6核心板
//#define BLUE_PILL 0 //蓝色40Pin的C8T6核心板
#ifdef BLUE_PILL
#define BOARD_LED_PORT GPIOC
#define BOARD_LED_PIN GPIO_Pin_13
#elif defined BLACK_PILL
#define BOARD_LED_PORT GPIOB
#define BOARD_LED_PIN GPIO_Pin_12
#endif
嗯,ifdef ··· #elif ··· #endif
确实是个很好用的套路,不过使用的时候注意把不符合实际情形的宏注释掉,比如,当你使用“Blue Pill”时,BLACK_PILL
应该被注释掉 。
这只是个很简单的例子,但是这种硬件定义与驱动程序相对分离的思路可以在很多地方得到应用。
感谢阅读!
(测试代码基于V3.5版本的ST官方标准库)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。