本帖最后由 shiyongzhu 于 2015-8-14 14:55 编辑
ST的HAL库的I2C模块函数库中提供了大量的函数,方便了用户的使用。近期由于需要使用下I2C接口,对HAL库中的函数以及相关的例程进行了一下研究,发现当mcu处于从机的时候,其提供的函数只能满足从机处于接收或者发送状态之一(本人观点,欢迎指正),当主机既需要对从机既需要读又需要写时,特别的不方便。为此本人对HAL库中的I2C进行了一下改造,使其处于从机时收发皆可。 主要的思想是开启地址匹配中断,主机对从机进行读或写时进入该中断后,在中断中读取状态寄存器ISR ,看主机是读还是写,然后分别进入相关的子程序即可。
具体实现以stm32f072为例,初始化,注意开启中断: - void MX_I2C1_Init(void)
- {
- hi2c1.Instance = I2C1;
- // hi2c1.Init.Timing = 0x00700000; //1MHz,100ns,100ns,slave
- hi2c1.Init.Timing = 0x00900000; //400kHz,10ns,10ns,slave
- hi2c1.Init.OwnAddress1 = ADDRESS; //µçµ÷µØÖ·
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // µØÖ·Îª7λ
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
- hi2c1.Init.OwnAddress2 = 0;
- hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
- hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
- HAL_I2C_Init(&hi2c1);
- /**Configure Analogue filter
- */
- HAL_I2CEx_AnalogFilter_Config(&hi2c1, I2C_ANALOGFILTER_ENABLED);
-
- HAL_NVIC_SetPriority(I2C1_IRQn, 0, 0); //i2c1
- HAL_NVIC_EnableIRQ(I2C1_IRQn);
- }
复制代码
然后写I2C中断服务函数void I2C1_IRQHandler(),具体程序如下 - void I2C1_IRQHandler(void)
- {
- if (hi2c1.Instance->ISR & (I2C_FLAG_BERR | I2C_FLAG_ARLO | I2C_FLAG_OVR)) {
- HAL_I2C_ER_IRQHandler(&hi2c1);
- } else {
- HAL_I2C_EV_IRQHandler(&hi2c1);
- }
- }
复制代码
具体干活的程序如下,本处对原有的函数库进行了改造 - void HAL_I2C_EV_IRQHandler(I2C_HandleTypeDef *hi2c)
- {
- if ((__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_ADDR) == SET) && (__HAL_I2C_GET_IT_SOURCE(hi2c, I2C_IT_ADDRI) == SET))
- {
- HAL_I2C_SlaveRxTxCallback(hi2c);
- }
-
- }
复制代码
HAL_I2C_SlaveRxTxCallback(hi2c)为自己写的函数,具体如下 - void HAL_I2C_SlaveRxTxCallback(I2C_HandleTypeDef *hi2c)
- {
- if(hi2c->Instance==I2C1)
- {
- /* Slave mode selected */
- if (hi2c->State == HAL_I2C_STATE_READY)
- {
- __HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR); // ÇåÖжÏ
- if(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_DIR)) // ÊÕ·¢·½Ïò
- {
- HAL_I2C_Slave_Transmit_DMA_NEW(hi2c,(uint8_t*)i2c1_tx_buffer, sizeof(i2c1_tx_buffer));
- }
- else
- {
- HAL_I2C_Slave_Receive_DMA_NEW(hi2c,(uint8_t*)i2c1_rx_buffer, sizeof(i2c1_rx_buffer));
- }
- }
- }
- }
复制代码
上面函数中的函数: HAL_I2C_Slave_Transmit_DMA_NEW(hi2c,(uint8_t*)i2c1_tx_buffer,sizeof(i2c1_tx_buffer)) HAL_I2C_Slave_Receive_DMA_NEW(hi2c,(uint8_t*)i2c1_rx_buffer,sizeof(i2c1_rx_buffer)); 为HAL_I2C_Slave_Transmit_DMA和HAL_I2C_Slave_Receive_DMA_NEW的改造,删除了原有函数中的地址匹配和方向判断的内容。
使用时,只要开启地址匹配中断一切就OK了 - __HAL_I2C_ENABLE_IT(&hi2c1,I2C_IT_ADDRI);
复制代码
|
IIC的结束符属于底层的细节(类似于物理层的协议),HAL库封装了IIC的实现细节,你直接关注上面的应用就行了。
至于收发数量,即使你自己实现物理层也不会知道具体的数量吧?!你只是知道来了数据了,每次读若干个。
如果上位机发过来的数据不定长怎么办? 我也不知道他要发多少字节。那我怎么知道应该接收多少个。
以前其他单片机都是已结束位判断一次接收已经完成?
是不是以结束位判断比较好呢?
IIC 都是以结束位结束,HAL库接收发送都要定长度,这很郁闷。
楼主在编辑帖子的时候,右上角有个添加代码文字按钮,用来放代码特别好用
谢谢沐紫,用了下效果好很多!
,我只是路过打酱油的
在中断回调函数中设置读1个字节,然后根据你的需要判断就可以了。接受完1个字节后根据需要再次打开中断。