发新帖我要提问
123
返回列表
打印
[应用相关]

【转】我对STM32所用位带操作宏的超详细剖析、优势分析及...

[复制链接]
楼主: networ
手机看帖
扫描二维码
随时随地手机跟帖
41
potenti| | 2020-3-21 20:03 | 只看该作者 回帖奖励 |倒序浏览
用位操作风格改写原子的寄存器版本SYS.C中的进入待机模式函数:
原子代码为:
//进入待机模式         
void Sys_Standby(void)
{
        SCB->SCR|=1<<2;//使能SLEEPDEEP位 (SYS->CTRL)           
  RCC->APB1ENR|=1<<28;     //使能电源时钟            
         PWR->CSR|=1<<8;          //设置WKUP用于唤醒
        PWR->CR|=1<<2;           //清除Wake-up 标志
        PWR->CR|=1<<1;           //PDDS置位                  
        WFI_SET();                                 //执行WFI指令                 
}

可以改写成:
void Sys_Standby(void)
{
        SCB->SCR|=1<<2;//使能SLEEPDEEP位 (SYS->CTRL)           
  bRCC_ENABLE_PWR=1;        //使能电源时钟            
         bPWR_CSR_EWUP=1;          //设置WKUP用于唤醒
        bPWR_CR_CWUF=1;           //清除Wake-up 标志
        bPWR_CR_PDDS=1;           //PDDS置位                  
        WFI_SET();                                 //执行WFI指令                 
}
注意第一句SCB->SCR|=1<<2;没有改写成位操作,因为有几个系统寄存器并不在位绑定区。   

使用特权

评论回复
42
potenti| | 2020-3-21 20:04 | 只看该作者
/******************************************************************************/
/*                                                                            */
/*                    EXIT寄存器--外部中断/事件控制器(n=0-18)                  */
/*                                                                            */
/******************************************************************************/

#define  bEXTI_INT_MASK(n)                           BIT_ADDR(EXTI_BASE, n)    //中断屏蔽:0屏蔽,1不屏蔽
#define  bEXTI_EVT_MASK(n)                          BIT_ADDR(EXTI_BASE+4, n)  //事件屏蔽:0屏蔽,1不屏蔽
#define  bEXTI_TRIG_RISE(n)                          BIT_ADDR(EXTI_BASE+8, n)  //上升沿触发:0禁用,1使能,可与下降沿触发共存
#define  bEXTI_TRIG_FALL(n)                          BIT_ADDR(EXTI_BASE+12, n) //下降沿触发:0禁用,1使能,可与上升沿触发共存
#define  bEXTI_SFT_RQST(n)                           BIT_ADDR(EXTI_BASE+16, n) //软中断请求,写1产生中断挂起,向下面的PR相关位写1则清本位
#define  bEXTI_INT_PENDING(n)                      BIT_ADDR(EXTI_BASE+20, n) //中断挂起,硬件置1,写1清0

使用特权

评论回复
43
potenti| | 2020-3-21 20:05 | 只看该作者
看起来EXIT寄存器是最适合使用位操作的,
因为它的全部6个寄存器都是按位来表达的。
又由于排列工整而有规律,可以用带参数的宏定义来表达。
注意,这里我对命名进行了些许调整,
使得其更便于**和理解。

使用特权

评论回复
44
perature| | 2020-3-21 20:06 | 只看该作者
补记一下,其实stm32f10x.h是一个非常重要的头文件,即使是你使用寄存器来编程序,也是必须依赖它的,
因为它除了上面说的定义了外设的基址外,
它还把各个外设所包含的所有寄存器都封装成了一个个的结构体,
即:每个外设的各种寄存器封装成一个结构体而且这个结构体不是随便封装的,
它是按照寄存器的地址严格排列的,即它暗藏了各个寄存器的地址偏移量。

比如EXTI外部中断寄存器,封装如下:
typedef struct
{
  __IO uint32_t IMR;
  __IO uint32_t EMR;
  __IO uint32_t RTSR;
  __IO uint32_t FTSR;
  __IO uint32_t SWIER;
  __IO uint32_t PR;
} EXTI_TypeDef;
如果把IMR和EMR互换一下位置行不行?

