一、准备工程文件
硬件I2C初始化和寄存器列表相关信息文件
二、建立完成文件之后,开始编写I2C硬件初始化信息,为了速度,I2C初始化和中断操作均使用寄存器操作方式
[mw_shl_code=applescript,true]/**
*****************************************************************************
*
@name : 硬件IIC初始化
*
* @Brief : none
*
* @Input : I2Cx: IIC组
* SlaveAdd: 作为从设备时识别地址
* F103IIC1_Remap: 针对F103的IIC1是否重映射
* 0: 不重映射
* 1: 重映射。PB.06 -> PB.08;PB.07 -> PB.09
*
* @Output : none
*
*
@return : none
*****************************************************************************
**/
void Hard_I2C_Init(I2C_TypeDef* I2Cx, uint8_t SlaveAdd, uint32_t Freq_SCL, uint8_t F103IIC1_Remap/* 针对F103的IIC1重映射的 */)
{
NVIC_InitTypeDef NVIC_InitStructure;
uint32_t i2c_temp = 0;
//
//根据不同的I2C设备进行初始化
//
if (I2Cx == I2C1)
{
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
//
//管脚复用
//
GPIOB->AFR[0] &= ~(GPIO_AFRL_AFR6 | GPIO_AFRL_AFR7);
GPIOB->AFR[0] |= (0x01<<(4 * 6)) | (0x01<<(4 * 7));
//
//初始化管脚
//
GPIOB->MODER &= ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7);
GPIOB->OTYPER &= ~(GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7);
GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7);
GPIOB->
UPDR &= ~(GPIO_PUPDR_PUPDR6 | GPIO_PUPDR_PUPDR7);
GPIOB->MODER |= (GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1); //复用功能
GPIOB->OTYPER |= (GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7);
GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7);
// GPIOB->
UPDR |= (GPIO_PUPDR_PUPDR6_0 | GPIO_PUPDR_PUPDR7_0);
//
//复位I2C设备
//
RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST;
}
else if (I2Cx == I2C2)
{
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
//
//管脚复用
//
GPIOB->AFR[1] &= ~(GPIO_AFRH_AFR10 | GPIO_AFRH_AFR11);
GPIOB->AFR[1] |= (0x01<<(4 * 2)) | (0x01<<(4 * 3));
//
//初始化管脚
//
GPIOB->MODER &= ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11);
GPIOB->OTYPER &= ~(GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_11);
GPIOB->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11);
GPIOB->
UPDR &= ~(GPIO_PUPDR_PUPDR10 | GPIO_PUPDR_PUPDR11);
GPIOB->MODER |= (GPIO_MODER_MODER10_1 | GPIO_MODER_MODER11_1); //复用功能
GPIOB->OTYPER |= (GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_11);
GPIOB->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11);
// GPIOB->
UPDR |= (GPIO_PUPDR_PUPDR10_0 | GPIO_PUPDR_PUPDR11_0);
//
//复位I2C设备
//
RCC->APB1RSTR |= RCC_APB1RSTR_I2C2RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C2RST;
}
//
//初始化I2C寄存器
//
I2Cx->CR1 &= ~I2C_CR1_PE; /*!< Disable I2C */
I2Cx->CR1 &= 0x00CFE0FF;
/*!< 时钟延展功能 */
#if I2C_NOSTRETCH_EN
I2Cx->CR1 &= ~I2C_CR1_NOSTRETCH; /*!< Enable Nostretch */
#else
I2Cx->CR1 |= I2C_CR1_NOSTRETCH; /*!< Disable Nostretch */
#endif
I2Cx->TIMINGR = 0x50E30000; /*!< Timing register
standard mode(100KHz) 0x50E30000
fast mode(400KHz) 0x00100000 */
I2Cx->CR1 |= I2C_CR1_PE; /*!< Enable I2C */
i2c_temp = 0;
I2Cx->OAR1 = i2c_temp;
i2c_temp = (uint32_t)(SlaveAdd & 0x00FF);
I2Cx->OAR1 |= i2c_temp; /*!< Slave Address */
I2Cx->OAR1 |= I2C_OAR1_OA1EN; /*!< Own Address1 enable */
I2Cx->CR2 &= 0x07FF7FFF;
I2Cx->CR2 &= ~I2C_CR2_NACK; /*!< ACK enable */
//
//事件中断优先级配置
//
if (I2Cx == I2C2)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C2_IRQn; /*!< Event IRQn */
}
else
{
NVIC_InitStructure.NVIC_IRQChannel = I2C1_IRQn; /*!< Event IRQn */
}
NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//
//开启中断
//
i2c_temp = 0;
i2c_temp = (I2C_CR1_ERRIE/*!< Error interrupts enable */ |
I2C_CR1_STOPIE/*!< STOP detection Interrupt enable */ |
I2C_CR1_NACKIE/*!< Not acknowledge received Interrupt enable */ |
I2C_CR1_ADDRIE/*!< Address match Interrupt enable (slave only) */ |
I2C_CR1_RXIE/*!< RX Interrupt enable */ |
I2C_CR1_TXIE/*!< TX Interrupt enable */);
I2Cx->CR1 |= i2c_temp;
}[/mw_shl_code]
简单,对照数据手册寄存器来操作就行了,不难
三、初始化就完事了,就要一个函数,接下来开始I2C寄存器列表了,定义一个数组
3.1. uint8_t I2C_REG_TEST[I2C_REG_TEST_SIZE];为了方便移植,使用了宏定义方式
3.2. 接下来两个函数,是在USMART组件中调用调试的
[mw_shl_code=applescript,true]/**
*****************************************************************************
* @Name : 获取IIC寄存器数值
*
* @Brief : none
*
* @Input : iicreg: 寄存器地址
*
* @Output : none
*
* @Return : 对应寄存器的数值
*****************************************************************************
**/
uint8_t I2C_DeviceGetRegister(uint8_t iicreg)
{
uint8_t reg = 0, off = 0;
reg = iicreg & 0xF0;
off = iicreg & 0x0F;
if (reg == I2C_REG_TEST_ADD)
{
return I2C_REG_TEST[off];
}
return 0x00;
}
/**
*****************************************************************************
* @Name : 设置IIC寄存器数值
*
* @Brief : none
*
* @Input : iicreg: 寄存器地址
*
* @Output : none
*
* @Return : 对应寄存器的数值
*****************************************************************************
**/
void I2C_DeviceSetRegister(uint8_t iicreg, uint8_t Data)
{
uint8_t reg = 0, off = 0;
reg = iicreg & 0xF0;
off = iicreg & 0x0F;
if (reg == I2C_REG_TEST_ADD)
{
I2C_REG_TEST[off] = Data;
}
}[/mw_shl_code] 3.3. 重头戏来了,就是I2C的中断服务函数了,这里的操作流程仿照EEPROM的操作流程实现的,仅供参考
四、以上准备完毕,开始编写main函数内容了,代码不多
五、编译,下载
六、用另外一块板子编写一个I2C主机读写程序,作为通讯调试用,采用IO模拟方式,硬件也行,省略1万字
七、准备就绪,I2C测试主机这边写入一个数据
八、然后从机这边获取对应的寄存器数据
九、附上逻辑分析仪波形
十、汇总
10.1. 刚开始是在STM32F302芯片上面实现的
10.2. ST官方解析在STM32F3上已经解决了STM32F1系列的I2C的bug,从设备在STM32F1系列上也测试过,勉强能用,还是不稳定,STM32F4也试过,和F1一样效果
10.3. 在看STM32F0的数据手册时,发现I2C寄存器和时序图什么的和F3系列的很像,对照过后,发现几乎一模一样,心想,是不是也可以使用呢,就有了这个帖子了
10.4. I2C从设备SCL频率不能过高,经过测试,当频率超过230K的时候,出错率直线上升,甚至100%失败
10.5. I2C作为主机的还没测试,盘友们手上有F0或者F3系列芯片的可以编写例程测试
10.6. 本帖I2C从设备经过多次的读写操作,相对比较稳定,建议打开I2C的时钟延展功能,关闭好像会出bug,在主机读取操作时,第一个字节会丢弃,F0上试过关闭未出现丢数据现象,建议大伙们还是打开时钟延展功能使用
十一、详细件附件
一周热门 更多>