原创 STM32 硬件I2C总结

2013-4-26 09:08 2496 15 15 分类: MCU/ 嵌入式
    最近项目需求研究了一下stm32的硬件I2C总线。
    stm32的I2C是基于状态机的处理流程,一共能产生2个中断
    1、事件中断 
    2、错误中断
    每个中断能有相应的事件,I2C的状态机就是以事件触发的(STM32的中断大部分都是如此)
下面给一个例程,这个中断例程比较长,因为内部地址不同(16位和8位)所以做了许多跳转,例程能实现对设备的读写控制,使用时一招按照格式填充好I2C结构体,然后复位各种标志位就可以发送起始信号了。
I2C结构体如下所示。


typedef struct  //I2C中断结构体定义
{
uint8_t direction; //方向  write  or  read
uint8_t device_addr;//设备地址
uint8_t data_num;  //数据个数
uint16_t inter_addr; //内部地址
uint8_t data_buff[I2C_MESSAGE_MAX]; //数据缓冲区
uint8_t rw_control; //I2C读写方向控制
uint8_t opera_over; //操作是否完成
}I2C_IntBuff_t;

I2C的事件处理中断函数

以下是代码片段:

/*I2C Interrupt Function----------------------------------------*/
void I2C2_EV_IRQHandler(void) //I2C2事件中断
{
static uint8_t eeprom_intflag = 0;
switch (I2C_GetLastEvent(I2C2))
{
case I2C_EVENT_MASTER_MODE_SELECT:   //发送设备地址              /* EV5 */

#ifdef I2C_DEBUG
UART_Printf("1");
#endif


if(I2C_intbuff_t.rw_control == Transmitter) //如果是发送模式的话
{
/* Master Transmitter ----------------------------------------------*/
/* Send slave Address for write */
I2C_Send7bitAddress(I2C2, I2C_intbuff_t.device_addr, I2C_Direction_Transmitter);
}
else   //接收模式
{
/* Master Receiver -------------------------------------------------*/
/* Send slave Address for read */
I2C_Send7bitAddress(I2C2, I2C_intbuff_t.device_addr, I2C_Direction_Receiver);
}
I2C_ITConfig(I2C2, I2C_IT_BUF, ENABLE);
break;

/* Master Transmitter --------------------------------------------------*/
/* Test on I2C2 EV6 and first EV8 and clear them */
case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: //发送从机内部地址 
#ifdef I2C_DEBUG
UART_Printf("2");
#endif

if(I2C_intbuff_t.direction == Receiver)//如果是接收模式
{
if(I2C_intbuff_t.device_addr == RTC_ADDR) //如果是pcf8563
{
/* Send the first data */
I2C_SendData(I2C2, I2C_intbuff_t.inter_addr&0xff);//这里发送的是内部地址

I2C_GenerateSTOP(I2C2, ENABLE);   //I2C总线协议上没有这个停止位,但是不加的话会触发两次主发送完成事件
I2C_ITConfig(I2C2, I2C_IT_BUF, DISABLE);//I2C状态机跳转到主机发送完成case 屏蔽掉主发送中事件
}
else //如果是24c256
{
I2C_SendData(I2C2, (I2C_intbuff_t.inter_addr&0xff00)>>8);
}
}
else   //如果是发送模式
{
if(I2C_intbuff_t.device_addr == RTC_ADDR)//如果是pcf8563 
{
I2C_SendData(I2C2, I2C_intbuff_t.inter_addr&0xff);
}
else   //如果是24c256的话
{
I2C_SendData(I2C2, (I2C_intbuff_t.inter_addr&0xff00)>>8);
}
}
break;

/* Test on I2C2 EV8 and clear it */
case I2C_EVENT_MASTER_BYTE_TRANSMITTING:  /* Without BTF, EV8 */
#ifdef I2C_DEBUG
UART_Printf("3");
#endif

if(I2C_intbuff_t.direction == Receiver)//接收数据
{
if(I2C_intbuff_t.device_addr == EEPROM_ADDR)//读pcf8563时直接跳过这个case
{
I2C_SendData(I2C2,I2C_intbuff_t.inter_addr&0x00ff);//这里发送的是内部地址
I2C_ITConfig(I2C2, I2C_IT_BUF, DISABLE);
}
}
else //发送数据
{ /*******************************************************************************************/
if(I2C_intbuff_t.device_addr == EEPROM_ADDR) //24C256写数据处理
{
if(eeprom_intflag == 1)
{
if(I2C_intbuff_t.data_num == 1)//写一个数据
{
I2C_ITConfig(I2C2, I2C_IT_BUF, DISABLE);
I2C_SendData(I2C2, I2C_intbuff_t.data_buff[I2C_Buff_index]);
eeprom_intflag = 0;
}
else  //写多个数据
{
I2C_SendData(I2C2, I2C_intbuff_t.data_buff[I2C_Buff_index]);//内部地址发送完成接下来是写数据
I2C_Buff_index++;

if(I2C_Buff_index == I2C_intbuff_t.data_num)
{
I2C_ITConfig(I2C2, I2C_IT_BUF, DISABLE);
eeprom_intflag = 0;
}
}
}
else//内部低八位地址写入
{
eeprom_intflag = 1;
I2C_SendData(I2C2, I2C_intbuff_t.inter_addr&0x00ff);//内部地址发送完成接下来是写数据
//UART_Printf((char *)&I2C_intbuff_t.data_buff[0]);
}
}
else //pcf8563写数据处理
{
if(I2C_intbuff_t.data_num == 1)//发送一个数据
{
I2C_ITConfig(I2C2, I2C_IT_BUF, DISABLE);
I2C_SendData(I2C2, I2C_intbuff_t.data_buff[I2C_Buff_index]);
}
else //发送多个数据
{
I2C_SendData(I2C2, I2C_intbuff_t.data_buff[I2C_Buff_index]);
I2C_Buff_index++;
if(I2C_Buff_index == I2C_intbuff_t.data_num)
{
I2C_ITConfig(I2C2, I2C_IT_BUF, DISABLE);
}
}
}
}
break;

case I2C_EVENT_MASTER_BYTE_TRANSMITTED: /* With BTF EV8-2 */
#ifdef I2C_DEBUG
UART_Printf("4");
#endif

if(I2C_intbuff_t.direction == Receiver) //如果是接收的话
{
I2C_ITConfig(I2C2, I2C_IT_BUF, ENABLE);
I2C_GenerateSTART(I2C2, ENABLE);
I2C_intbuff_t.rw_control = Receiver;//重新发送开始命令,跳转到EV5事件
}
else //如果是发送的话
{
//I2C_ITConfig(I2C2, I2C_IT_BUF, DISABLE);
I2C_GenerateSTOP(I2C2, ENABLE);
I2C_intbuff_t.opera_over = 1;
}

break;

/* Master Receiver -------------------------------------------------------*/
case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED:
#ifdef I2C_DEBUG
UART_Printf("5");
#endif

if(I2C_intbuff_t.data_num == 1)//只接收一个数据 data_buff[I2C_Buff_index]
{
/* Disable I2C2 acknowledgement */
I2C_AcknowledgeConfig(I2C2, DISABLE);
I2C_GenerateSTOP(I2C2, ENABLE);
}
else  //接收多个数据直接跳到到主接收完成
{

}
break;

/* Test on I2C2 EV7 and clear it */
case I2C_EVENT_MASTER_BYTE_RECEIVED:
#ifdef I2C_DEBUG
UART_Printf("6");
#endif
   
/*****************需要快速响应的代码 不能使用带参数的Printf函数*****************/

I2C_intbuff_t.data_buff[I2C_Buff_index] = I2C_ReceiveData(I2C2);

#ifdef I2C_DEBUG
//UART_Printf("%d",I2C_Buff_index);
//UART_Printf("%d\r\n",I2C_intbuff_t.data_buff[I2C_Buff_index]);
#endif

I2C_Buff_index++;
if(I2C_Buff_index == I2C_intbuff_t.data_num-1)
{
/* Disable I2C2 acknowledgement */
I2C_AcknowledgeConfig(I2C2, DISABLE);
/* Send I2C2 STOP Condition */
I2C_GenerateSTOP(I2C2, ENABLE);
}

if((I2C_intbuff_t.data_num == 1)||(I2C_intbuff_t.data_num == I2C_Buff_index))
{
I2C_intbuff_t.opera_over = 1;
}
break;

default:
#ifdef I2C_DEBUG
UART_Printf("error0\r\n");
#endif
break;
}
}

