从机通信实例

1、I2C从机模式
在从机模式下,I2C硬件时刻监视总线上是否出现4个已定义的从机地址。如地址被检测到,就会发出中断请求。在中断程序中回应主机的请求。

1-1 从机接收模式
在从机接收模式下,从主机接收数据字节。要初始化从接收模式,对任一从机地址寄存器(ADR0-3) 和从屏蔽寄存器(MASK0-3)。具体的寄存器配置如下表格所示:
32e0f1083e35412c94296a04d297311c?from=pc.jpg
图1 I2C从机模式配置寄存器

MASL位必须为0,并且I2CEN需置位去使能 I2C 功能。AA位必须置1以应答其自身的从机地址。STA、STO和SI位清 0。初始化ADR和CONSET后,I2C 接口开始等待,直到被其自身从机地址。如果方向位为0(W),则进入从接收模式。如果方向位为1(R),则进入从发送模式。接收到地址和方向位后,SI 位置位,可从状态寄存器(STAT) 读取一个有效状态代码。

1-2 从机发送模式
接收和处理第一个字节的方式与接收模式下相同。但是,在该模式下,方向位为 1,指示读操作。通过SDA发送串行数据,通过SCL输入串行时钟。起始和停止条件分别看作串行传输的开始和结束。在特定应用中,I2C可作为主机/从机。在从机模式,I2C 硬件查找自身从机地址。如果检测到其中一个地址,则请求中断。如果微控制器想成为总线主机,则在进入主机模式前,硬件将一直等待,直到总线空闲,这样就不会中断可能存在的从机操作。

1-3 从机接收发送简化流程
对于 I2C 从机接收发送,ME32F030R8T6还提供一套简化的状态标志来实现 I2C 的接收和发送。该方法无需查看I2C的状态代码,只需查看状态寄存器的SLVADDMATCH, SLVRXBUFFULL和SLVTXBUFEMPTY标志位。I2C从机初始化同上。I2C从机启动后,等待主机通讯请求。当从机接收到与自己匹配地址,SLVADDMATCH 状态置 1 并引发中断。当从主机接收到数据时,SLVRXBUFFULL标志位会被置1并产生中断,缓冲区数据读走后自动清零。如要发送数据,用户需在启动I2C后,存入发送数据,当数据被主机读走后, SLVTXBUFEMPTY标志位被置1并产生中断,用户需填入下一个将发送的数据。中断服务流程图如下:
cc71c814ae7448abb15be7d21260eec3?from=pc.jpg
图2 从机接收流程

2、I2C从机操作
在例程LIB->common->Drivers->Source文件夹内有i2c_slave.c文件,这个就是提供的I2C从机模式的驱动文件,里面包含了一些常用的驱动函数。