使用特权

评论回复
45
perature| | 2020-3-21 20:06 | 只看该作者
反正都在一个结构体内,按名称访问不就行了吗?
正常的C程序是可以的,因为我都是按成员名称赋值,再按成员名称读取,不会有任何问题。
但这里是和硬件息息相关的,有时候某些成员并不是你去赋值你去读,而是由硬件赋值的。
而硬件是按物理地址赋值的(对于硬件来说,这些所谓的结构体呀、成员什么的都一概不知!!),
互换位置之后,硬件给甲寄存器赋的值被你当成乙寄存器的值来读,
那不是彻底乱套了吗?
所以,这里的顺序是相当重要的。中间如果有保留的地址空间都需要如实地填充,必须确保各个
寄存器在外设基址之上的偏移量是正确的。

使用特权

评论回复
46
perature| | 2020-3-21 20:08 | 只看该作者
看了下面的情况,你就对此更加深理解了,从1380行开始,有类似如下的宏定义:
#define TIM2                ((TIM_TypeDef *) TIM2_BASE)
#define TIM3                ((TIM_TypeDef *) TIM3_BASE)
#define TIM4                ((TIM_TypeDef *) TIM4_BASE)
......
#define EXTI                ((EXTI_TypeDef *) EXTI_BASE)
这个是什么意思?  
前面所定义的外设基址,如TIM2_BASE并不是地址,只是一个具体的32位常数。
这里的定义把这个常数用强制类型转换的方式使其变成了真正的地址---指向结构体变量的指针
这样,外设TIMER2的寄存器族就与具体的物理地址联系起来,
它在结构体中按顺序排列使寄存器与其真正的物理地址完全对应起来了。
这样,EXTI->IMR就能够实打实地访问到真正的中断屏蔽寄存器了。
(甚至我们都不需要申明一个结构体的实体变量,这是因为只要有具体的地址,就相当有了实体
从指定地址按结构体的成员组成及类型去读取就可以,这就是顺序必须严格的原因)
以上这些显然就是寄存器版本编程的基础,所以寄存器版本编程必须#include "stm32f10x.h"

总之,要牢记的是,有了stm32f10x.h这个头文件,我们就可以通过“外设名->寄存器名”的方式对所有寄存器进行访问了。

使用特权

评论回复
47
observa| | 2020-3-21 20:08 | 只看该作者
RCC有关寄存器的位操作:
RCC是复位与时钟控制,是系统比较重要的寄存器,
它基本所有的寄存器都比较适合采用位操作。
这里的宏定义我采取了忽略寄存器名称,而使用归纳用途来定义的方法,
其实各个外设之所以有不同的寄存器名称,一是为了区分不同的寄存器,二是为了编程寻址的方便。
但是有一个缺点就是,如果类似的BIT位比较多,就得分成多个不同的寄存器,
于是我们就见到了CR1、CR2、CR3之类的,这些其实是比较麻烦的,
为了查某个控制位,还得去查这个位在哪个控制器。
还有我们大家容易犯的错误就是:因为STM32对总线按速度进行了分区,有AHB、APBH1、APBH2之类的,
由于SPI1和SPI2在不同的分区,我们经常因为一时忽略了总线区域的变化而配错,
这种错误往往比较隐蔽,以为自己开启了SPI1的时钟而其实没有,造成外设不工作。
采用位操作,我们在定义宏的时候,就可以小封装一下,将这些细节封装成统一的形式,
开启时钟的时候,只须使用bRCC_ENABLE_SPI1或者bRCC_ENABLE_SPI2,而不能去查它们究竟在哪个总线区域,
由哪个寄存器的比特位来设置。
其它类似的按功能区分的有:RESET(复位)、CONFIG(配置)、CLK(系统时钟)、**(标志位)、INT(中断位)等,
具体可看楼下的定义:

使用特权

