[其他] 基于灵动微MM32SPIN27xx的I2C从机

[复制链接]
1732|14
 楼主| 4c1l 发表于 2023-2-24 23:23 | 显示全部楼层 |阅读模式
  1. 翻遍了网络,国产MCU的资源还是较少,就贡献一个吧

  2. 使用了国产的RT-thread操作系统+M0的单片机,在灵动微MM32SPIN27上实现的硬件I2C从机,有需要的朋友拿去,根据情况改改可用

  3. 1.头文件

  4. /******************************************************************************************
  5. * 文件  名:  drv_i2c_slave.h
  6. * 文件描述:  i2c从机驱动程序头文件
  7. * 文件版本:  V1.2
  8. * 日    期:     2021/04/19
  9. * 作    者:     何江     
  10. * 版本记录
  11. * 2021/04/19:第一次发布
  12. *
  13. *
  14. ******************************************************************************************/
  15. #ifndef __DRV_I2C_SLAVE_H__
  16. #define __DRV_I2C_SLAVE_H__
  17. /******************************************************************************************
  18.                                                                         头文件包含
  19. *******************************************************************************************/
  20. #include "HAL_device.h"
  21. #include "HAL_conf.h"
  22. #include "config.h"
  23. /******************************************************************************************
  24.                                                                         宏定义
  25. *******************************************************************************************/
  26. #define     USING_I2C1                                                    1
  27. #define     USING_I2C2                                                    0

  28. #define     I2C_SLAVE_WADDR                                            0xA0
  29. #define     I2C_SLAVE_RADDR                                            0xA1

  30. #define     I2C_SLAVE_TX_BUF_SIZE                                36
  31. #define     I2C_SLAVE_RX_BUF_SIZE                                36
  32. /******************************************************************************************
  33.                                                                         全局变量
  34. *******************************************************************************************/
  35. //i2c工作状态机
  36. typedef enum{
  37.     I2C_IDLE,                    //I2C空闲
  38.     I2C_ADDR_MATCH,        //地址匹配
  39.     I2C_RECV_DATA,        //接收数据
  40.     I2C_TRAN_DATA,        //发送数据
  41. }i2c_sstatus_t;

  42. //i2c设备从机属性结构
  43. typedef    struct{
  44.     I2C_TypeDef        *device;
  45.     i2c_sstatus_t status;
  46.     int8u_t         recv_buffer[I2C_SLAVE_RX_BUF_SIZE];
  47.     int8u_t         tran_buffer[I2C_SLAVE_TX_BUF_SIZE];
  48.     int8u_t         *precv_buf;
  49.     int8u_t         *ptran_buf;
  50.     int16s_t         recv_count;
  51.     int16s_t         tran_count;
  52. }i2c_slave_t;

  53. #if (USING_I2C1)
  54. extern i2c_slave_t i2c1_slave_dev;
  55. #endif

  56. #if (USING_I2C2)
  57. extern i2c_slave_t i2c2_slave_dev;
  58. #endif
  59. /******************************************************************************************
  60.                                                                         功能函数
  61. *******************************************************************************************/
  62. void i2c_slave_init(i2c_slave_t *pdev,int8u_t numb);

  63. int16s_t i2c_slave_read_recv_data(i2c_slave_t *pdev,int8u_t *pd);

  64. int16s_t i2c_slave_write_txbuffer_nbyte(i2c_slave_t *pdev,int16s_t index,int8u_t *pd,int16s_t count);

  65. int16s_t i2c_slave_read_txbuffer_nbyte(i2c_slave_t *pdev,int16s_t index,int8u_t *pd,int16s_t count);

  66. #endif

  67. /******************************************************************************************
  68.                                                     end file
  69. *******************************************************************************************/


评论

