
对于STM32芯片的使用,之前一直用库函数来进行配置,最近发现直接配置寄存器有时候好像也挺容易的,而且可读性也不会很差。下面分享关于寄存器配置的一些笔记: 一、嵌入式中位操作一些常见用法1、一个32bit数据的位、字节读取操作 (1)获取单字节: #define GET_LOW_BYTE0(x) ((x >> 0) & 0x000000ff) /* 获取第0个字节 */#define GET_LOW_BYTE1(x) ((x >> 8) & 0x000000ff) /* 获取第1个字节 */* A" U4 A4 a6 x4 { #define GET_LOW_BYTE2(x) ((x >> 16) & 0x000000ff) /* 获取第2个字节 */* Y0 Z/ K/ ?8 w" M7 j9 K8 h2 _2 E #define GET_LOW_BYTE3(x) ((x >> 24) & 0x000000ff) /* 获取第3个字节 */ 示例: ![]() ![]() (2)获取某一位: #define GET_BIT(x, bit) ((x & (1 << bit)) >> bit) /* 获取第bit位 */示例: ![]() ![]() 2、一个32bit数据的位、字节清零操作 (1)清零某个字节: #define CLEAR_LOW_BYTE0(x) (x &= 0xffffff00) /* 清零第0个字节 */" ~( i0 W# J9 T5 V% l* ^. t, ~#define CLEAR_LOW_BYTE1(x) (x &= 0xffff00ff) /* 清零第1个字节 */ #define CLEAR_LOW_BYTE2(x) (x &= 0xff00ffff) /* 清零第2个字节 */ #define CLEAR_LOW_BYTE3(x) (x &= 0x00ffffff) /* 清零第3个字节 */ 示例: ![]() ![]() (2)清零某一位: #define CLEAR_BIT(x, bit) (x &= ~(1 << bit)) /* 清零第bit位 */示例: =======007 =======008 : f0 t% W' E b% a+ C. m$ @) v3、一个32bit数据的位、字节置1操作 (1)置某个字节为1: #define SET_LOW_BYTE0(x) (x |= 0x000000ff) /* 第0个字节置1 */ #define SET_LOW_BYTE1(x) (x |= 0x0000ff00) /* 第1个字节置1 */ #define SET_LOW_BYTE2(x) (x |= 0x00ff0000) /* 第2个字节置1 */ #define SET_LOW_BYTE3(x) (x |= 0xff000000) /* 第3个字节置1 */示例: ![]() ![]() (2)置位某一位: #define SET_BIT(x, bit) (x |= (1 << bit)) /* 置位第bit位 */![]() ![]() 4、判断某一位或某几位连续位的值 (1)判断某一位的值 举例说明:判断0x68第3位的值。 ![]() ![]() 也就是说,要判断第几位的值,if里就左移几位(当然别过头了)。在嵌入式编程中,可通过这样的方式来判断寄存器的状态位是否被置位。 (2)判断某几位连续位的值 /* 获取第[n:m]位的值 */#define BIT_M_TO_N(x, m, n) ((unsigned int)(x << (31-(n))) >> ((31 - (n)) + (m)))示例: ![]() ![]() 这是一个查询连续状态位的例子,因为有些情况不止有0、1两种状态,可能会有多种状态,这种情况下就可以用这种方法来取出状态位,再去执行相应操作。 以上是对32bit数据的一些操作进行总结,其它位数的数据类似,可根据需要进行修改。 二、STM32寄存器配置 STM32有几套固件库,这些固件库函数以函数的形式进行1层或者多层封装(软件开发中很重要的思想之一:分层思想),但是到了最里面的一层就是对寄存器的配置。我们平时都比较喜欢固件库来开发,大概是因为固件库用起来比较简单,用固件库写出来的代码比较容易阅读。最近一段时间一直在配置寄存器,越发地发现使用寄存器来进行一些外设的配置也是很容易懂的。使用寄存器的方式编程无非就是往寄存器的某些位置1、清零以及对寄存器一些状态位进行判断、读取寄存器的内容等。 这些基本操作在上面的例子中已经有介绍,我们依旧以实例来巩固上面的知识点(以STM32F1xx为例): (1)寄存器配置 看一下GPIO功能的端口输出数据寄存器 (GPIOx_ODR) (x=A..E) : ![]() 假设我们要让PA10引脚输出高、输出低,可以这么做: 方法一: GPIOA->ODR |= 1 << 10; /* PA10输出高(置1操作) */GPIOA->ODR &= ~(1 << 10); /* PA10输出低(清0操作) */也可用我们上面的置位、清零的宏定义: SET_BIT(GPIOA->ODR, 10); /* PA10输出高(置1操作) */CLEAR_BIT(GPIOA->ODR, 10); /* PA10输出低(清0操作) */方法二: GPIOA->ODR |= (uint16_t)0x0400; /* PA10输出高(置1操作) */GPIOA->ODR &= ~(uint16_t)0x0400; /* PA10输出低(清0操作) */貌似第二种方法更麻烦?还得去细心地去构造一个数据。 但是,其实第二种方法其实是ST推荐我们用的方法,为什么这么说呢?因为ST官方已经把这些我们要用到的值给我们配好了,在stm32f10x.h中: ![]() 这个头文件中存放的就是外设寄存器的一些位配置。 所以我们的方法二等价于: GPIOA->ODR |= GPIO_ODR_ODR10; /* PA10输出高(置1操作) */GPIOA->ODR &= ~GPIO_ODR_ODR10; /* PA10输出低(清0操作) */两种方法都是很好的方法,但方法一似乎更好理解。 配置连续几位的方法也是一样的,就不介绍了。简单介绍配置不连续位的方法,以TIM1的CR1寄存器为例: ![]() 设置CEN位为1、设置CMS[1:0]位为01、设置CKD[1:0]位为10: TIM1->CR1 |= (0x1 << 1)| (0x1 << 5) |(0x2 << 8);这是组合的写法。当然,像上面一样拆开来写也是可以的。 (2)判断标志位 以状态寄存器(USART_SR) 为例: ![]() 判断RXNE是否被置位: /* 数据寄存器非空,RXNE标志置位 */if (USART1->SR & (1 << 5)){ /* 其它代码 */ USART1->SR &= ~(1 << 5); /* 清零RXNE标志 */}或者: /* 数据寄存器非空,RXNE标志置位 */if (USART1->SR & USART_SR_RXNE){ /* 其它代码 */ USART1->SR &= ~USART_SR_RXNE; /* 清零RXNE标志 */}END:以上笔记中如有错误,欢迎指出!谢谢 2 _+ S* S, [4 g/ l! S |
对你决定走这条路有好处
多谢!受教了
翻手册是个好习惯呀
哈哈 没那么闲 只不过是渲染一下代码 让代码好看些。在这可以渲染代码:http://carbon.now.sh/?bg=rgba(48%2C12%2C66%2C0.88)&t=lucario&wt=none&l=text%2Fx-csrc&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=56px&ph=56px&ln=false&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false
好吧,确实挺好看的,楼主有心了