出於某些原因,我們可能需要MCU進行速率較高的IIC匯流排通訊,一般stm32的IIC預設傳輸速率是100kpbs,最大為400kpbs。現在大部分專案會使用程式IO模擬的IIC,使用方便,具體網上例子很多。
這裡我需要使用LIS3DH三軸加速度感測器,獲取三個方向的加速度用於碰撞檢測
關於這個感測器的詳細設定,可以參考這篇:
https://blog.csdn.net/zhangfls/article/details/109021073
這個是LIS3DH加速度資料重新整理速率的設定暫存器:
出於某些原因,我需要達到500hz以上的資料重新整理速率,所以ODR要設定成0b1000,三軸晶片輸出1.6khz速率的加速度資訊。因為一條資訊包含x、y、z個位元組的資料,實際每讀取一個方向的暫存器需要總計4次讀寫操作,所以總共至少12位元組的通訊才能完整獲取到1次資料重新整理。則通訊的最小速率為1.6K*12*8=153600bps
開始用IO模擬IIC讀取,發現資料讀不全,總有一部分資料被漏掉。網上查了一下,好像軟體模擬IIC達不到這速率,所以只能考慮一下用stm32的硬體IIC,據說能到400Kbps。
這裡用的是STM32F105RCT6,與LIS3DH三軸加速度感測器進行IIC通訊,獲取三個方向的加速度用於碰撞檢測。
範例:stm32F1的IIC設定都差不多
一、IIC初始化:
void IIC1_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//GPIO設定
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_OD;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
//IIC設定
I2C_InitStruct.I2C_Ack=I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed=400000;
I2C_InitStruct.I2C_DutyCycle=I2C_DutyCycle_2;
I2C_InitStruct.I2C_Mode=I2C_Mode_I2C;
I2C_InitStruct.I2C_OwnAddress1=0x55;
I2C_Init(I2C1,&I2C_InitStruct);
I2C_Cmd(I2C1,ENABLE);
}
二、IIC寫一個位元組暫存器(LIS3DH)
void IIC_WriteByte(u8 addr,u8 data)
{
u16 timeout=1000;
I2C_AcknowledgeConfig(I2C1,ENABLE);
I2C_GenerateSTART(I2C1,ENABLE); //start
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))
{
if((timeout--)==0)
{
RS232_Printf(1,"EV5 Fail");
break;
}
} timeout=1000;
I2C_Send7bitAddress(I2C1,LIS_ADDR|0x00,I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((timeout--)==0)
{
RS232_Printf(1,"EV6 Fail");
break;
}
} timeout=1000;
I2C_SendData(I2C1,addr);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((timeout--)==0)
{
RS232_Printf(1,"EV8 Fail");
break;
}
} timeout=1000;
I2C_SendData(I2C1,data);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
{
if((timeout--)==0)
{
RS232_Printf(1,"EV8 Fail");
break;
}
} timeout=1000;
I2C_GenerateSTOP(I2C1,ENABLE);
}
三、IIC讀一個位元組暫存器(LIS3DH)
u8 IIC_ReadByte(u8 addr)
{
u8 readtemp;
u16 timeout=1000;
I2C_AcknowledgeConfig(I2C1,ENABLE);
I2C_GenerateSTART(I2C1,ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))
{
if((timeout--)==0)
{
RS232_Printf(1,"start Fail");
break;
}
} timeout=1000;
I2C_Send7bitAddress(I2C1,LIS_ADDR|0x00,I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
{
if((timeout--)==0)
{
RS232_Printf(1,"EV5 Fail");
break;
}
} timeout=1000;
I2C_SendData(I2C1,addr);
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
{
if((timeout--)==0)
{
RS232_Printf(1,"EV8 Fail");
break;
}
} timeout=1000;
I2C_GenerateSTART(I2C1,ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))
{
if((timeout--)==0)
{
RS232_Printf(1,"start Fail");
break;
}
} timeout=1000;
I2C_Send7bitAddress(I2C1,LIS_ADDR|0x01,I2C_Direction_Receiver);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
{
if((timeout--)==0)
{
RS232_Printf(1,"EV5 Fail");
break;
}
} timeout=1000;
I2C_AcknowledgeConfig(I2C1,DISABLE);
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS )
{
if((timeout--)==0)
{
RS232_Printf(1,"ack Fail");
break;
}
} timeout=1000;
readtemp = I2C_ReceiveData(I2C1);
I2C_GenerateSTOP(I2C1,ENABLE);
return readtemp;
}
四、LIS3DH初始化與資料讀取
uint8_t LIS3DH_Init(void)
{
u8 res = 0;
IIC1_Init();
res = IIC_ReadByte(WHO_AM_I);
if(res != 0x33)
{
return 0;
}
//測試FIFO
IIC_WriteByte(LIS3DH_CTRL_REG1,0x80|0x0F); //0010 0111 低功耗模式
IIC_WriteByte(LIS3DH_CTRL_REG2,0x00); //高通濾波關閉
IIC_WriteByte(LIS3DH_CTRL_REG3,0x06); //使能FIFO中斷 0000 0110
IIC_WriteByte(LIS3DH_CTRL_REG4,0x00); //解析度+-16g 0011 0000
IIC_WriteByte(LIS3DH_CTRL_REG5,0x48); //FIFO使能 0100 1000
IIC_WriteByte(LIS3DH_FIFO_CTRL,0x80|0x1D); //0100 1111 設定FIFO模式和水印
IIC_ReadByte(LIS3DH_INT1_SRC); //清除中斷位
collect_LIS_Data();
return 1;
}
void collect_LIS_Data(void)
{
uint16_t LIS_temp_data[3] = {0,0,0};
uint8_t data_len,i;
FIFO_data_len = IIC_ReadByte(LIS3DH_FIFO_SRC);
// if(FIFO_data_len<0x40)
// return;
FIFO_data_len &= 0x1F;
for(i=0;i<FIFO_data_len;i++)
{
LIS3DH_ReadData(LIS_temp_data);
}
}
上面的範例,將輸出設定為FIFOstream模式,同時將速率設定成了1.6khz,watermarker設定成了29。
實測在主程式中,每10ms呼叫一次資料採集,每次讀到的確實大約在15-17個之間,watermarker沒有發生溢位。IIC通訊也沒有把MCU的10ms全搶佔完,其他任務執行也是正常的。基本可以確定stm32硬體IIC可以達到這樣的速率。
另外,網上有一些說法,stm32的硬體IIC有BUG,長時間執行會崩潰之類的。我特意將裝置掛了1天,到結束時,資料依舊可以正常接收,mcu也沒有發生復位之類的問題
可能是ST已經修復了這個問題,也可能是我試的不夠多,所以要使用的話最好還是要謹慎一些