STM32F系列ARM Cortex-M3核微控制器基础之系统时钟
问题一:STM32 BIT_BAND 位段位带别名区使用:什么是位段、位带别名区? 它有什么好处?
记得MCS51吗? MCS51就是有位操作,以一位(BIT)为数据对象的操作,MCS51可以简单的将P1口的第2位独立操作: P1.2=0;P1.2=1 ; 就是这样把P1口的第三个脚(BIT2)置0置。而现在STM32的位段、位带别名区就为了实现这样的功能。对象可以是SRAM,I/O外设空间。实现对这些地方的某一位的操作。 它是这样的。在寻址空间(32位地址是 4GB )另一地方,取个别名区空间,从这地址开始处,每一个字(32BIT)就对应SRAM或I/O的一位。 这样呢,1MB SRAM就 可以有32MB的对应别名区空间,就是1位膨胀到32位(1BIT 变为1个字)。我们对这个别名区空间开始的某一字操作,置0或置1,就等于它映射的SRAM或I/O相应的某地址的某一位的操作。 简单来说,可以把代码缩小, 速度更快,效率更高,更安全。一般操作要6条指令,而使用 位带别名区只要4条指令。一般操作是 读-改-写 的方式, 而位带别名区是 写 操作。防止中断对读-改-写 的方式的影响。
CM3支持了位带操作(bit_band),可以使用普通的加载/存储指令来对单一的比特进行读写。CM3中有两个区中实现了位带。其中一个是SRAM 区的最低1MB 范围,第二个则是片内外设区的最低1MB 范围。这两个区中的地址除了可以像普通的RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个32 位的字 每个比特膨胀成一个32 位的字,就是把 1M 扩展为 32M ,于是;RAM地址 0X200000000(一个字节)扩展到8个32 位的字,它们是:0X220000000 ,0X220000004,0X220000008,0X22000000C,0X220000010,0X220000014, 0X220000018,0X22000001C 。支持位带操作的两个内存区的范围是:0x2000_0000‐0x200F_FFFF(SRAM 区中的最低1MB)0x4000_0000‐0x400F_FFFF(片上外设区中的最低1MB)。
对SRAM 位带区的某个比特,记它所在字节地址为A,位序号为n(0<=n<=7),在别名区的地址为:AliasAddr= 0x22000000 +((A‐0x20000000)*8+n)*4 =0x22000000+ (A‐0x20000000)*32 + n*4。对于片上外设位带区的某个比特,记它所在字节的地址为A,位序号为n(0<=n<=7),则该比特在别名区的地址为:AliasAddr= 0x42000000+((A‐0x40000000)*8+n)*4 =0x42000000+ (A‐0x40000000)*32 + n*4。上式中,“*4”表示一个字为4 个字节,“*8”表示一个字节中有8 个比特。
#define APB1PERIPH_BASE PERIPH_BASE //APB1总线基地址((uint32_t)0x4000 0000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) //APB2总线基地址((uint32_t)0x4001 0000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) //AHB 总线基地址((uint32_t)0x4002 0000)
把“位带地址+位序号”转换别名地址宏
#define BITBAND(addr, bitnum) ((addr & 0xF000 0000)+0x200 0000+((addr &0xF FFFF)<<5)+(bitnum<<2)),
注:addr & 0xF000 0000取出0x2000 0000或0x4000 0000高位,addr &0xF FFFF取出bit-band区地址变化的部分
把该地址转换成一个指针,并解索引
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
使用:
MEM_ADDR(BITBAND( (u32)&CRCValue,1)) = 0x1;
对库函数中的该部分地址的组织规则的探讨:
第一次划分:
#define FLASH_BASE ((uint32_t)0x08000000) /*!< FLASH base address in the alias region */
#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */
//别名区SRAM基地址
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
//别名区外设基地址
#define SRAM_BASE ((uint32_t)0x20000000) /*!< SRAM base address in the alias region */
//别名区SRAM基地址
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
//别名区外设基地址
#define SRAM_BB_BASE ((uint32_t)0x22000000) /*!< SRAM base address in the bit-band region */
//位带区SRAM基地址
#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */
//位带区外设基地址
#define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address */
//位带区SRAM基地址
#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */
//位带区外设基地址
#define FSMC_R_BASE ((uint32_t)0xA0000000) /*!< FSMC registers base address */
第二次划分
/*!< Peripheral memory map 外设存储映像*/
#define APB1PERIPH_BASE PERIPH_BASE //APB1总线基地址((uint32_t)0x4000 0000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) //APB2总线基地址((uint32_t)0x4001 0000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) //AHB 总线基地址((uint32_t)0x4002 0000)
第三次划分
#define TIM2_BASE (APB1PERIPH_BASE + 0x0000) //TIM2定时器基地址((uint32_t)0x4000 0000)
#define TIM3_BASE (APB1PERIPH_BASE + 0x0400) //TIM3定时器基地址((uint32_t)0x4000 0400)
#define TIM4_BASE (APB1PERIPH_BASE + 0x0800) //TIM4定时器基地址((uint32_t)0x4000 0800)
#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00) //TIM5定时器基地址((uint32_t)0x4000 0C00)
#define TIM6_BASE (APB1PERIPH_BASE + 0x1000) //TIM6定时器基地址((uint32_t)0x4000 1000)
#define TIM7_BASE (APB1PERIPH_BASE + 0x1400) //TIM7定时器基地址((uint32_t)0x4000 1400)
#define TIM12_BASE (APB1PERIPH_BASE + 0x1800)
#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00)
#define TIM14_BASE (APB1PERIPH_BASE + 0x2000)
#define RTC_BASE (APB1PERIPH_BASE + 0x2800) //RTC基地址 ((uint32_t)0x4000 2800)
#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00) //窗口看门狗基地址((uint32_t)0x4000 2C00)
#define IWDG_BASE (APB1PERIPH_BASE + 0x3000) //独立看门狗基地址((uint32_t)0x4000 3000)
......
#define RCC_BASE (AHBPERIPH_BASE + 0x1000) //复位和时钟控制基地址((uint32_t)0x4002 1000)
下面是各寄存器的地址参考:
我们可以看到固件库中的外设地址宏定义是按照先整体后部分的模式来组织的。
先定义在存储器中外设别名和位带的基地址,然后在外设别名区此基础上分割出AHB,APB1,APB2的基地址。最后对各个总线下挂载的外设寄存器的基地址进行宏定义。
固件V3.5.0中对位带操作的使用:
/* ------------ RCC registers bit address in the alias region ----------- */
//复位和时钟控制寄存器别名区位地址
#define RCC_OFFSET (RCC_BASE - PERIPH_BASE) //RCC寄存器组在外设存储映像中的偏移
//RCC_BASE = (AHBPERIPH_BASE + 0x1000) AHBPERIPH_BASE = (PERIPH_BASE + 0x20000)
//PERIPH_BASE = ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
//RCC_BASE = ((uint32_t)0x40021000) RCC_OFFSET = ((uint32_t)0x0002 1000)
/* --- CR Register ---*/
//时钟控制寄存器
#define CR_OFFSET (RCC_OFFSET + 0x00) //CR寄存器在外设存储映像中的偏移
//CR_OFFSET = (RCC_OFFSET + 0x00) = ((uint32_t)0x0002 1000)
/* Alias word address of HSION bit */ //外部高速时钟使能位的别名
#define HSION_BitNumber 0x00 //HSION在CR寄存器中处于第0位
#define CR_HSION_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BitNumber * 4)) //将位带区的位转换为别名区的字
//PERIPH_BB_BASE = ((uint32_t)0x42000000) /* Peripheral base address in the bit-band region */
//复位和时钟控制寄存器别名区位地址
#define RCC_OFFSET (RCC_BASE - PERIPH_BASE) //RCC寄存器组在外设存储映像中的偏移
//RCC_BASE = (AHBPERIPH_BASE + 0x1000) AHBPERIPH_BASE = (PERIPH_BASE + 0x20000)
//PERIPH_BASE = ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
//RCC_BASE = ((uint32_t)0x40021000) RCC_OFFSET = ((uint32_t)0x0002 1000)
/* --- CR Register ---*/
//时钟控制寄存器
#define CR_OFFSET (RCC_OFFSET + 0x00) //CR寄存器在外设存储映像中的偏移
//CR_OFFSET = (RCC_OFFSET + 0x00) = ((uint32_t)0x0002 1000)
/* Alias word address of HSION bit */ //外部高速时钟使能位的别名
#define HSION_BitNumber 0x00 //HSION在CR寄存器中处于第0位
#define CR_HSION_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BitNumber * 4)) //将位带区的位转换为别名区的字
//PERIPH_BB_BASE = ((uint32_t)0x42000000) /* Peripheral base address in the bit-band region */
操作举例:
/**
* @brief Enables or disables the Internal High Speed oscillator (HSI).
使能或失能内部高速晶振
* @note HSI can not be stopped if it is used directly or through the PLL as system clock.
HSI不能被停止如果HSI直接作为系统时钟或通过PLL间接作为系统时钟
* @param NewState: new state of the HSI. This parameter can be: ENABLE or DISABLE.
* @retval None
*/
void RCC_HSICmd(FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NewState));
//typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
//#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
*(__IO uint32_t *) CR_HSION_BB = (uint32_t)NewState;
/* Alias word address of HSION bit */
//#define PERIPH_BB_BASE ((uint32_t)0x42000000) /*!< Peripheral base address in the bit-band region */
//#define CR_OFFSET (RCC_OFFSET + 0x00) #define HSION_BitNumber 0x00
//#define CR_HSION_BB (PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BitNumber * 4))
}
*(__IO uint32_t *) CR_HSION_BB = (uint32_t)NewState; //这边即是写别名区的字
上面是自己的理解,下面看看Cortex-M3权威指南上关于该部分的内容: