野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 8722|回复: 4

[STM32入门篇]第十四课 对于位带操作的理解 以及7色灯的实现

[复制链接]
发表于 2022-7-28 16:11:40 | 显示全部楼层 |阅读模式
本帖最后由 GCT 于 2022-7-28 16:16 编辑

入门第十四课《位带操作—GPIO输出和输入》 对于“位带操作”的理解 以及七色灯

    入门篇 第十四课题目:

    野火论坛202207281116113343..png


    经过从寄存器操作到库函数开发的一系列转变,可以很清楚的认识到使用库函数变成的方便直观之处。

    在头文件中进行复杂宏定义以后,main函数中的操作可以简化到极致,可读性max

    野火论坛202207281125233109..png

    例如LED_Green(OFF),一个不懂程序的人也能明白这个函数作用是关绿色LED灯。


    stm32中可以使用BSR、BSRR寄存器,使用例如SetBits()、ResetBits()一类的函数,改变IDR、ODR相应的位的值。但是不能进行“位操作”,即单独对一个位读或写。

    据说51单片机中可以实现Px=1、Px=0这样的位操作,但是在stm32里是不合法的,这就需要进行位带操作了。


    对于“位带操作”,如果刚接触去看这么大段概念,是很抽象的。我也懵了好久,什么外设位带区、SRAM位带区,位带别名区,比特位膨胀......%¥*@...还是云里雾里的。

    直到看到某个网页的博主这样的解读以后,我才基本理解。

    一个很形象的比喻:可以把寄存器的每一位看成一户一户人家。“GPIOx”的“x”其实可以看成楼层。比如绿LED的PB0可以看做第B层的0室,按照现实中的门牌号就是2层1室201。

    201住着你的好朋友,你要给ta寄东西。但是在他们的社区规定,不能给具体的哪一家送东西,但是可以寄给居委会。那如果要邮寄,写的地址就不能是xxx的201,这样收不到。

    要新起一个名字,叫居委xxx,这样你的好朋友就能收到东西了。感觉上好像给了更高一级的单位,但是最终的目标还是每一户。

   

    在STM32中不允许对单一端口进行位操作,不允许PB0=1。给它另外起一个别名,把地址扩展成32位的字地址,再去位操作就可以了。这种对于单一端口进行的操作就是位带操作。

   

    位带地址转换别名地址的公式:

    野火论坛202207281222136532..png

   

    根据参考手册可以算出IDR、ODR的起始地址。根据需要,GPIOA、GPIOC用于按键输入;GPIOB用于LED输出,所以定义三个函数PAin、PCin、PBout。

    公式中的 位带地址 替换为 寄存器的起始地址,位带序号 替换为 端口号。

    定义如下:

    野火论坛202207281252199973..png


    在定义完这个三句后,就可以像51里的那样对一个一个位进行操作了。


    题外话:LED灯理论上可以显示255种颜色。如果不用PWM控制,似乎只能通过三原色的合成来实现7种颜色的显示。

    野火论坛202207281443229644..png


    为了方便控制,可以把每一种颜色的情况都定义在宏定义

    方案一:在LED_TOGGLE后面加了个参数,参数是颜色名。

    野火论坛202207281451221423..png

    如果要运用一种颜色的灯,则像这样调用:

    野火论坛202207281452514795..png


    方案二:把每一种颜色都定义为单独的宏定义

    野火论坛202207281457042667..png

    如要运用,可以直接使用宏定义的标识符。

    为方便后续移植,将位带序号也定义为宏。

    野火论坛202207281545275026..png


    将三个LED端口进行宏定义并依次初始化

    野火论坛202207281547133210..png

   

    初始化(输出引脚、推挽输出、速度50MHz)

    野火论坛202207281548224068..png

    初始化以后要将那一位置1,不然灯会先亮。


    键盘的两个端口也同样宏定义并初始化

    野火论坛202207281549331919..png

   

    初始化(输入端口、浮空输入模式)

    野火论坛202207281550081667..png

    按键检测部分可以写到主函数里,在头文件里就不写了。


    在主函数写入一个while循环,让它一直检测按键。

    野火论坛202207281557511639..png

    if的作用是判断按键是否按下,if条件中的while判断按键是否松开,如果松开就跳出循环。

    while下面就可以添加按键的功能了。

    如果想让左按键实现红灯的亮灭、右按键实现蓝灯的亮灭,就使用LED_XXX_TOGGLE。PA条件下写LED_RED_TOGGLE,PC条件下写LED_BLUE_TOGGLE。

    如果想让左按键实现紫灯的点亮、右按键实现青色(靛)灯的亮灭,就使用LED_XXX。PA条件下写LED_PURPLE,PC条件下写LED_CYAN。

    在这里已经可以实现七种颜色灯的点亮了。

    (在使用LED_XXX的情况下是不能关闭灯的,可以进行灯的切换而颜色不会叠加,关闭灯用LED_OFF)


    最终代码:

bsp_key.h

  1. #ifndef __BSP_KEY_H
  2. #define __BSP_KEY_H

  3. #include "stm32f10x.h"

  4. #define KEY_ON  1
  5. #define KEY_OFF 0

  6. #define KEY1_GPIO_PIN     GPIO_Pin_0
  7. #define KEY1_GPIO_PORT    GPIOA
  8. #define KEY1_GPIO_CLK     RCC_APB2Periph_GPIOA

  9. #define KEY2_GPIO_PIN     GPIO_Pin_13
  10. #define KEY2_GPIO_PORT    GPIOC
  11. #define KEY2_GPIO_CLK     RCC_APB2Periph_GPIOC

  12. void KEY_GPIO_Config(void);

  13. #endif /*__BSP_KEY_H*/
复制代码


bsp_key.c
  1. #include "bsp_key.h"

  2. void KEY_GPIO_Config(void)
  3. {
  4.     GPIO_InitTypeDef  GPIO_InitStruct;
  5.    
  6.     RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);
  7.    
  8.     GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN;
  9.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  10.    
  11.     GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStruct);
  12.    
  13.     RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK, ENABLE);
  14.    
  15.     GPIO_InitStruct.GPIO_Pin = KEY2_GPIO_PIN;
  16.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  17.    
  18.     GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStruct);
  19. }
复制代码


bsp_led.h
  1. #ifndef __BSP_LED_H
  2. #define __BSP_LED_H

  3. #include "stm32f10x.h"

  4. #define LED_Red_GPIO_PIN       GPIO_Pin_5
  5. #define LED_Red_GPIO_PORT      GPIOB
  6. #define LED_Red_CLK            RCC_APB2Periph_GPIOB

  7. #define LED_Green_GPIO_PIN     GPIO_Pin_0
  8. #define LED_Green_GPIO_PORT    GPIOB
  9. #define LED_Green_CLK          RCC_APB2Periph_GPIOB

  10. #define LED_Blue_GPIO_PIN      GPIO_Pin_1
  11. #define LED_Blue_GPIO_PORT     GPIOB
  12. #define LED_Blue_CLK           RCC_APB2Periph_GPIOB

  13. #define Red      5
  14. #define Green    0
  15. #define Blue     1

  16. #define GPIOB_ODR_Addr    (GPIOB_BASE+0x0C)
  17. #define PBout(n)          *(unsigned int*)((GPIOB_ODR_Addr & 0xF0000000)\
  18.                             +0x2000000+((GPIOB_ODR_Addr & 0x00FFFFFF)<<5)+(n<<2))

  19. #define GPIOA_IDR_Addr    (GPIOA_BASE+0x08)
  20. #define PAin(n)           *(unsigned int*)((GPIOA_IDR_Addr & 0xF0000000)\
  21.                             +0x2000000+((GPIOA_IDR_Addr & 0x00FFFFFF)<<5)+(n<<2))

  22. #define GPIOC_IDR_Addr    (GPIOC_BASE+0x08)
  23. #define PCin(n)           *(unsigned int*)((GPIOC_IDR_Addr & 0xF0000000)\
  24.                             +0x2000000+((GPIOC_IDR_Addr & 0x00FFFFFF)<<5)+(n<<2))


  25. #define LED_RED_TOGGLE       {LED_Red_GPIO_PORT->ODR ^=LED_Red_GPIO_PIN;}
  26. #define LED_GREEN_TOGGLE     {LED_Green_GPIO_PORT->ODR ^=LED_Green_GPIO_PIN;}
  27. #define LED_BLUE_TOGGLE      {LED_Blue_GPIO_PORT->ODR ^=LED_Blue_GPIO_PIN;}
  28. #define LED_WHITE_TOGGLE     {LED_Red_GPIO_PORT->ODR ^=LED_Red_GPIO_PIN;\
  29.                                                         LED_Green_GPIO_PORT->ODR ^=LED_Green_GPIO_PIN;\
  30.                                                         LED_Blue_GPIO_PORT->ODR ^=LED_Blue_GPIO_PIN;}
  31. #define LED_YELLOW_TOGGLE    {LED_Red_GPIO_PORT->ODR ^=LED_Red_GPIO_PIN;\
  32.                                                         LED_Green_GPIO_PORT->ODR ^=LED_Green_GPIO_PIN;}
  33. #define LED_PURPLE_TOGGLE    {LED_Red_GPIO_PORT->ODR ^=LED_Red_GPIO_PIN;\
  34.                                                             LED_Blue_GPIO_PORT->ODR ^=LED_Blue_GPIO_PIN;}
  35. #define LED_CYAN_TOGGLE      {LED_Green_GPIO_PORT->ODR ^=LED_Green_GPIO_PIN;\
  36.                                                             LED_Blue_GPIO_PORT->ODR ^=LED_Blue_GPIO_PIN;}

  37.                                                             
  38. #define LED_RED       {PBout(Red)=0;PBout(Green)=1;PBout(Blue)=1;}
  39. #define LED_GREEN     {PBout(Red)=1;PBout(Green)=0;PBout(Blue)=1;}
  40. #define LED_BLUE      {PBout(Red)=1;PBout(Green)=1;PBout(Blue)=0;}
  41. #define LED_WHITE     {PBout(Red)=0;PBout(Green)=0;PBout(Blue)=0;}
  42. #define LED_YELLOW    {PBout(Red)=0;PBout(Green)=0;PBout(Blue)=1;}
  43. #define LED_PURPLE    {PBout(Red)=0;PBout(Green)=1;PBout(Blue)=0;}
  44. #define LED_CYAN      {PBout(Red)=1;PBout(Green)=0;PBout(Blue)=0;}
  45. #define LED_OFF       {PBout(Red)=1;PBout(Green)=1;PBout(Blue)=1;}

  46. void LED_GPIO_Config(void);

  47. #endif /*__BSP_LED_H*/