———————————————— 版权声明:本文为CSDN博主「老是要密码」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/hejiang177/article/details/116643518  发表于 2023-2-24 23:23
 楼主| 4c1l 发表于 2023-2-24 23:25 | 显示全部楼层
  1. 2.驱动C文件

  2. /******************************************************************************************
  3. * 文 件 名:  drv_i2c_slave.c
  4. * 文件描述:  i2c从机驱动程序文件
  5. * 文件版本:  V1.0
  6. * 日    期:     2020/12/14
  7. * 作    者:     何江     
  8. * 版本记录
  9. * 2020/12/14: 第一次发布
  10. *   

  11. ******************************************************************************************/
  12. /******************************************************************************************
  13.                                                                         头文件包含
  14. *******************************************************************************************/
  15. #include "drv_i2c_slave.h"
  16. #include <rthw.h>
  17. #include <rtthread.h>
  18. /******************************************************************************************
  19.                                                                         宏定义
  20. *******************************************************************************************/
  21. /******************************************************************************************
  22.                                                                         功能函数
  23. *******************************************************************************************/
  24. /******************************************************************************************
  25.                                                                         I2C从机GPIO初始化
  26. *******************************************************************************************/
  27. #if (USING_I2C1)
  28. i2c_slave_t i2c1_slave_dev;
  29. static void I2C1_Configuration(void)
  30. {
  31.     GPIO_InitTypeDef GPIO_InitStruct;
  32.     NVIC_InitTypeDef NVIC_InitStructure;
  33.     /* Enable GPIOA clock */
  34.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);   
  35.     //复用功能选择
  36.     GPIO_PinAFConfig( GPIOA ,GPIO_PinSource11, GPIO_AF_5);
  37.     GPIO_PinAFConfig( GPIOA ,GPIO_PinSource12, GPIO_AF_5);
  38.     //GPIO配置
  39.     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
  40.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
  41.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
  42.     GPIO_Init(GPIOA , &GPIO_InitStruct);
  43.    
  44.     GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
  45.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
  46.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
  47.     GPIO_Init(GPIOA , &GPIO_InitStruct);
  48.    
  49.     //配置i2c中断
  50.     NVIC_InitStructure.NVIC_IRQChannel = I2C1_IRQn;
  51.     NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
  52.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  53.     NVIC_Init(&NVIC_InitStructure);   
  54. }
  55. #endif
  56. /******************************************************************************************
  57. I2C_SendSlaveAddress 设置从机模式的本机地址
  58. *******************************************************************************************/
  59. static void I2C_SendSlaveAddress(I2C_TypeDef* i2c, u8 addr)
  60. {
  61.     WRITE_REG(i2c->IC_SAR, addr >> 1);
  62. }
  63. /******************************************************************************************
  64. 函数名称:    i2c_slave_init

  65. 功能描述:   I2C 从机初始化配置

  66. 输     入:   pdev  从机设备
  67.                         numb i2c外设号,有的mcu支持2路

  68. 输     出:   无            
  69. *******************************************************************************************/
  70. void i2c_slave_init(i2c_slave_t *pdev,int8u_t numb)
  71. {
  72.     I2C_TypeDef    *pi2c;
  73.     I2C_InitTypeDef I2C_InitStruct;
  74.     if(numb == 1){
  75.     #if (USING_I2C1)
  76.         pi2c = I2C1;
  77.         I2C1_Configuration();
  78.     #endif
  79.     }else if(numb == 2){
  80.     #if (USING_I2C2)
  81.         pI2C = I2C2;
  82.         I2C2_Configuration();
  83.     #endif
  84.     }else{
  85.         return ;
  86.     }
  87.     pdev->device = pi2c;
  88.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 , ENABLE);
  89.    
  90.     I2C_StructInit(&I2C_InitStruct);
  91.     /* I2C configuration */
  92.     I2C_InitStruct.I2C_Mode             = I2C_Mode_SLAVE;
  93.     I2C_InitStruct.I2C_OwnAddress = 0;        
  94.     I2C_InitStruct.I2C_Speed             = I2C_Speed_STANDARD;
  95.     I2C_InitStruct.I2C_ClockSpeed = 100000;   
  96.     I2C_Init(pi2c, &I2C_InitStruct);
  97.     I2C_ITConfig(pi2c,I2C_IT_RD_REQ|I2C_IT_STOP_DET,ENABLE);
  98.     I2C_ITConfig(pi2c,I2C_IT_RX_FULL,ENABLE);
  99.     //记得打开SLAVE使能标志
  100.     pi2c->IC_CON &= (~IC_SLAVE_DISABLE);
  101.     I2C_Cmd(pi2c, ENABLE);
  102.     //从机匹配地址
  103.     I2C_SendSlaveAddress(pi2c,I2C_SLAVE_WADDR);
  104.     pdev->status = I2C_IDLE;
  105.     pdev->ptran_buf = pdev->tran_buffer;
  106.     pdev->precv_buf = pdev->recv_buffer;
  107.     pdev->tran_count = I2C_SLAVE_TX_BUF_SIZE;
  108.     pdev->recv_count = 0;
  109. }
  110. /*******************************************************************************************
  111. 函数名称:     i2c_slave_read_recv_data         

  112. 功能描述:   从I2C缓冲区中读取所有数据,并清除接收数据计数器

  113. 输     入:   pdev           i2c结构
  114.                         pd        保存数据的缓冲区

  115. 输     出:   实际接收到的数据个数                       
  116. *******************************************************************************************/
  117. int16s_t i2c_slave_read_recv_data(i2c_slave_t *pdev,int8u_t *pd)
  118. {
  119.     int16s_t count;
  120.     rt_base_t level;
  121.     if(pd == _NULL || pdev == _NULL)
  122.         return -1;
  123.     if(pdev->status == I2C_IDLE){
  124.         level = rt_hw_interrupt_disable();
  125.         for(count = 0; count < pdev->recv_count; count++){
  126.             pd[count] = pdev->recv_buffer[count];
  127.         }
  128.         pdev->recv_count = 0;
  129.         pdev->precv_buf = pdev->recv_buffer;
  130.         rt_hw_interrupt_enable(level);
  131.     }else{
  132.         count = 0;
  133.     }
  134.     return count;
  135. }
  136. /*******************************************************************************************
  137. 函数名称:     i2c_slave_write_txbuffer_nbyte         

  138. 功能描述:   设置修改I2C发送缓冲区中数据,并更新发送数量

  139. 输     入:   pdev          i2c结构
  140.                         index            缓冲区数组索引
  141.                         pd        要写入数据的缓冲区
  142.                         count     要写入数据的个数

  143. 输     出:   实际写入的数据个数                       
  144. *******************************************************************************************/
  145. int16s_t i2c_slave_write_txbuffer_nbyte(i2c_slave_t *pdev,int16s_t index,int8u_t *pd,int16s_t count)
  146. {
  147.     int16s_t i;
  148.     rt_base_t level;
  149.     if(pd == _NULL || pdev == _NULL || ((index + count) >= I2C_SLAVE_TX_BUF_SIZE))
  150.         return -1;
  151.     //写入缓冲区完成之前不允许被i2c主机来读取
  152.     //__disable_irq();
  153.     level = rt_hw_interrupt_disable();
  154.     for(i = index; i < (index + count); i++){
  155.         pdev->tran_buffer[i] = pd[i];
  156.     }
  157.     //__enable_irq();
  158.     //设置i2c发送缓冲区的数量
  159.     pdev->tran_count = index + count;
  160.     rt_hw_interrupt_enable(level);
  161.     return i;
  162. }
  163. /*******************************************************************************************
  164. 函数名称:     i2c_slave_read_txbuffer_nbyte      

  165. 功能描述:   读取I2C发送缓冲区中数据,一般用来与应用程序中作比较用

  166. 输     入:   pdev           i2c结构
  167.                         index            缓冲区数组索引
  168.                         pd        要写入数据的缓冲区
  169.                         count     要写入数据的个数

  170. 输     出:   实际读取的数据个数                       
  171. *******************************************************************************************/
  172. int16s_t i2c_slave_read_txbuffer_nbyte(i2c_slave_t *pdev,int16s_t index,int8u_t *pd,int16s_t count)
  173. {
  174.     int16s_t i;
  175.     if(pd == _NULL || pdev == _NULL || ((index + count) >= I2C_SLAVE_TX_BUF_SIZE))
  176.         return -1;
  177.     for(i = 0; i < count; i++){
  178.         pd[i] = pdev->tran_buffer[index];
  179.     }
  180.     return i;
  181. }

  182. /*******************************************************************************************
  183. 函数名称:     i2c_slave_isr         

  184. 功能描述:   I2C中断函数

  185. 输     入:   pdev        i2c结构
  186.                         

  187. 输     出:   无
  188. *******************************************************************************************/
  189. void i2c_slave_isr(i2c_slave_t *pdev)
  190. {
  191.     //主机写,直接存入缓冲区
  192.     if(I2C_GetITStatus(pdev->device, I2C_IT_RX_FULL)){
  193.         pdev->status             = I2C_RECV_DATA;
  194.         while(I2C_GetFlagStatus(pdev->device, I2C_STATUS_FLAG_RFNE)){
  195.             if(pdev->recv_count < I2C_SLAVE_RX_BUF_SIZE){
  196.                 *pdev->precv_buf++ = I2C_ReceiveData(pdev->device);
  197.                 pdev->recv_count++;
  198.             }else{
  199.                 //用于清除I2C_IT_RX_FULL中断
  200.                 I2C_ReceiveData(pdev->device);                                          
  201.             }
  202.         }
  203.     }
  204.     //总线停止中断,STOP中断后i2c slave进入空闲状态
  205.     if(I2C_GetITStatus(pdev->device, I2C_IT_STOP_DET)){
  206.         I2C_ITConfig(pdev->device,I2C_IT_TX_EMPTY,DISABLE);
  207.         I2C_ClearITPendingBit(pdev->device, I2C_IT_TX_EMPTY);
  208.         I2C_ClearITPendingBit(pdev->device, I2C_IT_STOP_DET);
  209.         I2C_ClearFlag(pdev->device, I2C_IT_TX_EMPTY);
  210.         I2C_ClearFlag(pdev->device, I2C_IT_TX_ABRT);
  211.         pdev->status             = I2C_IDLE;
  212.     }
  213.     //主机请求读,只会进一次
  214.     if(I2C_GetITStatus(pdev->device, I2C_IT_RD_REQ)){        
  215.         I2C_ClearITPendingBit(pdev->device, I2C_IT_RD_REQ);
  216.         //打开发送寄存器空中断
  217.         I2C_ITConfig(pdev->device,I2C_IT_TX_EMPTY,ENABLE);
  218.         //复位发送缓冲区指针
  219.         pdev->ptran_buf     = pdev->tran_buffer;
  220.         pdev->tran_count     = I2C_SLAVE_TX_BUF_SIZE;
  221.         pdev->status            = I2C_TRAN_DATA;
  222.     }
  223.     switch(pdev->status){
  224.         case I2C_IDLE:
  225.             if(I2C_GetITStatus(pdev->device, I2C_IT_TX_EMPTY)){
  226.                 I2C_ITConfig(pdev->device,I2C_IT_TX_EMPTY,DISABLE);
  227.                 I2C_ClearITPendingBit(pdev->device, I2C_IT_TX_EMPTY);
  228.             }
  229.             break;
  230.         case I2C_RECV_DATA:
  231.             break;
  232.         case I2C_TRAN_DATA:
  233.             if(I2C_GetITStatus(pdev->device, I2C_IT_TX_EMPTY)){
  234.                 I2C_ClearITPendingBit(pdev->device, I2C_IT_TX_EMPTY);               
  235.                 if(pdev->tran_count > 0){
  236.                     I2C_SendData(pdev->device,*pdev->ptran_buf++);
  237.                     pdev->tran_count--;
  238.                     //缓冲区数据发送完毕,退出发送
  239.                     if(pdev->tran_count == 0){
  240.                         pdev->ptran_buf = pdev->tran_buffer;
  241.                         pdev->tran_count = I2C_SLAVE_TX_BUF_SIZE;
  242.                         //中止发送
  243.                         I2C_GenerateSTOP(I2C1, ENABLE);
  244.                         pdev->status = I2C_IDLE;
  245.                         break;
  246.                     }
  247.                 }else{
  248.                     //复位发送缓冲区指针
  249.                     pdev->ptran_buf     = pdev->tran_buffer;
  250.                     pdev->tran_count     = I2C_SLAVE_TX_BUF_SIZE;
  251.                     //中止发送
  252.                     I2C_GenerateSTOP(I2C1, ENABLE);
  253.                     pdev->status = I2C_IDLE;
  254.                 }
  255.             }
  256.             break;
  257.         default:
  258.             break;
  259.     }
  260. }
  261. /*******************************************************************************
  262. * Function Name  : I2C1_IRQHandler
  263. * Description    : This function handles I2C1 global interrupt request.
  264. * Input          : None
  265. * Output         : None
  266. * Return         : None
  267. *******************************************************************************/
  268. void I2C1_IRQHandler(void)
  269. {
  270. #if (USING_I2C1)
  271.     i2c_slave_isr(&i2c1_slave_dev);
  272. #endif
  273. }
  274. /******************************************************************************************
  275.                                                                         endfile
  276. *******************************************************************************************/
 楼主| 4c1l 发表于 2023-2-24 23:25 | 显示全部楼层
  1. 使用方法

  2. 初始化

  3. i2c_slave_init(&i2c1_slave_dev,1);

  4. 处理例程

  5. /******************************************************************************************
  6.                                                                     i2c_slave_proc
  7. *******************************************************************************************/
  8. static void i2c_slave_proc(void)
  9. {
  10.     int8u_t buffer[I2C_SLAVE_RX_BUF_SIZE];
  11.     int16s_t len;
  12.     len =  i2c_slave_read_recv_data(&i2c1_slave_dev,buffer);
  13.     if(len > 0){
  14.         switch(buffer[0]){
  15.         case FLW4C_CONF_REG: //控制捕获命令
  16.             break;
  17.         case FLW4C_DOUT_PWR_REG:
  18.             if(buffer[1] == 1){
  19.                 GPIO_SetBits(GPIOA,GPIO_Pin_15);
  20.             }else{
  21.                 GPIO_ResetBits(GPIOA,GPIO_Pin_15);
  22.             }
  23.             break;
  24.         }
  25.     }else{
  26.         int32u_t ufreq = 100, count = 150;
  27.         int8u_t *pd = buffer;
  28.         rt_memcpy(pd,&ufreq,4);pd += 4;
  29.         rt_memcpy(pd,&count,4);pd += 4;
  30.         
  31.         rt_memcpy(pd,&ufreq,4);pd += 4;
  32.         rt_memcpy(pd,&count,4);pd += 4;
  33.         
  34.         rt_memcpy(pd,&ufreq,4);pd += 4;
  35.         rt_memcpy(pd,&count,4);pd += 4;
  36.         
  37.         rt_memcpy(pd,&ufreq,4);pd += 4;
  38.         rt_memcpy(pd,&count,4);
  39.         i2c_slave_write_txbuffer_nbyte(&i2c1_slave_dev,0,buffer,32);
  40.     }
  41. }