下面是填充I2C结构体并触发I2C开始事件的函数

/*@
 *@从I2C命令缓冲区中备份一个完整的命令
 *@ 供中断使用。
 *@param :
 *@      dir  direction
 *@      device_add   Device address
 *@      inter_add    内部地址  16位 +++++注意这个参数,pcf8563和24c256不同
 *@      data_num     数据长度
 *@      pdata        数据指针
 *@data :2013/4/19
 *@Author :
 */
以下是代码片段:
void I2C_SendCmd(uint8_t dir,uint8_t device_add,uint16_t inter_add,uint8_t data_num,uint8_t *pdata)
{
uint8_t i;
I2C_intbuff_t.direction = dir;
I2C_intbuff_t.device_addr = device_add;
I2C_intbuff_t.data_num = data_num;
I2C_intbuff_t.inter_addr =  inter_add;
if(dir == Transmitter)
{
for(i = 0;i < data_num; i++)
{
I2C_intbuff_t.data_buff = pdata;
}
}
I2C_intbuff_t.rw_control =  Transmitter;
I2C_intbuff_t.opera_over = 0;
I2C_Buff_index = 0;
I2C_AcknowledgeConfig(I2C2, ENABLE);
/* Send START condition */
I2C_GenerateSTART(I2C2, ENABLE);
}