评论回复
48
observa| | 2020-3-21 20:10 | 只看该作者
/******************************************************************************/
/*                                                                            */
/*                         RCC寄存器--复位和时钟控制                           */
/*                                                                            */
/******************************************************************************/
//typedef struct
//{
//  __IO uint32_t CR;
//  __IO uint32_t CFGR;
//  __IO uint32_t CIR;
//  __IO uint32_t APB2RSTR;
//  __IO uint32_t APB1RSTR;
//  __IO uint32_t AHBENR;
//  __IO uint32_t APB2ENR;
//  __IO uint32_t APB1ENR;
//  __IO uint32_t BDCR;
//  __IO uint32_t CSR;

//#ifdef STM32F10X_CL  
//  __IO uint32_t AHBRSTR;
//  __IO uint32_t CFGR2;
//#endif /* STM32F10X_CL */

//#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)   
//  uint32_t RESERVED0;
//  __IO uint32_t CFGR2;
//#endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */
//} RCC_TypeDef;

//RCC_CR--时钟控制寄存器
#define  bRCC_CLK_HSION        BIT_ADDR(RCC_BASE, 0) //LSI时钟: 0禁用,1开启
#define  bRCC_CLK_HSIRDY        BIT_ADDR(RCC_BASE, 1) //LSI时钟状态由硬件控制(只读):0不可用,1就绪
#define  bRCC_CLK_HSEON        BIT_ADDR(RCC_BASE, 16) //HSE时钟: 0禁用,1开启
#define  bRCC_CLK_HSERDY        BIT_ADDR(RCC_BASE, 17) //HSE时钟状态由硬件控制(只读):0不可用,1就绪
#define  bRCC_CLK_HSEBYP       BIT_ADDR(RCC_BASE, 18) //外部时钟旁路(调试用)-- 0不旁路  1旁路
#define  bRCC_CLK_CSSON     BIT_ADDR(RCC_BASE, 19) //系统时钟安全系统使能  0时钟检测禁用  1外部时钟就绪后启动检测
#define  bRCC_CLK_PLLON       BIT_ADDR(RCC_BASE, 24) //PLL倍频: 0禁用,1开启
#define  bRCC_CLK_PLLRDY      BIT_ADDR(RCC_BASE, 25) //PLL倍频状态由硬件控制(只读):0不可用,1就绪
//本寄存器还有5BIT的HSITRIM  内部高速时钟调整
//           8BIT的HSICAL  内部高速时钟校准  用于补偿因温度等变化对内部RC振荡器时钟频率的影响.

//RCC_CFGR--时钟配置寄存器
#define  bRCC_CONFIG_SW0        BIT_ADDR(RCC_BASE+0x04, 0) //系统时钟选择2BIT-- 00:HSI  01:HSE
#define  bRCC_CONFIG_SW1        BIT_ADDR(RCC_BASE+0x04, 1) //                   10LL  11: 无效
#define  bRCC_CONFIG_SW_PLL           BIT_ADDR(RCC_BASE+0x04, 1)  //SYSCLK时钟选择位1,置位时为选择PLL作为系统时钟源
#define  bRCC_CONFIG_SWS0        BIT_ADDR(RCC_BASE+0x04, 2) //系统时钟源指示,只读: 定义同上
#define  bRCC_CONFIG_SWS1        BIT_ADDR(RCC_BASE+0x04, 3) //
#define  bRCC_CONFIG_SWS_PLL                   BIT_ADDR(RCC_BASE+0x04, 3)  //SYSCLK时钟指示位1,为1时指示PLL已经为系统时钟源
#define  bRCC_CONFIG_PLLSRC       BIT_ADDR(RCC_BASE+0x04, 16) //PLL钟源选择, 0: HSI/2  1:HSE(PREDIV1的输出)
#define  bRCC_CONFIG_PLLXTPRE     BIT_ADDR(RCC_BASE+0x04, 17) //输出至PLL的HSE是否分频 0:不分频  1:二分频
#define  bRCC_CONFIG_USBPRE       BIT_ADDR(RCC_BASE+0x04, 22) //USB预分频控制 0: PLL/1.5   1: PLL
#define  bRCC_CONFIG_ADCPRE0      BIT_ADDR(RCC_BASE+0x04, 14) //ADC(对PLCK2)预分频2BIT控制: 00: 2分频  01: 4分频
#define  bRCC_CONFIG_ADCPRE1      BIT_ADDR(RCC_BASE+0x04, 15) //                           10: 6分频  11: 8分频
//本寄存器还有4BIT的HPRE控制位: 控制系统时钟SYSCLK分频至AHB
//           3BIT的PPRE1控制位: 控制至APB1的预分频
//           3BIT的PPRE2控制位: 控制至APB2的预分频
//           4BIT的PLLMUL:选择PLL的倍频数,2-16
//           3BIT的MCO控制位: 对MCU输出时钟的选择进行控制

