用极海MCU ARM M0的APM32F03x实现I2C的主从模式的SMBUS通信
1.SMBUS通信协议
1.1 符号含义
S (1 bit) : 起始位
Sr (1 bit) : 重复的起始位
P (1 bit) : 停止位
R/W# (1 bit) : Read/Write bit,读写位
A, N (1 bit) : 应答位
Address(7 bits): 地址位,7 位地址
Command Code (8 bits): 命令字节,一般用来选择芯片内部的寄存器
Data Byte (8 bits): 数据字节,8 位;如果是 16 位数据的话,用 2 个字节来表示。
Count (8 bits): 在 block 操作总,表示数据长度
[…]: 中括号表示 I2C 设备发送的数据,没有中括号表示 主机 发送的数据
#define SLAVEADDR0 0xB0 从机I2C地址
1.2 SMBus Read Byte
先发从机地址,发出芯片内部的寄存器地址,再读数据,读取设备寄存器地址储存的1个字节的数据。
1.3 SMBus Write Byte
先发从机地址,发出芯片内部的寄存器地址,再写数据,向设备寄存器地址写1个字节的数据。
2.主从模式SMBUS通信软件实现
2.1 实现架构
2.2 主I2C2初始化
/*!
* @brief I2C Init
*
* @param None
*
* @retval None
*
* @note
*/
void I2C2Init(void)
{
GPIO_Config_T gpioConfigStruct;
I2C_Config_T i2cConfigStruct;
/** Enable I2C related Clock */
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOB);
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_I2C2);
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
/** Free I2C_SCL and I2C_SDA */
gpioConfigStruct.mode = GPIO_MODE_OUT;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.outtype = GPIO_OUT_TYPE_PP;
gpioConfigStruct.pupd = GPIO_PUPD_NO;
gpioConfigStruct.pin = GPIO_PIN_10;
GPIO_Config(GPIOB, &gpioConfigStruct);
gpioConfigStruct.pin = GPIO_PIN_11;
GPIO_Config(GPIOB, &gpioConfigStruct);
GPIO_SetBit(GPIOB,GPIO_PIN_10);
GPIO_SetBit(GPIOB,GPIO_PIN_11);
/** Connect I2C to SCL */
GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_10, GPIO_AF_PIN1);
/** Connect I2C to SDA */
GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_11, GPIO_AF_PIN1);
/** Config I2C2 GPIO */
gpioConfigStruct.mode = GPIO_MODE_AF;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.outtype = GPIO_OUT_TYPE_OD;
gpioConfigStruct.pupd = GPIO_PUPD_NO;
gpioConfigStruct.pin = GPIO_PIN_10;
GPIO_Config(GPIOB, &gpioConfigStruct);
gpioConfigStruct.pin = GPIO_PIN_11;
GPIO_Config(GPIOB, &gpioConfigStruct);
/** Config I2C1 */
I2C_Reset(I2C2);
RCM_ConfigI2CCLK(RCM_I2C1CLK_SYSCLK);
i2cConfigStruct.ack = I2C_ACK_ENABLE;
i2cConfigStruct.ackaddress = I2C_ACK_ADDRESS_7BIT;
i2cConfigStruct.address1 = 0xA0;
i2cConfigStruct.analogfilter = I2C_ANALOG_FILTER_ENABLE;
i2cConfigStruct.digitalfilter = I2C_DIGITAL_FILTER_0;
i2cConfigStruct.mode = I2C_MODE_SMBUSHOST;
i2cConfigStruct.timing = 0xB0420F13; //100K 48M //0x1042F013;
I2C_Config(I2C2,&i2cConfigStruct);
/** Enable the I2C2 Interrupt */
I2C_EnableInterrupt(I2C2,I2C_INT_RXIE|I2C_INT_ADDRIE|I2C_INT_STOPIE);
/** NVIC configuration */
NVIC_EnableIRQRequest(I2C2_IRQn,2);
/** slave configuration*/
//I2C_EnableSlaveByteControl(I2C2);
//I2C_EnableReload(I2C2);
//I2C_EnableStretchClock(I2C2);
/** Enable I2Cx */
I2C_Enable(I2C2);
}
2.3 从I2C1初始化
//--------------------------------------------------------------------------------------------------------------------
// @brief I2CInit
// @param None
// @retval None
// @note
//--------------------------------------------------------------------------------------------------------------------
void I2CInit(void)
{
GPIO_Config_T gpioConfigStruct;
I2C_Config_T i2cConfigStruct;
/** Enable I2C related Clock */
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOB);
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_I2C1);
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
/** Free I2C_SCL and I2C_SDA */
gpioConfigStruct.mode = GPIO_MODE_OUT;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.outtype = GPIO_OUT_TYPE_PP;
gpioConfigStruct.pupd = GPIO_PUPD_NO;
gpioConfigStruct.pin = GPIO_PIN_6;
GPIO_Config(GPIOB, &gpioConfigStruct);
gpioConfigStruct.pin = GPIO_PIN_7;
GPIO_Config(GPIOB, &gpioConfigStruct);
GPIO_SetBit(GPIOB,GPIO_PIN_6);
GPIO_SetBit(GPIOB,GPIO_PIN_7);
/** Connect I2C to SCL */
GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_6, GPIO_AF_PIN1);
/** Connect I2C to SDA */
GPIO_ConfigPinAF(GPIOB, GPIO_PIN_SOURCE_7, GPIO_AF_PIN1);
/** Config I2C1 GPIO */
gpioConfigStruct.mode = GPIO_MODE_AF;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.outtype = GPIO_OUT_TYPE_OD;
gpioConfigStruct.pupd = GPIO_PUPD_NO;
gpioConfigStruct.pin = GPIO_PIN_6;
GPIO_Config(GPIOB, &gpioConfigStruct);
gpioConfigStruct.pin = GPIO_PIN_7;
GPIO_Config(GPIOB, &gpioConfigStruct);
/** Config I2C1 */
I2C_Reset(I2C1);
RCM_ConfigI2CCLK(RCM_I2C1CLK_SYSCLK);
i2cConfigStruct.ack = I2C_ACK_ENABLE;
i2cConfigStruct.ackaddress = I2C_ACK_ADDRESS_7BIT;
i2cConfigStruct.address1 = SLAVEADDR0; //0xB0;
i2cConfigStruct.analogfilter = I2C_ANALOG_FILTER_ENABLE;
i2cConfigStruct.digitalfilter = I2C_DIGITAL_FILTER_0;
i2cConfigStruct.mode = I2C_MODE_SMBUSDEVICE;
i2cConfigStruct.timing = 0xB0420F13; //100K 48M //0x1042F013;
I2C_Config(I2C1,&i2cConfigStruct);
/** Enable the I2C1 Interrupt */
I2C_EnableInterrupt(I2C1,I2C_INT_RXIE|I2C_INT_ADDRIE|I2C_INT_STOPIE);
/** NVIC configuration */
NVIC_EnableIRQRequest(I2C1_IRQn,1);
/** slave configuration*/
//I2C_EnableSlaveByteControl(I2C1);
//I2C_EnableReload(I2C1);
//I2C_EnableStretchClock(I2C1);
/** Enable I2Cx */
I2C_Enable(I2C1);
}
2.4 I2C主器件主动读写操作处理过程
//----------------------------------------------------------------------------------------------------------
// I2C_MasterReadWrite I2C主器件主动读写操作
// 参数: uSlaveAdd 器件地址
// 参数: uCommandCode 读写指令或寄存器
// 参数: uCommandData 读写的数据
// 参数: uReadWrite 读写操作 0:写 、1:读
//----------------------------------------------------------------------------------------------------------
uint16_t I2C_MasterReadWrite(uint8_t uSlaveAdd,uint8_t uCommandCode,uint16_t uCommandData,uint8_t uReadWrite)
{
uint8_t Recev1[4] = {0,0,0,0};
//uint8_t Recev2 = 0x00;
uint8_t i = 0x00;
uint16_t uiCounter1 = 0x00;
uint16_t uiInputValue = 0x00;
uint8_t uAutoEndLen = 0x03;
uint8_t uAutoRecLen = 0x01;
uint8_t uMode = 0x00; //0:写1Byte;1:读1Byte;2:写1Word;3:读1Word
for(i=0;i<=40;i++)
{
uI2CData[i]=0;
uI2C2Data[i]=0;
}
if((uCommandCode&0x80)==0x80)
uAutoRecLen =2; //字节操作
else
uAutoRecLen =1;
if(uReadWrite==0)
{ //写
if((uCommandCode&0x80)==0x80)
{ //双字节操作
uAutoEndLen =4;
uMode=2; //写1Word
}
else
{ //写1Byte;
uMode=0; //写1Byte
}
}
else
{ //读
if((uCommandCode&0x80)==0x80)
{ //双字节操作
uMode=3; //读1Word
}
else
{ //读1Byte
uMode=1; //读1Byte
}
}
//---------------------------------------------------------------
I2CTimeout = I2CT_LONG_TIMEOUT;
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_BUSY) == SET) //IF BUSY
{
I2C2Init();
if((I2CTimeout--) == 0)
return 0;
}
//---------------------------------------------------------------
/** I2C Send Configuration*/
//I2C_HandlingTransfer(I2C2,uSlaveAdd,2,I2C_RELOAD_MODE_SOFTEND,I2C_GENERATE_START_WRITE);
/** Disable I2C interrupt */
//I2C_DisableInterrupt(I2C2,I2C_INT_RXIE|I2C_INT_ADDRIE|I2C_INT_STOPIE);
/** I2C Send Configuration*/
I2C_HandlingTransfer(I2C2,uSlaveAdd,uAutoEndLen,I2C_RELOAD_MODE_AUTOEND,I2C_GENERATE_START_WRITE);
//---------------------------------------------------------------
I2C_TxData(I2C2,uSlaveAdd); //发地址
//---------------------------------------------------------------
uiCounter1 = 0x00;
//If Write OK
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_TXCF) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++;
}
//---------------------------------------------------------------
I2C_TxData(I2C2,uCommandCode); //发需要读/写的寄存号
//---------------------------------------------------------------
uiCounter1 = 0x00;
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_TXCF) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
if(uReadWrite==0)
{ //写入
if(uMode==0)
{ //1Byte
I2C_TxData(I2C2,uCommandData&0xFF); //发数据1Byte
//---------------------------------------------------------------
uiCounter1 = 0x00;
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_TXCF) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
}
else if(uMode==2)
{ //1Word
I2C_TxData(I2C2,uCommandData&0xFF); //发Low Byte
//---------------------------------------------------------------
uiCounter1 = 0x00;
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_TXCF) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
I2C_TxData(I2C2,((uCommandData>>8)&0xFF)); //发High Byte
//---------------------------------------------------------------
uiCounter1 = 0x00;
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_TXCF) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
}
}
else
{ //读数据
I2C_TxData(I2C2,uSlaveAdd|0x01); //发地址+读标置位
//---------------------------------------------------------------
uiCounter1 = 0x00;
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_TXCF) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
I2C_HandlingTransfer(I2C2,uSlaveAdd,uAutoRecLen,I2C_RELOAD_MODE_SOFTEND,I2C_GENERATE_START_READ);
//---------------------------------------------------------------
for(i=0;i<uAutoRecLen;i++)
{
uiCounter1 = 0x00;
//If I2C_FLAG_RXBNE OK
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_RXBNE) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++; //超时检测
}
Recev1[i]=I2C_RxData(I2C2); //读数据
}
//---------------------------------------------------------------
}
//---------------------------------------------------------------
// I2C2 Generate Stop signal
//I2C_EnableGenerateStop(I2C2);
//---------------------------------------------------------------
uiCounter1 = 0x00;
//If I2C_FLAG_STOP OK
while(I2C_ReadStatusFlag(I2C2,I2C_FLAG_STOP) == RESET && uiCounter1<I2CT_LONG_TIMEOUT)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
/** I2C Clear Stop Flag */
I2C_ClearIntFlag(I2C2,I2C_INT_FLAG_STOP);
I2C_ConfigNumberOfBytes(I2C2, 1);
/** I2C Enable Interrupt */
//I2C_EnableInterrupt(I2C2,I2C_INT_RXIE|I2C_INT_ADDRIE|I2C_INT_STOPIE);
//---------------------------------------------------------------
uiInputValue=Recev1[1]<<8|Recev1[0];
return uiInputValue;
}
2.5 从器件响应主器件而返回数据操作处理过程
//----------------------------------------------------------------------------------------------------------
// I2C_SlaveSendData I2C从器件响应主器件而返回数据操作
// 参数: *i2c 从I2C
// 参数: uCommandCode 读写指令或寄存器
// 参数: uCommandData 读写的数据
// 参数: uReadWrite 读写操作 0:写 、1:读
//----------------------------------------------------------------------------------------------------------
void I2C_SlaveSendData(I2C_T *i2c)
{
uint16_t uiCounter1 = 0x00;
if(uSlaveSendFlag==1)
{
uSlaveSendFlag =0;
if((uI2CData[1]&0x80)==0x80)
uSlaveSendLen =2;
else
uSlaveSendLen =1;
//---------------------------------------------------------------
// Send Frist Byte
switch(uI2CData[1])
{
case 0x01: //单字节0x01寄存器
I2C_TxData(i2c,0x11);
break;
case 0x02: //单字节0x02寄存器
I2C_TxData(i2c,0x12);
break;
case 0x03: //单字节0x03寄存器
I2C_TxData(i2c,0x13);
break;
case 0x04: //单字节0x04寄存器
I2C_TxData(i2c,0x14);
break;
case 0x81: //双字节0x81寄存器低字节
I2C_TxData(i2c,0x81);
break;
case 0x82: //双字节0x82寄存器低字节
I2C_TxData(i2c,0x82);
break;
case 0x83: //双字节0x83寄存器低字节
I2C_TxData(i2c,0x83);
break;
case 0x84: //双字节0x84寄存器低字节
I2C_TxData(i2c,0x84);
break;
default:
I2C_TxData(i2c,0xFF);
break;
}
//---------------------------------------------------------------
uiCounter1 = 0x00;
while(I2C_ReadStatusFlag(i2c,I2C_FLAG_TXCF) == RESET && uiCounter1<48000)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
if((uI2CData[1]&0x80)==0x80)
{ // Send Second Byte
switch(uI2CData[1])
{
case 0x81: //双字节0x81寄存器高字节
I2C_TxData(i2c,0x91);
break;
case 0x82: //双字节0x82寄存器高字节
I2C_TxData(i2c,0x92);
break;
case 0x83: //双字节0x83寄存器高字节
I2C_TxData(i2c,0x93);
break;
case 0x84: //双字节0x84寄存器高字节
I2C_TxData(i2c,0x94);
break;
default:
I2C_TxData(i2c,0xFF);
break;
}
//---------------------------------------------------------------
uiCounter1 = 0x00;
while(I2C_ReadStatusFlag(i2c,I2C_FLAG_TXCF) == RESET && uiCounter1<48000)
{
uiCounter1++; //超时检测
}
//---------------------------------------------------------------
}
I2C_EnableGenerateStop(i2c); //发停止信号P
}
}
2.6 从I2C1的中断处理过程
/*!
* @brief I2C interrupt service routine
*
* @param None
*
* @retval None
*
* @note This function need to put into I2C1_IRQHandler() in apm32f0xx_int.c
*/
void I2C_Isr(void)
{
uint8_t udata;
static uint16_t uiDir=0;
static uint8_t uAddMatchFlag=0;
if(I2C_ReadIntFlag(I2C1,I2C_INT_FLAG_ADDR) == SET)
{
I2C_ConfigNumberOfBytes(I2C1, 1);
I2C_ClearIntFlag(I2C1,I2C_INT_FLAG_ADDR);
uAddMatchFlag=1; //地址匹配标志置1
udata = (uint8_t)I2C_RxData(I2C1);
uiDir= (udata&0x01);
if(uiDir==1)
{ // I2C1: slave enters transmitter mode.
//udata=0xB0;
}
else
{ // I2C1: slave enters receiver mode.
uI2CRxPos=0;
}
}
else if(I2C_ReadStatusFlag(I2C1,I2C_FLAG_RXBNE) == SET && uAddMatchFlag==1)
{
/** I2C_ConfigNumberOfBytes is necessary*/
I2C_ConfigNumberOfBytes(I2C1, 1);
udata = (uint8_t)I2C_RxData(I2C1);
uI2CData[uI2CRxPos]=udata;
uI2CRxPos++;
uI2CRxPos%=40;
if(uI2CRxPos==3 && uI2CData[0]==SLAVEADDR0 && (uI2CData[0]|0x01)==uI2CData[2])
{ //对本机的读指令
uSlaveSendFlag = 1;
//I2C_TxData(I2C1,0xFF);
printf("%s\r\n",&uI2CData[1]);
}
else if(uI2CRxPos>2 && uI2CData[0]==SLAVEADDR0)
{ //对本机的写指令
if((uI2CData[1]&0x80)==0 && uI2CRxPos==3)
{ //1Byte
//--------------------------------------
//请将数据写入相应的1Byte寄存器
//...
//--------------------------------------
I2C_EnableGenerateStop(I2C1); //发停止信号P
}
else if((uI2CData[1]&0x80)==0x80 && uI2CRxPos==4)
{ //1Word
//--------------------------------------
//请将数据写入相应的1Word寄存器
//...
//--------------------------------------
I2C_EnableGenerateStop(I2C1); //发停止信号P
}
}
}
else if(I2C_ReadIntFlag(I2C1,I2C_INT_FLAG_STOP) == SET && uAddMatchFlag==1)
{
I2C_ClearIntFlag(I2C1,I2C_INT_FLAG_STOP);
uAddMatchFlag =0;
if(uI2CRxPos>0)
{
printf("%s\r\n",&uI2CData[1]); //串口输出I2C1收来的数据
}
uI2CRxPos=0;
}
}
2.7 主I2C2的中断处理过程
/*!
* @brief I2C interrupt service routine
*
* @param None
*
* @retval None
*
* @note This function need to put into I2C1_IRQHandler() in apm32f0xx_int.c
*/
void I2C2_Isr(void)
{
uint8_t udata;
static uint8_t uAdd2MatchFlag=0;
static uint8_t uI2C2RxPos=0;
if(I2C_ReadIntFlag(I2C2,I2C_INT_FLAG_ADDR) == SET)
{
I2C_ConfigNumberOfBytes(I2C2, 1);
I2C_ClearIntFlag(I2C2,I2C_INT_FLAG_ADDR);
uAdd2MatchFlag =1;
uI2C2RxPos=0;
}
else if(I2C_ReadStatusFlag(I2C2,I2C_FLAG_RXBNE) == SET)
{
#if(1)
/** I2C_ConfigNumberOfBytes is necessary*/
I2C_ConfigNumberOfBytes(I2C2, 1);
udata = (uint8_t)I2C_RxData(I2C2);
uI2C2Data[uI2C2RxPos]=udata;
uI2C2RxPos++;
uI2C2RxPos%=40;
if(uI2C2RxPos==uSlaveSendLen)
{
I2C_EnableGenerateStop(I2C2);
}
#endif
}
else if(I2C_ReadIntFlag(I2C2,I2C_INT_FLAG_STOP) == SET)
{
I2C_ClearIntFlag(I2C2,I2C_INT_FLAG_STOP);
uAdd2MatchFlag =0;
if(uI2C2RxPos>0)
{
printf("%s\r\n",uI2C2Data); //串口输出I2C2收来的数据
}
uI2C2RxPos =0;
}
}
2.8 按键的中断处理过程
/*!
* @brief BUTTON_KEY interrupt service routine
*
* @param None
*
* @retval None
*
* @note This function need to put into EINT0_1_IRQHandler()
* in apm32f0xx_int.c
*/
void APM_MINI_PB_I2C_Isr()
{
//uint8_t uDataTx[]="Hello master\r\n";
if(EINT_ReadStatusFlag(EINT_LINE1)==SET)
{
EINT_ClearStatusFlag(EINT_LINE1);
if(uButtonDownFlag==0)
{
uButtonDownFlag = 1;
uButtonCounter++;
uButtonCounter%=5;
//按键I2C主器件向从器件读写数据
uSlaveReadData=I2C_MasterReadWrite(SLAVEADDR0,(0x00|uButtonCounter),0x8899,0x00); //写单字节(测试指令)
//uSlaveReadData=I2C_MasterReadWrite(SLAVEADDR0,(0x00|uButtonCounter),0x8899,0x01); //读单字节(测试指令)
//uSlaveReadData=I2C_MasterReadWrite(SLAVEADDR0,(0x80|uButtonCounter),0x8899,0x00); //写双字节(测试指令)
//uSlaveReadData=I2C_MasterReadWrite(SLAVEADDR0,(0x80|uButtonCounter),0x8899,0x01); //读双字节(测试指令)
}
}
}
2.9 主程序的处理过程
/*!
* @brief Main program
*
* @param None
*
* @retval None
*
* @note
*/
int main(void)
{
//uint16_t j=0;
SystemClock_PLL_Init(); //PLL 初始化,将系统时钟调整到48MHz
APM_MINI_LEDInit(LED3); //指示灯的初始化
APM_MINI_PBInit(BUTTON_KEY1,BUTTON_MODE_EINT); //按键Key1的端口初始化
APM_MINI_COMInit(COM1); //串口1的初始化
I2C2Init(); //主I2C初始化
I2CInit(); //从I2C初始化
//I2C_DATATX("TX=%s Code = %d\r\n","test 4321",1);
while(1)
{
/** Press the BUTTON_KEY1, slave will send data to
master along with information that showed in serial port*/
//APM_MINI_LEDToggle(LED3);
if(GPIO_ReadInputBit(GPIOA,GPIO_PIN_1)==BIT_SET)
{ //防按键重复检测到
uButtonDownFlag = 0;
}
I2C_SlaveSendData(I2C1); //从I2C返回主器件要求的数据
}
}
2.10 完整的源代码请下载
https://download.csdn.net/download/kingpower2018/87458548?spm=1001.2014.3001.5503
源代码下载