tpgf 发表于 2023-3-6 16:12 | 显示全部楼层
iic使用工作状态机简直不要太方便了
木木guainv 发表于 2023-3-6 16:57 | 显示全部楼层
如果我们自己使用io口模拟iic  是不是一般就不会有什么bug了啊
磨砂 发表于 2023-3-6 17:06 | 显示全部楼层
处理例程里边 变量的自加4是什么意思呢
晓伍 发表于 2023-3-6 17:19 | 显示全部楼层
看到楼主的中断处理函数了  处理的太干净利索了
八层楼 发表于 2023-3-7 08:08 | 显示全部楼层
如果想要在iic上挂满外设  是否对带载能力有一定的要求 呀
观海 发表于 2023-3-7 08:53 | 显示全部楼层
如果iic总线上挂好多个设备 怎么一次性的写入那么多地址呢
Undshing 发表于 2023-3-9 18:52 | 显示全部楼层
观海 发表于 2023-3-7 08:53
如果iic总线上挂好多个设备 怎么一次性的写入那么多地址呢

不行,iic一次只能和一个设备通信
Jacquetry 发表于 2023-3-12 22:16 | 显示全部楼层
设备挂载多了会不会带不动啊
星辰大海不退缩 发表于 2023-3-13 15:56 | 显示全部楼层
一般不会涉及到IIC总线上挂载多主机多从机的情况。但挂载单个主机多个从机的情况还是有的。
wsnsyy 发表于 2024-11-12 22:18 | 显示全部楼层
做从机,怎么像EEPROM那样配置从机地址和寄存器寄存器地址
suncat0504 发表于 2024-11-16 21:27 | 显示全部楼层
处理中间的各种状态,根据状态值确定下一步的动作,感觉挺麻烦的。谢谢楼主分享!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

703

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部