复制代码


bsp_led.c
  1. #include "bsp_led.h"

  2. void LED_GPIO_Config(void)
  3. {
  4.     GPIO_InitTypeDef  GPIO_InitStruct;
  5.    
  6.     RCC_APB2PeriphClockCmd(LED_Red_CLK, ENABLE);
  7.     GPIO_InitStruct.GPIO_Pin = LED_Red_GPIO_PIN;
  8.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  9.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  10.     GPIO_Init(LED_Red_GPIO_PORT, &GPIO_InitStruct);
  11.     GPIO_SetBits(LED_Red_GPIO_PORT, LED_Red_GPIO_PIN);
  12.    
  13.     RCC_APB2PeriphClockCmd(LED_Green_CLK, ENABLE);
  14.     GPIO_InitStruct.GPIO_Pin = LED_Green_GPIO_PIN;
  15.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  16.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  17.     GPIO_Init(LED_Green_GPIO_PORT, &GPIO_InitStruct);
  18.     GPIO_SetBits(LED_Green_GPIO_PORT, LED_Green_GPIO_PIN);
  19.    
  20.     RCC_APB2PeriphClockCmd(LED_Blue_CLK, ENABLE);
  21.     GPIO_InitStruct.GPIO_Pin = LED_Blue_GPIO_PIN;
  22.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  23.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  24.     GPIO_Init(LED_Blue_GPIO_PORT, &GPIO_InitStruct);
  25.     GPIO_SetBits(LED_Blue_GPIO_PORT, LED_Blue_GPIO_PIN);
  26. }
复制代码

main.c
  1. #include "stm32f10x.h"   // 相当于51单片机中的  #include <reg51.h>
  2. #include "bsp_led.h"
  3. #include "bsp_key.h"

  4. int main(void)
  5. {
  6.     // 来到这里的时候,系统的时钟已经被配置成72M。
  7.     LED_GPIO_Config();
  8.     KEY_GPIO_Config();
  9.    
  10.     while(1)
  11.     {
  12.         if(PAin(0) ==KEY_ON)
  13.         {
  14.             while(PAin(0) ==KEY_ON);
  15.             //LED_PURPLE;
  16.             //LED_RED_TOGGLE;
  17.         }
  18.         if(PCin(13) ==KEY_ON)
  19.         {
  20.             while(PCin(13) ==KEY_ON);
  21.             //LED_CYAN;
  22.             //LED_BLUE_TOGGLE;
  23.         }
  24.     }
  25. }
复制代码



回复

使用道具 举报

发表于 2022-7-29 09:15:27 | 显示全部楼层
STM32不像51有真正的位操作。
回复 支持 反对

使用道具 举报

发表于 2022-7-29 09:18:57 | 显示全部楼层
可以,理解透彻
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2022-7-29 14:13:41 | 显示全部楼层
lrz 发表于 2022-7-29 09:18
可以,理解透彻

感谢支持!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-7-29 14:14:03 | 显示全部楼层
killalljp 发表于 2022-7-29 09:15
STM32不像51有真正的位操作。

确实,感觉STM32像是模拟的位操作
回复 支持 1 反对 0

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-11-23 15:54 , Processed in 0.037358 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表