学习过STM32F1库函数的同学们都知道,STM32模拟IIC在读写数据时要改变I/O方向,正点原子官方例程给出的方法是,操作CRL或CRH寄存器,如下:
//SDA引脚 PB(7) IO方向设置 #define SDA_IN() {GPIOB -> CRL &= 0X0FFFFFFF; GPIOB -> CRL |= (u32)(8 << 28);} #define SDA_OUT() {GPIOB -> CRL &= 0X0FFFFFFF; GPIOB -> CRL |= (u32)(3 << 28);}
查阅STM32的CRL和CRH寄存器定义可知,CRL寄存器控制GPIO低8个引脚,对照定义来理解例程,要设置PB7为输入,则CNF7[1:0] = 0b10,MOD[1:0] = 0b00。因此先将CNF7[1:0]和MOD[1:0]清零,即:GPIOB -> CRL &= 0X0FFFFFFF; 再将1000b写入CRL的CNF7[1:0]和MOD7[1:0],即GPIOB -> CRL |= (u32)(8 << 28)。配置输出模式同理。
回归主题,说说STM32F0 HAL库改变I/O方向的寄存器操作。来看HAL中定义的一个结构体:
typedef struct
{ __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x1A */ __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ __IO uint32_t AFR[2]; /*!< GPIO alternate function low register, Address offset: 0x20-0x24 */ __IO uint32_t BRR; /*!< GPIO bit reset register, Address offset: 0x28 */ } GPIO_TypeDef;
MODER寄存器就是我们需要操作的寄存器。其定义如下:
要配置引脚的模式和写入的数据对应如下:
输入 -> 0b00
输出 -> 0b01
于是SDA引脚(PA3)配置为输入,直接将MODER3[1:0]清零,即GPIO_PORT_IIC -> MODER &= 0xffffff3f; 再配置PA3为输出,先将MODER3[1:0]清零,再将0b01写入MODER3[1:0],即GPIO_PORT_IIC -> MODER |= ((uint32_t)0x01 << 6); 代码如下:
#define SDA_IN() {GPIO_PORT_IIC -> MODER &= 0xffffff3f;} #define SDA_OUT() {GPIO_PORT_IIC -> MODER &= 0xffffff3f; GPIO_PORT_IIC -> MODER |= (uint32_t)(1 << 6);}