//RCC_CIR--时钟中断寄存器
#define  bRCC_INT_LSIRDY**      BIT_ADDR(RCC_BASE+0x08, 0) //LSI稳定且对应IE置位时由硬件置位,只读0无效,1可用
#define  bRCC_INT_LSERDY**      BIT_ADDR(RCC_BASE+0x08, 1) //LSE稳定且对应IE置位时由硬件置位,只读0无效,1可用
#define  bRCC_INT_HSIRDY**      BIT_ADDR(RCC_BASE+0x08, 2) //HSI稳定且对应IE置位时由硬件置位,只读0无效,1可用
#define  bRCC_INT_HSERDY**      BIT_ADDR(RCC_BASE+0x08, 3) //HSE稳定且对应IE置位时由硬件置位,只读0无效,1可用
#define  bRCC_INT_PLLRDY**      BIT_ADDR(RCC_BASE+0x08, 4) //PLL锁定且对应IE置位时由硬件置位,只读0无效,1可用
#define  bRCC_INT_CSSF           BIT_ADDR(RCC_BASE+0x08, 7) //外部振荡器失效时由硬件置位,只读0:无效,1中断可用
#define  bRCC_INT_LSIRDYIE           BIT_ADDR(RCC_BASE+0x08, 8) //LSI可用   中断使能, 0禁用  1使能
#define  bRCC_INT_LSERDYIE           BIT_ADDR(RCC_BASE+0x08, 9) //下同
#define  bRCC_INT_HSIRDYIE           BIT_ADDR(RCC_BASE+0x08, 10)
#define  bRCC_INT_HSERDYIE           BIT_ADDR(RCC_BASE+0x08, 11)
#define  bRCC_INT_PLLRDYIE           BIT_ADDR(RCC_BASE+0x08, 12)
#define  bRCC_INT_LSIRDYCLR      BIT_ADDR(RCC_BASE+0x08, 16) //写1以清零相应的LSIRDYF,下同
#define  bRCC_INT_LSERDYCLR      BIT_ADDR(RCC_BASE+0x08, 17)
#define  bRCC_INT_HSIRDYCLR      BIT_ADDR(RCC_BASE+0x08, 18)
#define  bRCC_INT_HSERDYCLR      BIT_ADDR(RCC_BASE+0x08, 19)
#define  bRCC_INT_PLLRDYCLR      BIT_ADDR(RCC_BASE+0x08, 20)
#define  bRCC_INT_CSSCLR         BIT_ADDR(RCC_BASE+0x08, 23)

