在使用使用STM32单片机的时候,喜欢使用库函数,由于stm32的寄存器太多了,如果直接使用寄存器的话,设置起来太麻烦了,而且stm32单片机速度快,容量大,使用寄存器要额提高不了多少效率。
对于STM8单片机来说使用寄存器还是很有必要的,本身stm8单片机的速度相比于stm32就会慢很多,同时芯片容量也比较小,使用库函数的话,比较占用空间,导致系统稍微大一点,芯片容量就不够用,所以在stm8单片机上,使用比较多的就是寄存器,stm8的寄存器也比较少,设置起来也比较简单。
但是好多stm8单片机的例程都是库函数版的,那么如何将库函数版的代码移植为寄存器版的呢?
这里使用LED闪烁的库函数代码来说明
打开一个LED库函数版的工程,首先从main函数开始,第一行代码就是系统初始化。
在BSP_Initializes()函数上单击鼠标右键,选择跳转到函数定义。
这时就会跳转到系统初始化代码处。这个初始化代码里面有两个函数,一个是初始化系统时钟,一个是初始化LED的端口。
用上面的方法,单击鼠标右键,跳转到时钟初始化函数里面。
这里面又调用了一个函数,那么继续使用右键跳转。
这时候就会跳转到系统的库函数里面来了,通过这个函数可以看出,此时操作的是CKL_CKDIVR寄存器。
然后在单片机手册中找到这个寄存器,可以看到这个寄存器是设置时钟分频值的。
说明时钟初始化的过程就是设置时钟分频值,那么这个分频值设置的是多少呢?
在CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); 这一行代码中,函数括号里面传递的参数CLK_PRESCALER_HSIDIV1,然后鼠标右键,选择跳转到定义。这个参数的值,就是设置CKL_CKDIVR寄存器的值。
通过宏定义可以看出,传递的参数值是0,也就是说设置CKL_CKDIVR寄存器的值为0.
通过芯片手册寄存器的介绍中可以看出,值为0,也就是寄存器设置为1分频。
既然知道了,时钟初始化的功能就是将CKL_CKDIVR寄存器的值设置为0,那么就可以系统时钟初始化的代码直接改为寄存器操作。
直接将CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); 这行代码屏蔽掉,然后使用 CLK->CKDIVR = 0x00;代替,下来重新编译下载,看一下LED灯的闪烁是否正常。
重新编译下载,LED灯闪烁正常,说明代码的替换暂时没有问题,接下来继续替换LED初始化部分的代码。
使用右键查找定义,依次向下查找。
最后跳转到了GPIO_Init这个库函数中,这个库函数代码比较长,设置的寄存器也比较多,那么要如何替呢?
这时候需要一个参数,一个参数来替换。
这个GPIO_Init函数有三个参数,分别是:
LED_GPIO_PORT,
(GPIO_Pin_TypeDef)LED_GPIO_PIN,
GPIO_MODE_OUT_PP_LOW_FAST
第一个参数是设置GPIO端口,第二个参数是设置端口引脚,第三个参数是设置端口模式。
首先使用右键查看LED_GPIO_PORT这个参数的具体含义
通过宏定义可以看到,端口是GPIOB,引脚是5脚,
这时候就可以在GPIO_Init函数内部查看GPIOB是写入了那个寄存器。
在代码代码中查看GPIOx可以看到,这个是用来选择设置哪个寄存器的,宏定义中设置的GPIOx为GPIOB,那么GPIO_Init函数中设置的就是GPIOB寄存器。
接下来看GPIO_Pin
GPIO_PIN_5的值是0x20,在GPIO_Init函数中设置了GPIPOB的ODR寄存器和DDR寄存器。
ODR寄存器用于设置端口的输出数据,DDR寄存器用于设置IO口的输入输出模式,这里用于驱动LED,那么IO口肯定要设置为输出模式。
那么这里肯定设置的是DDR5为1,也就是将DDR寄存器的第5位设置为1,将GPIOB口的第5个口设置为输出。
ODR寄存器用于设置输出0还是1,也就是用来控制LED亮灭的。
下面接着看第三个参数GPIO_MODE_OUT_PP_LOW_FAST
这个参数的值为0xE0,带入到GPIO_Init函数查看。
可以看到这个值并没有直接设置给寄存器,而是用来比较判断不同的位,设置不同模式的。
首先将0xE0转换为二进制
可以看到最高的3位数都是1.
然后在代码中分析,在判断语句中哪些代码会执行。
首先GPIOx->CR2 &= (uint8_t)(~(GPIO_Pin));
这行代码会执行,然后判断GPIO_Mode第7位是不是1,由于GPIO_Mode最高的3位都是1,所以条件成立。进入if语句,然后判断GPIO_Mode的第4位是不是1,GPIO_Mode的第4位是0,所以第二个if条件不成立。执行
GPIOx->ODR &= (uint8_t)(~(GPIO_Pin));这行代码。接下来执行 GPIOx->DDR |= (uint8_t)GPIO_Pin;
然后继续判断GPIO_Mode的第6位和第5位是不是1,GPIO_Mode的第5位和第6位都是1,所以执行
GPIOx->CR1 |= (uint8_t)GPIO_Pin; GPIOx->CR2 |= (uint8_t)GPIO_Pin;
这两行代码
将上面执行的代码整理后如下所示
GPIOx->CR2 &= (uint8_t)(~(GPIO_Pin));
GPIOx->ODR &= (uint8_t)(~(GPIO_Pin));
GPIOx->DDR |= (uint8_t)GPIO_Pin;
GPIOx->CR1 |= (uint8_t)GPIO_Pin;
GPIOx->CR2 |= (uint8_t)GPIO_Pin;`
分析后发现这三行代码会执行
然后将参数中的值替换为宏定义中的值
GPIOB->CR2 &= (uint8_t)(~(0x20));
GPIOB->ODR &= (uint8_t)(~(0x20));
GPIOB->DDR |= (uint8_t)0x20;
GPIOB->CR1 |= (uint8_t)0x20;
GPIOB->CR2 |= (uint8_t)0x20;
通过代码和寄存器可以分析出,
第一行将CR2寄存器的第五位清零,也就是设置最大输出速度为2MHz。
第二行将ODR寄存器第五位清0,输出为0。
第三行将DDR寄存器第五位设置为1,也就是设置为输出模式。
第四行将CR1寄存器第五位设置为1,设置IO口为推挽输出模式。
最后一行将CR2寄存器设置为1,设置IO口的最大输出为10Mhz
由于寄存器复位后的默认值都为0,所以前两行的设置可以不要,只需要后面三行的设置代码就行,那么GPIO_Init()函数最终就可以简化为下面三行代码:
GPIOB->DDR |= (uint8_t)0x20;
GPIOB->CR1 |= (uint8_t)0x20;
GPIOB->CR2 |= (uint8_t)0x20;
然后用这三行代码将GPIO_Init()函数替换
编译下载程序到单片机中,观察LED闪烁情况,下载完成后LED闪烁正常,说明代码设置也正常。
这样就将CLK_Configuration()函数和GPIO_Configuration()函数都替换为了寄存器版,为了方便观看,将寄存器的相关初始化都放在main函数中。
这样将所有的初始化代码就放在main函数里面,看起来就更方便了,通过寄存器将刚才一大堆系统初始化工作简化为4行寄存器设置的代码。这样程序执行起来的效率就更高效了。
同样可以将while语句里面的LED控制,也改为寄存器操作。
LED_ON 可以替换为 GPIOB->ODR |= (uint8_t)0x20;
LED_OFF 可以替换为 GPIOB->ODR &= (uint8_t)(~0x20);
将主函数中的代码替换
重新编译下载,LED灯正常闪烁,说明代码替换是成功的。
这样通过一步步进入库函数,然后使用寄存器一行一行替换库函数,每替换一个就重新编译下载,观察测试结果,直到所有的代码都替换完。
STM8每个功能的寄存器都比较少,替换起来的时候还是比较快的,如果熟悉的话,一个库函数代码,几分钟就可以全部替换为寄存器代码了。每个功能设置的步骤基本都是一样的,一旦一个功能替换完成,以后遇见类似的库函数代码,直接就可以用以前替换好的寄存器代码去替代。比如初始化IO口,一般都是设置DDR、CR1、CR2这三个寄存器,所以如果遇到了IO初始化的库函数代码,直接用这个三行代码去替换就行,就不用进入到库函数中一行一行代码去分析了。
上一篇:STM8单片机 ADC模拟看门狗中文资料错误
下一篇:STM8单片机 PWM无波形输出解决方法
推荐阅读最新更新时间:2024-11-01 20:25
设计资源 培训 开发板 精华推荐
- 外围扩展
- TP4057-单节锂电池充电验证板
- AL3022EV1,基于用于低成本 LED 背光源的 AL3022 升压控制器的评估板
- NCP300LSN33T1 3.3V LED条形图电压监视器的典型应用
- 使用 LT6205、低电源电压放大器的视频信号分配
- LTC4162EUFD-SAD 32V 至 24V、3.2A 充电器的典型应用,具有 PowerPath 和 2A 输入限制
- 【训练营】一条狗腿子
- 使用 ROHM Semiconductor 的 BU4915 的参考设计
- 使用 ROHM Semiconductor 的 BAJ0BC0WT 的参考设计
- 使用 Sanken Electric Co., Ltd 的 STR2W152D 的参考设计