使用时周期性的检测下面的函数,如果转换完成则处理数据并返回成功TRUE,如果没有转换完成则返回FALSE。

以下是代码片段:
bool pcf8563_Dispose()
{
uint8_t i = 0;

if(I2C_intbuff_t.opera_over)
{
if(!I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY))
{
if(I2C_intbuff_t.device_addr == RTC_ADDR)//PCF8563的处理方式
{
#if I2C_DISDEBUG == 1
for(i = 0;i < 16;i++)
UART_Printf("%d ",I2C_intbuff_t.data_buff); //显示所有的寄存器数据
UART_Printf("\r\n");
#endif

/*Time data dispose for printf------------------------------------------------------*/

time_struct.second = BCD2CONVER(I2C_intbuff_t.data_buff[2],0X70);
time_struct.min = BCD2CONVER(I2C_intbuff_t.data_buff[3],0X70);
time_struct.hour = BCD2CONVER(I2C_intbuff_t.data_buff[4],0X30);

time_struct.day = BCD2CONVER(I2C_intbuff_t.data_buff[5],0X30);
time_struct.month = BCD2CONVER(I2C_intbuff_t.data_buff[7],0X10);
time_struct.year = BCD2CONVER(I2C_intbuff_t.data_buff[8],0XF0);


#if I2C_DISDEBUG == 2
UART_Printf("%d/%d/%d %d:%d:%d\r\n",\
            time_struct.year,time_struct.month,time_struct.day,\
           time_struct.hour,time_struct.min,time_struct.second);
#endif
}
else         //24c256的处理方式
{
#if I2C_DISDEBUG == 2
UART_Printf((char *)&I2C_intbuff_t.data_buff);////////////////////////需要修改的地方/////////////////////
UART_Printf("\r\n");
#endif
}
for(i = 1;i < 64; i++)
{
I2C_intbuff_t.data_buff = 0;
}
return TRUE;
}
}

return FALSE;
}

用下面的函数实现两种I2C外设的互斥访问

以下是代码片段:
/*I2C外设互斥访问 状态机----------------------------------*/
void I2C_UpdataTask(void)
{
static uint8_t re_flag =0 ;
switch(re_flag)
{
case 0:
I2C_SendCmd(Receiver,EEPROM_ADDR,0x0000,10,i2c_buff);
re_flag = 1;
break;
case 1:
if(pcf8563_Dispose())
{
re_flag = 2;
I2C_SendCmd(Receiver,RTC_ADDR,0x0000,16,i2c_buff);
}
break; 
case 2:
if(pcf8563_Dispose())
{
re_flag = 0;
}
break; 
default:
break;
}
}


硬件I2C的BUG目前还没有出现。
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
15
关闭 站长推荐上一条 /3 下一条