//RCC_APB2RSTR寄存器
#define  bRCC_RESET_AFIO                         BIT_ADDR(RCC_BASE+0x0C, 0)   //0无效,1复位,下同
#define  bRCC_RESET_GPIOA                 BIT_ADDR(RCC_BASE+0x0C, 2)
#define  bRCC_RESET_GPIOB                 BIT_ADDR(RCC_BASE+0x0C, 3)
#define  bRCC_RESET_GPIOC                 BIT_ADDR(RCC_BASE+0x0C, 4)
#define  bRCC_RESET_GPIOD                 BIT_ADDR(RCC_BASE+0x0C, 5)
#define  bRCC_RESET_GPIOE                 BIT_ADDR(RCC_BASE+0x0C, 6)
#define  bRCC_RESET_ADC1                         BIT_ADDR(RCC_BASE+0x0C, 9)
#define  bRCC_RESET_ADC2                         BIT_ADDR(RCC_BASE+0x0C, 10)
#define  bRCC_RESET_TIM1                         BIT_ADDR(RCC_BASE+0x0C, 11)
#define  bRCC_RESET_SPI1                         BIT_ADDR(RCC_BASE+0x0C, 12)
#define  bRCC_RESET_USART1                       BIT_ADDR(RCC_BASE+0x0C, 14)
//RCC_APB1RSTR寄存器
#define  bRCC_RESET_TIM2                 BIT_ADDR(RCC_BASE+0x10, 0)   //0无效,1复位,下同
#define  bRCC_RESET_TIM3                 BIT_ADDR(RCC_BASE+0x10, 1)
#define  bRCC_RESET_TIM4                 BIT_ADDR(RCC_BASE+0x10, 2)
#define  bRCC_RESET_WWDG                 BIT_ADDR(RCC_BASE+0x10, 11)
#define  bRCC_RESET_SPI2                 BIT_ADDR(RCC_BASE+0x10, 14)
#define  bRCC_RESET_USART2               BIT_ADDR(RCC_BASE+0x10, 17)
#define  bRCC_RESET_USART3               BIT_ADDR(RCC_BASE+0x10, 18)
#define  bRCC_RESET_I2C1                 BIT_ADDR(RCC_BASE+0x10, 21)
#define  bRCC_RESET_I2C2                 BIT_ADDR(RCC_BASE+0x10, 22)
#define  bRCC_RESET_USB                  BIT_ADDR(RCC_BASE+0x10, 23)
#define  bRCC_RESET_CAN                        BIT_ADDR(RCC_BASE+0x10, 25)
#define  bRCC_RESET_BKP                  BIT_ADDR(RCC_BASE+0x10, 27)
#define  bRCC_RESET_PWR                  BIT_ADDR(RCC_BASE+0x10, 28)
//RCC_AHBEN寄存器
#define  bRCC_ENABLE_DMA                   BIT_ADDR(RCC_BASE+0x14, 0)  //0关闭时钟,1开启时钟,下同
#define  bRCC_ENABLE_SRAM                  BIT_ADDR(RCC_BASE+0x14, 2)
#define  bRCC_ENABLE_FLITF                 BIT_ADDR(RCC_BASE+0x14, 4)
#define  bRCC_ENABLE_CRC                          BIT_ADDR(RCC_BASE+0x14, 6)
//RCC_APB2ENR寄存器
#define  bRCC_ENABLE_AFIO                 BIT_ADDR(RCC_BASE+0x18, 0)   //0关闭时钟,1开启时钟,下同
#define  bRCC_ENABLE_GPIOA                 BIT_ADDR(RCC_BASE+0x18, 2)
#define  bRCC_ENABLE_GPIOB                BIT_ADDR(RCC_BASE+0x18, 3)
#define  bRCC_ENABLE_GPIOC                BIT_ADDR(RCC_BASE+0x18, 4)
#define  bRCC_ENABLE_GPIOD                 BIT_ADDR(RCC_BASE+0x18, 5)
#define  bRCC_ENABLE_GPIOE                 BIT_ADDR(RCC_BASE+0x18, 6)
#define  bRCC_ENABLE_ADC1                 BIT_ADDR(RCC_BASE+0x18, 9)
#define  bRCC_ENABLE_ADC2                 BIT_ADDR(RCC_BASE+0x18, 10)
#define  bRCC_ENABLE_TIM1                 BIT_ADDR(RCC_BASE+0x18, 11)
#define  bRCC_ENABLE_SPI1                 BIT_ADDR(RCC_BASE+0x18, 12)
#define  bRCC_ENABLE_USART1               BIT_ADDR(RCC_BASE+0x18, 14)
//RCC_APB1ENR寄存器
#define  bRCC_ENABLE_TIM2                 BIT_ADDR(RCC_BASE+0x1C, 0)   //0关闭时钟,1开启时钟,下同
#define  bRCC_ENABLE_TIM3                 BIT_ADDR(RCC_BASE+0x1C, 1)
#define  bRCC_ENABLE_TIM4                 BIT_ADDR(RCC_BASE+0x1C, 2)
#define  bRCC_ENABLE_WWDG                 BIT_ADDR(RCC_BASE+0x1C, 11)
#define  bRCC_ENABLE_SPI2                 BIT_ADDR(RCC_BASE+0x1C, 14)
#define  bRCC_ENABLE_USART2               BIT_ADDR(RCC_BASE+0x1C, 17)
#define  bRCC_ENABLE_USART3               BIT_ADDR(RCC_BASE+0x1C, 18)
#define  bRCC_ENABLE_I2C1                 BIT_ADDR(RCC_BASE+0x1C, 21)
#define  bRCC_ENABLE_I2C2                 BIT_ADDR(RCC_BASE+0x1C, 22)
#define  bRCC_ENABLE_USB                  BIT_ADDR(RCC_BASE+0x1C, 23)
#define  bRCC_ENABLE_CAN                         BIT_ADDR(RCC_BASE+0x1C, 25)
#define  bRCC_ENABLE_BKP                  BIT_ADDR(RCC_BASE+0x1C, 27)
#define  bRCC_ENABLE_PWR                  BIT_ADDR(RCC_BASE+0x1C, 28)
//RCC_BDCR寄存器--备份区域控制
#define  bRCC_CLK_LSEON                  BIT_ADDR(RCC_BASE+0x20, 0)  //LSE时钟: 0禁用,1开启
#define  bRCC_CLK_LSERDY                 BIT_ADDR(RCC_BASE+0x20, 1) //LSE时钟状态由硬件控制(只读):0不可用,1就绪
#define  bRCC_CLK_RTCSEL0                BIT_ADDR(RCC_BASE+0x20, 24) //RTC时钟源选择两位共同控制: 00:无时钟 01SE
#define  bRCC_CLK_RTCSEL1                BIT_ADDR(RCC_BASE+0x20, 26) //                      10SI   11: HSE/128
#define  bRCC_ENABLE_RTC                 BIT_ADDR(RCC_BASE+0x20, 27)  //0禁用RTC; 1:使能RTC
#define  bRCC_RESET_BKUPDOMAIN           BIT_ADDR(RCC_BASE+0x20, 28)   //备份区域软复位  写1复位 0复位未被激活