2-1 从机模式初始化
初始化I2C为从机模式的例子。
1. 将自身的从属地址装入ADR。
2. 使能I2C中断。
3. 向寄存器CONSET写入0x42来置位EN和清除MASL位,并使能从机功能。
void I2C_Slave_Init(uint32_t SlaveAddress)
  • {
  • //使能I2C时钟并复位
  • SYSCON->PRESETCTRL_b.I2C_RST_N |= 1;
  • SYSCON->SYSAHBCLKCTRL_b.I2C_CLK |= 1;
  • //清除I2C标志
  • I2C->CONCLR = 0xFF;   
  • //从机地址
  • I2C->ADR0 = SlaveAddress;
  • I2C->MASK0 = 0xFE;
  • I2C->MASK1 = 0xFE;
  • I2C->MASK2 = 0xFE;
  • I2C->MASK3 = 0xFE;
  • //从机模式
  • I2C->CONSET = I2C_S_CONCLR_I2ENC|I2C_S_CONSET_AA;
  • //使能I2C中断
  • NVIC_EnableIRQ(I2C_IRQn);
  • }
  • 复制代码
    2-2 从机接收状态处理

    状态代码: 0x0a

    已接收自身的 SLA+W,已返回 ACK。 即将接收数据,并返回 ACK。
    1. 向CONCLR 写入0x08 来清除SI 标志。
    2. 初始化从机数据接收缓冲区。
    3. 初始化从机数据接收缓冲区指针。
    4. 退出。

    状态代码: 0x13
    前一次寻址使用自身从机地址;已接收数据字节;已返回 ACK。 即将接收更多数据。
    1. 从DAT读取数据字节存入从机数据接收缓冲区。
    2. 缓冲区指针加1。如果是最后一个数据,跳转到5。
    3. 向CONCLR 写入0x0C 来清除SI 和AA标志。
    4. 退出。 .
    5. 向CONCLR 写入0x08 来清除SI 标志。
    6. 缓冲区指针加1。
    7. 退出。

    2-3 从机发射状态处理

    状态代码: 0x4a

    已接收到自身从机地址和读操作位,已返回 ACK。将发送数据和接收 ACK 位。
    1. 将从机发送缓冲区的第一个数据字节装入DAT。
    2. 向CONCLR 写入0x09 来清除SI 和TXRX标志。
    3. 初始化从机发送缓冲区。
    4. 从机发送缓冲区指针加1。
    5. 退出。

    状态代码: 0x5c
    已发送数据并接收了 ACK。将发送数据和接收 ACK 位。
    1. 将从机发送缓冲区的第一个数据字节装入DAT。 .
    2. 向CONCLR 写入0x08 来清除SI 标志。
    3. 从机发送缓冲区指针加1。
    4. 退出。

    2-4 I2C中断服务程序

    确定 I2C 的状态和处理该状态的状态程序。具体参考i2c_slave.c函数文件中的中断服务程序I2C_IRQHandler。当I2C总线监视到4个已定义的从机地址后便进入中断程序。
    int main(void)
  • {
  •         //init I2C pin  
  •         PC_11_INIT (PC_11_I2C_SDA );
  •         PC_10_INIT (PC_10_I2C_SCL );
  •                
  •         I2C->CONCLR = 0xFF;
  •         // Initial I2C, Slave Address is 0x5A;
  •         I2C_Slave_Init(0x52);         
  •         
  •         while(1);
  • }
  • /*****************************************************************************
  • *   I2C_Slave.c:  Sourse file for I2C comunication
  • *                ver 1.0
  • ******************************************************************************/
  • #include "CMSDK_CM0.h"
  • #include "sys.h"
  • #include "i2c_slave.h"
  • //Slave communication protocol
  • //i2c read
  • //1. master start i2c with slave address, write flag
  • //2. master write slave sub_addr
  • //3. master restart i2c with slave address, read flag
  • //4. slave send byte from sub_addr, and sub_addr increase
  • //5. repeat step 4
  • //6. master stop i2c
  • //i2c write
  • //1. master start i2c with slave address, write flag
  • //2. master write slave sub_addr
  • //3. slave sreceive byte and put to sub_addr, and sub_addr increase
  • //4. repeat step 3
  • //5. master stop i2c
  • extern uint32_t SystemCoreClock;
  • uint8_t Slave_Data_Memory[128] =
  • {
  •     0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
  •                 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
  •     0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
  •                 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
  •     0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
  •                 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
  •     0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
  •                 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
  • };
  • uint8_t I2CSlaveAddrMatch = 0;
  • uint8_t I2CSlaveAddr;
  • uint8_t I2CDataIndex=0;
  • uint8_t I2CReceiveSubAddr = 0;
  • /*****************************************************************************
  • ** Function name:                I2C initial
  • **
  • ** Descriptions:                I2C initial function
  • **
  • ** parameters:      
  • **                  SlaveAddress: Slave address in slave mode
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • void I2C_Slave_Init(uint32_t SlaveAddress)
  • {
  •         /*---  Enable I2C clock and de-assert reset  ---*/
  •     SYSCON->PRESETCTRL_b.I2C_RST_N |= 1;
  •     SYSCON->SYSAHBCLKCTRL_b.I2C_CLK |= 1;
  •    
  •         /*--- Clear flags ---*/
  •         I2C->CONCLR = 0xFF;   
  •                 I2C->ADR0 = SlaveAddress; // Slave address
  •                 I2C->MASK0 = 0xFE; // Slave address MASK
  • //                I2C->ADR1 = SlaveAddress; // Slave address
  •                 I2C->MASK1 = 0xFE; // Slave address MASK
  • //                I2C->ADR2 = SlaveAddress; // Slave address
  •                 I2C->MASK2 = 0xFE; // Slave address MASK
  • //                I2C->ADR3 = SlaveAddress; // Slave address
  •                 I2C->MASK3 = 0xFE; // Slave address MASK
  •                 I2C->CONSET = I2C_S_CONCLR_I2ENC|I2C_S_CONSET_AA; // Slave mode
  •         /* Enable the I2C Interrupt */
  •         NVIC_EnableIRQ(I2C_IRQn);
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_GetI2CStatus
  • **
  • ** Descriptions:                Get I2C Status
  • **
  • ** parameters:      None
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • uint8_t I2C_GetI2CStatus( void )
  • {
  •         return (I2C->STAT);
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_ReadFlag
  • **
  • ** Descriptions:                Read I2C Flag
  • **
  • ** parameters:      Read bit
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • uint8_t I2C_ReadFlag( uint8_t I2C_CONSET )
  • {
  •         return(I2C->CONSET & I2C_CONSET);      /* retuen flag */
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_SetFlag
  • **
  • ** Descriptions:                Set I2C Flag
  • **
  • ** parameters:      Set bit
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • void I2C_SetFlag( uint8_t I2C_CONSET )
  • {
  •         I2C->CONSET = I2C_CONSET;      /* Set flag */
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_ClearFlag
  • **
  • ** Descriptions:                Clear I2C Flag
  • **
  • ** parameters:      Clear bit
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • void I2C_ClearFlag( uint32_t I2C_CONCLR )
  • {
  •         I2C->CONCLR = I2C_CONCLR;      /* Clear flag */
  • }
  • /*****************************************************************************
  • * Function name:    I2C_IRQHandler
  • *
  • * Descriptions:     Use status to control process, slave mode
  • *
  • * parameters:       None
  • * Returned value:   None
  • *
  • *****************************************************************************/
  • void I2C_IRQHandler(void)
  • {
  •     if (I2C->STAT_b.SLVADDMATCH)
  •     {
  •                         //i2c start
  •                         I2C_ClearFlag(I2C_S_CONCLR_SLAADRC);
  •                         I2CSlaveAddrMatch = 1;
  •                         I2CSlaveAddr=I2C->DAT;
  •                         I2CReceiveSubAddr = 0;
  •                         I2CDataIndex ++;
  •     }
  •     if (I2CSlaveAddrMatch)
  •     {
  •         if (I2C->STAT_b.SLVRXBUFFULL )
  •         {   //i2c write
  •             if (I2CReceiveSubAddr != 1)
  •             {
  •               //get sub_addr  
  •                                                         I2CDataIndex=I2C->DAT;
  •                                                         I2C->DAT=Slave_Data_Memory[I2CDataIndex];
  •                                                         I2CReceiveSubAddr = 1;
  •             }
  •             else
  •             {
  •                                                         //receive data from master and put to data memory
  •                                                         Slave_Data_Memory[I2CDataIndex]=I2C->DAT;
  •                                                         I2CDataIndex++;
  •             }
  •         }
  •         else if(I2C->STAT_b.SLVTXBUFEMPTY)
  •         {   //i2c read
  •             I2C->DAT=Slave_Data_Memory[I2CDataIndex];
  •             I2CDataIndex++;
  •         }
  •     }
  •     else if(I2C->STAT_b.SLVTXBUFEMPTY)
  •     {
  •                         //prepare first data at beginning
  •                         I2CDataIndex=0;
  •                         I2CSlaveAddrMatch = 0;
  •                         I2CSlaveAddr=0;
  •                         I2C->DAT=Slave_Data_Memory[I2CDataIndex];
  •     }
  •     return;
  • }
  • 复制代码
    /*****************************************************************************
  • *   I2C_Slave.c:  Sourse file for I2C comunication
  • *                ver 1.0
  • ******************************************************************************/
  • #include "CMSDK_CM0.h"
  • #include "sys.h"
  • #include "i2c_slave.h"
  • //Slave communication protocol
  • //i2c read
  • //1. master start i2c with slave address, write flag
  • //2. master write slave sub_addr
  • //3. master restart i2c with slave address, read flag
  • //4. slave send byte from sub_addr, and sub_addr increase
  • //5. repeat step 4
  • //6. master stop i2c
  • //i2c write
  • //1. master start i2c with slave address, write flag
  • //2. master write slave sub_addr
  • //3. slave sreceive byte and put to sub_addr, and sub_addr increase
  • //4. repeat step 3
  • //5. master stop i2c
  • extern uint32_t SystemCoreClock;
  • uint8_t Slave_Data_Memory[128] =
  • {
  •     0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
  •                 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
  •     0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
  •                 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
  •     0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
  •                 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
  •     0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
  •                 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
  • };
  • uint8_t I2CSlaveAddrMatch = 0;
  • uint8_t I2CSlaveAddr;
  • uint8_t I2CDataIndex=0;
  • uint8_t I2CReceiveSubAddr = 0;
  • /*****************************************************************************
  • ** Function name:                I2C initial
  • **
  • ** Descriptions:                I2C initial function
  • **
  • ** parameters:      
  • **                  SlaveAddress: Slave address in slave mode
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • void I2C_Slave_Init(uint32_t SlaveAddress)
  • {
  •         /*---  Enable I2C clock and de-assert reset  ---*/
  •     SYSCON->PRESETCTRL_b.I2C_RST_N |= 1;
  •     SYSCON->SYSAHBCLKCTRL_b.I2C_CLK |= 1;
  •    
  •         /*--- Clear flags ---*/
  •         I2C->CONCLR = 0xFF;   
  •                 I2C->ADR0 = SlaveAddress; // Slave address
  •                 I2C->MASK0 = 0xFE; // Slave address MASK
  • //                I2C->ADR1 = SlaveAddress; // Slave address
  •                 I2C->MASK1 = 0xFE; // Slave address MASK
  • //                I2C->ADR2 = SlaveAddress; // Slave address
  •                 I2C->MASK2 = 0xFE; // Slave address MASK
  • //                I2C->ADR3 = SlaveAddress; // Slave address
  •                 I2C->MASK3 = 0xFE; // Slave address MASK
  •                 I2C->CONSET = I2C_S_CONCLR_I2ENC|I2C_S_CONSET_AA; // Slave mode
  •         /* Enable the I2C Interrupt */
  •         NVIC_EnableIRQ(I2C_IRQn);
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_GetI2CStatus
  • **
  • ** Descriptions:                Get I2C Status
  • **
  • ** parameters:      None
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • uint8_t I2C_GetI2CStatus( void )
  • {
  •         return (I2C->STAT);
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_ReadFlag
  • **
  • ** Descriptions:                Read I2C Flag
  • **
  • ** parameters:      Read bit
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • uint8_t I2C_ReadFlag( uint8_t I2C_CONSET )
  • {
  •         return(I2C->CONSET & I2C_CONSET);      /* retuen flag */
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_SetFlag
  • **
  • ** Descriptions:                Set I2C Flag
  • **
  • ** parameters:      Set bit
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • void I2C_SetFlag( uint8_t I2C_CONSET )
  • {
  •         I2C->CONSET = I2C_CONSET;      /* Set flag */
  • }
  • /*****************************************************************************
  • ** Function name:                I2C_ClearFlag
  • **
  • ** Descriptions:                Clear I2C Flag
  • **
  • ** parameters:      Clear bit
  • **
  • ** Returned value:        None
  • **
  • *****************************************************************************/
  • void I2C_ClearFlag( uint32_t I2C_CONCLR )
  • {
  •         I2C->CONCLR = I2C_CONCLR;      /* Clear flag */
  • }
  • /*****************************************************************************
  • * Function name:    I2C_IRQHandler
  • *
  • * Descriptions:     Use status to control process, slave mode
  • *
  • * parameters:       None
  • * Returned value:   None
  • *
  • *****************************************************************************/
  • void I2C_IRQHandler(void)
  • {
  •     if (I2C->STAT_b.SLVADDMATCH)
  •     {
  •                         //i2c start
  •                         I2C_ClearFlag(I2C_S_CONCLR_SLAADRC);
  •                         I2CSlaveAddrMatch = 1;
  •                         I2CSlaveAddr=I2C->DAT;
  •                         I2CReceiveSubAddr = 0;
  •                         I2CDataIndex ++;
  •     }
  •     if (I2CSlaveAddrMatch)
  •     {
  •         if (I2C->STAT_b.SLVRXBUFFULL )
  •         {   //i2c write
  •             if (I2CReceiveSubAddr != 1)
  •             {
  •               //get sub_addr  
  •                                                         I2CDataIndex=I2C->DAT;
  •                                                         I2C->DAT=Slave_Data_Memory[I2CDataIndex];
  •                                                         I2CReceiveSubAddr = 1;
  •             }
  •             else
  •             {
  •                                                         //receive data from master and put to data memory
  •                                                         Slave_Data_Memory[I2CDataIndex]=I2C->DAT;
  •                                                         I2CDataIndex++;
  •             }
  •         }
  •         else if(I2C->STAT_b.SLVTXBUFEMPTY)
  •         {   //i2c read
  •             I2C->DAT=Slave_Data_Memory[I2CDataIndex];
  •             I2CDataIndex++;
  •         }
  •     }
  •     else if(I2C->STAT_b.SLVTXBUFEMPTY)
  •     {
  •                         //prepare first data at beginning
  •                         I2CDataIndex=0;
  •                         I2CSlaveAddrMatch = 0;
  •                         I2CSlaveAddr=0;
  •                         I2C->DAT=Slave_Data_Memory[I2CDataIndex];
  •     }
  •     return;
  • }
  • 复制代码
    来源:老马识途单片机 https://www.toutiao.com/a6891102629535941134/