承接上一篇GD32F407硬件IIC主机模式,下面这一片介绍GD32F407硬件IIC从机模式,用MCU来做从机模式百度上有用的资源比较少,都是STM32里面的源码,千篇一律,有点水帖的感觉。
网上百度用GPIO模拟方式来做从机好像没有找到资料,也咨询了GD32F407的FE没有做过GPIO模拟从机,所以就用硬件方式来,官方源码这一次终于不是while结构了,而且官方还是IIC0做主机,IIC1从机的方式,真的太给力了。
1、从机接收模式下的软件流程
2、代码,官方源码从机发送模式
/*!
\brief handle I2C1 event interrupt request
\param[in] none
\param[out] none
\retval none
*/
void I2C1_EventIRQ_Handler(void)
{
if(i2c_flag_get(I2C1, I2C_ADDSEND)){
/* clear the ADDSEND bit */
i2c_flag_clear(I2C1, I2C_STAT0_ADDSEND);
}else if((i2c_flag_get(I2C1, I2C_TBE))&&(!i2c_flag_get(I2C1, I2C_AERR))){
/* send a data byte */
i2c_transmit_data(I2C1, *i2c_txbuffer++);
}
}
是不是感觉太少了,有点怀疑可行性啊,单独测试官方源码可以使用,但是拿到项目就不能用了,修改如下,加了一个清除ADDSEND bit后的标志,让它必须清除ADDSEND后才能下一步
void Si2c_Send_Data(uint32_t i2c_periph,BYTE Channel)
{
/* wait until ADDSEND bit is set */
if(i2c_flag_get(i2c_periph, I2C_ADDSEND)){
ADDSEND_FLAG=1;
/* clear ADDSEND bit */
i2c_flag_clear(i2c_periph, I2C_ADDSEND);
}
if(ADDSEND_FLAG){
if(i2c_flag_get(i2c_periph, I2C_RBNE)){
/* reception data register */
SReceAddrBuffer[Channel] = i2c_receive_data(i2c_periph);
}
}
}
也是很简单的
3、从机发送模式下的软件流程
4、从机发送模式代码,官方源码
*!
\brief handle I2C1 event interrupt request
\param[in] none
\param[out] none
\retval none
*/
void I2C1_EventIRQ_Handler(void)
{
if(i2c_flag_get(I2C1, I2C_ADDSEND)){
/* clear the ADDSEND bit */
i2c_flag_clear(I2C1, I2C_STAT0_ADDSEND);
}else if(i2c_flag_get(I2C1, I2C_RBNE)){
/* if reception data register is not empty ,I2C1 will read a data from I2C_DATA */
*i2c_rxbuffer++ = i2c_receive_data(I2C1);
}else if(i2c_flag_get(I2C1, I2C_STPDET)){
Status = SUCCESS;
/* clear the STPDET bit */
i2c_enable(I2C1);
/* disable I2C1 interrupt */
i2c_interrupt_disable(I2C1, I2C_CTL1_ERRIE | I2C_CTL1_BUFIE | I2C_CTL1_EVIE);
}
}
也是很简单,直接测试没有问题,
将发送和接收数据整合在一起,整合一定要注意接收和发送的标志位不同,
oid Si2c_Transfer_Data(uint32_t i2c_periph,BYTE Channel)
{
/* wait until ADDSEND bit is set */
if(i2c_flag_get(i2c_periph, I2C_ADDSEND)){
ADDSEND_FLAG=1;
/* clear ADDSEND bit */
i2c_flag_clear(i2c_periph, I2C_ADDSEND);
}
if(ADDSEND_FLAG){
if(i2c_flag_get(i2c_periph, I2C_RBNE) && (SRFalg ==0)){
/* Receive register addr*/
SReceAddrBuffer[Channel] = i2c_receive_data(i2c_periph);
SRFalg = 1;
}
if(SReceAddrBuffer[Channel] != 0XFF){
if(i2c_flag_get(i2c_periph, I2C_TBE)){
/* Send a word data */
MatchAdd_SendData(i2c_periph,SReceAddrBuffer[Channel]);
ADDSEND_FLAG=0;
SRFalg = 0;
}
if((SRFalg==1) && i2c_flag_get(i2c_periph, I2C_RBNE)){
/*Receive a byte data */
SReceDataBuffer[Channel] = i2c_receive_data(i2c_periph);
MatchAdd_ReceData(SReceAddrBuffer[Channel],SReceDataBuffer[Channel]);
}
if(i2c_flag_get(i2c_periph, I2C_STPDET)){
/* Receive mode clear the STPDET bit */
ic20Flag = 2;
SRFalg = 0;
ADDSEND_FLAG=0;
i2c_enable(i2c_periph);
i2c_interrupt_disable(i2c_periph, I2C_CTL1_ERRIE | I2C_CTL1_BUFIE | I2C_CTL1_EVIE);
}
if(ic20Flag == 2){
i2c_interrupt_enable(i2c_periph, I2C_CTL1_ERRIE | I2C_CTL1_BUFIE | I2C_CTL1_EVIE);
ic20Flag = 0;
}
}
}
}
注意:首先我们知道,,一般的IIC从设备都是有很多寄存器地址的,所以我们主机肯定也会发一个寄存器地址过来,官方源码里面是没有做处理的就当做(0x00),所以在整合的代码里面我有两次接收数据一个是寄存器地址放到地址数组和一个是数据放到了数据数组中。
5、接收发送处理函数
接收一个WORD数据
void MatchAdd_ReceData(BYTE Cmd,BYTE Data)
{
if (Cmd == 0x02)
{ /* Currently system power state. */
PwrState.byte = Data;
}
else if (Cmd == 0xB3)
{ /* button event state */
ButtState.byte = Data;
}
else if (Cmd == 0xB4)
{ /* button event number */
SCINumber = Data;
}
else if (Cmd == 0xB5)
{
HeartBeatEnable = Data;
}
else if (Cmd == 0xB6)
{
HeartBeatTimer = Data;
}
else if (Cmd == 0xB7)
{
HeartBeatFlag = Data;
}
}
发送一个WORD数据
void MatchAdd_SendData(uint32_t i2c_periph ,BYTE Cmd)
{
WORD rval = 0;
WORD *pntr;
if (Cmd == 0x02)
{ /* Currently system power state. */
rval = (WORD) PwrState.byte;
}
else if (Cmd == 0xB3)
{ /* button event state */
rval = (WORD) ButtState.byte;
}
else if (Cmd == 0xB4)
{ /* button sci event number */
rval = (WORD) SCINumber;
}
i2c_transmit_worddata(i2c_periph,rval);
}
注意:接受和发送的寄存器其实是同一个地址,通俗说就是从机有一个0x00--0xFF的地址的寄存器来让主机读写
官方里面没有发送一个Word数据函数,修改如下
void i2c_transmit_worddata(uint32_t i2c_periph, uint16_t Data)
{
/* send a data byte */
I2C_DATA(i2c_periph) = Data;
I2C_DATA(i2c_periph) = Data>>8;
}
6、中断函数
void I2C0_EV_IRQHandler(void)
{
//DEBUG(0XAA);
Si2c_Transfer_Data(I2C0,0);
}
到此GD32F407硬件IIC就结束了,其实硬件模式的从机还是比主机简单很多,主要是文档非常详细。