//RCC_CSR寄存器--状态与控制
#define  bRCC_CLK_LSION                   BIT_ADDR(RCC_BASE+0x24, 0)   //LSI时钟: 0禁用,1开启
#define  bRCC_CLK_LSIRDY                  BIT_ADDR(RCC_BASE+0x24, 1)   //LSI时钟状态由硬件控制(只读):0不可用,1就绪
#define  bRCC_**_RMVF                    BIT_ADDR(RCC_BASE+0x24, 24) //清除复位标志 写0复位未激活的复位标志,写1则清复位标志
#define  bRCC_**_PINRSTF                 BIT_ADDR(RCC_BASE+0x24, 26)  //引脚复位标志  硬件置1软件写RMVF位清0
#define  bRCC_**_PORRSTF                 BIT_ADDR(RCC_BASE+0x24, 27)  //端口复位标志  硬件置1软件写RMVF位清0
#define  bRCC_**_SFTRSTF                 BIT_ADDR(RCC_BASE+0x24, 28)   //软件复位标志  硬件置1软件写RMVF位清0
#define  bRCC_**_IWDGRSTF                BIT_ADDR(RCC_BASE+0x24, 29)  //独立看门狗复位标志  硬件置1软件写RMVF位清0
#define  bRCC_**_WWDGRSTF                BIT_ADDR(RCC_BASE+0x24, 30)//窗口看门狗复位标志  硬件置1软件写RMVF位清0
#define  bRCC_**_LPWRRSTF                BIT_ADDR(RCC_BASE+0x24, 31)//低功耗管理复位标志  硬件置1软件写RMVF位清0

使用特权

评论回复
49
renzheshengui| | 2020-4-7 13:35 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
50
wakayi| | 2020-4-7 13:35 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
51
wowu| | 2020-4-7 13:35 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
52
xiaoqizi| | 2020-4-7 13:36 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
53
磨砂| | 2020-4-7 13:36 | 只看该作者
非常感谢楼主分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则