打印
[其他]

基于灵动微MM32SPIN27xx的I2C从机

[复制链接]
753|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
4c1l|  楼主 | 2023-2-24 23:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
翻遍了网络,国产MCU的资源还是较少,就贡献一个吧

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

1.头文件

/******************************************************************************************
* 文件  名:  drv_i2c_slave.h
* 文件描述:  i2c从机驱动程序头文件
* 文件版本:  V1.2
* 日    期:     2021/04/19
* 作    者:     何江     
* 版本记录
* 2021/04/19:第一次发布
*
*
******************************************************************************************/
#ifndef __DRV_I2C_SLAVE_H__
#define __DRV_I2C_SLAVE_H__
/******************************************************************************************
                                                                        头文件包含
*******************************************************************************************/
#include "HAL_device.h"
#include "HAL_conf.h"
#include "config.h"
/******************************************************************************************
                                                                        宏定义
*******************************************************************************************/
#define     USING_I2C1                                                    1
#define     USING_I2C2                                                    0

#define     I2C_SLAVE_WADDR                                            0xA0
#define     I2C_SLAVE_RADDR                                            0xA1

#define     I2C_SLAVE_TX_BUF_SIZE                                36
#define     I2C_SLAVE_RX_BUF_SIZE                                36
/******************************************************************************************
                                                                        全局变量
*******************************************************************************************/
//i2c工作状态机
typedef enum{
    I2C_IDLE,                    //I2C空闲
    I2C_ADDR_MATCH,        //地址匹配
    I2C_RECV_DATA,        //接收数据
    I2C_TRAN_DATA,        //发送数据
}i2c_sstatus_t;

//i2c设备从机属性结构
typedef    struct{
    I2C_TypeDef        *device;
    i2c_sstatus_t status;
    int8u_t         recv_buffer[I2C_SLAVE_RX_BUF_SIZE];
    int8u_t         tran_buffer[I2C_SLAVE_TX_BUF_SIZE];
    int8u_t         *precv_buf;
    int8u_t         *ptran_buf;
    int16s_t         recv_count;
    int16s_t         tran_count;
}i2c_slave_t;

#if (USING_I2C1)
extern i2c_slave_t i2c1_slave_dev;
#endif

#if (USING_I2C2)
extern i2c_slave_t i2c2_slave_dev;
#endif
/******************************************************************************************
                                                                        功能函数
*******************************************************************************************/
void i2c_slave_init(i2c_slave_t *pdev,int8u_t numb);

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

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

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

#endif

/******************************************************************************************
                                                    end file
*******************************************************************************************/


使用特权

评论回复
评论
4c1l 2023-2-24 23:23 回复TA
———————————————— 版权声明:本文为CSDN博主「老是要密码」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/hejiang177/article/details/116643518 
沙发
4c1l|  楼主 | 2023-2-24 23:25 | 只看该作者
2.驱动C文件

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

******************************************************************************************/
/******************************************************************************************
                                                                        头文件包含
*******************************************************************************************/
#include "drv_i2c_slave.h"
#include <rthw.h>
#include <rtthread.h>
/******************************************************************************************
                                                                        宏定义
*******************************************************************************************/
/******************************************************************************************
                                                                        功能函数
*******************************************************************************************/
/******************************************************************************************
                                                                        I2C从机GPIO初始化
*******************************************************************************************/
#if (USING_I2C1)
i2c_slave_t i2c1_slave_dev;
static void I2C1_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    NVIC_InitTypeDef NVIC_InitStructure;
    /* Enable GPIOA clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);   
    //复用功能选择
    GPIO_PinAFConfig( GPIOA ,GPIO_PinSource11, GPIO_AF_5);
    GPIO_PinAFConfig( GPIOA ,GPIO_PinSource12, GPIO_AF_5);
    //GPIO配置
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOA , &GPIO_InitStruct);
   
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_Init(GPIOA , &GPIO_InitStruct);
   
    //配置i2c中断
    NVIC_InitStructure.NVIC_IRQChannel = I2C1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);   
}
#endif
/******************************************************************************************
I2C_SendSlaveAddress 设置从机模式的本机地址
*******************************************************************************************/
static void I2C_SendSlaveAddress(I2C_TypeDef* i2c, u8 addr)
{
    WRITE_REG(i2c->IC_SAR, addr >> 1);
}
/******************************************************************************************
函数名称:    i2c_slave_init

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

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

输     出:   无            
*******************************************************************************************/
void i2c_slave_init(i2c_slave_t *pdev,int8u_t numb)
{
    I2C_TypeDef    *pi2c;
    I2C_InitTypeDef I2C_InitStruct;
    if(numb == 1){
    #if (USING_I2C1)
        pi2c = I2C1;
        I2C1_Configuration();
    #endif
    }else if(numb == 2){
    #if (USING_I2C2)
        pI2C = I2C2;
        I2C2_Configuration();
    #endif
    }else{
        return ;
    }
    pdev->device = pi2c;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 , ENABLE);
   
    I2C_StructInit(&I2C_InitStruct);
    /* I2C configuration */
    I2C_InitStruct.I2C_Mode             = I2C_Mode_SLAVE;
    I2C_InitStruct.I2C_OwnAddress = 0;        
    I2C_InitStruct.I2C_Speed             = I2C_Speed_STANDARD;
    I2C_InitStruct.I2C_ClockSpeed = 100000;   
    I2C_Init(pi2c, &I2C_InitStruct);
    I2C_ITConfig(pi2c,I2C_IT_RD_REQ|I2C_IT_STOP_DET,ENABLE);
    I2C_ITConfig(pi2c,I2C_IT_RX_FULL,ENABLE);
    //记得打开SLAVE使能标志
    pi2c->IC_CON &= (~IC_SLAVE_DISABLE);
    I2C_Cmd(pi2c, ENABLE);
    //从机匹配地址
    I2C_SendSlaveAddress(pi2c,I2C_SLAVE_WADDR);
    pdev->status = I2C_IDLE;
    pdev->ptran_buf = pdev->tran_buffer;
    pdev->precv_buf = pdev->recv_buffer;
    pdev->tran_count = I2C_SLAVE_TX_BUF_SIZE;
    pdev->recv_count = 0;
}
/*******************************************************************************************
函数名称:     i2c_slave_read_recv_data         

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

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

输     出:   实际接收到的数据个数                       
*******************************************************************************************/
int16s_t i2c_slave_read_recv_data(i2c_slave_t *pdev,int8u_t *pd)
{
    int16s_t count;
    rt_base_t level;
    if(pd == _NULL || pdev == _NULL)
        return -1;
    if(pdev->status == I2C_IDLE){
        level = rt_hw_interrupt_disable();
        for(count = 0; count < pdev->recv_count; count++){
            pd[count] = pdev->recv_buffer[count];
        }
        pdev->recv_count = 0;
        pdev->precv_buf = pdev->recv_buffer;
        rt_hw_interrupt_enable(level);
    }else{
        count = 0;
    }
    return count;
}
/*******************************************************************************************
函数名称:     i2c_slave_write_txbuffer_nbyte         

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

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

输     出:   实际写入的数据个数                       
*******************************************************************************************/
int16s_t i2c_slave_write_txbuffer_nbyte(i2c_slave_t *pdev,int16s_t index,int8u_t *pd,int16s_t count)
{
    int16s_t i;
    rt_base_t level;
    if(pd == _NULL || pdev == _NULL || ((index + count) >= I2C_SLAVE_TX_BUF_SIZE))
        return -1;
    //写入缓冲区完成之前不允许被i2c主机来读取
    //__disable_irq();
    level = rt_hw_interrupt_disable();
    for(i = index; i < (index + count); i++){
        pdev->tran_buffer[i] = pd[i];
    }
    //__enable_irq();
    //设置i2c发送缓冲区的数量
    pdev->tran_count = index + count;
    rt_hw_interrupt_enable(level);
    return i;
}
/*******************************************************************************************
函数名称:     i2c_slave_read_txbuffer_nbyte      

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

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

输     出:   实际读取的数据个数                       
*******************************************************************************************/
int16s_t i2c_slave_read_txbuffer_nbyte(i2c_slave_t *pdev,int16s_t index,int8u_t *pd,int16s_t count)
{
    int16s_t i;
    if(pd == _NULL || pdev == _NULL || ((index + count) >= I2C_SLAVE_TX_BUF_SIZE))
        return -1;
    for(i = 0; i < count; i++){
        pd[i] = pdev->tran_buffer[index];
    }
    return i;
}

/*******************************************************************************************
函数名称:     i2c_slave_isr         

功能描述:   I2C中断函数

输     入:   pdev        i2c结构
                        

输     出:   无
*******************************************************************************************/
void i2c_slave_isr(i2c_slave_t *pdev)
{
    //主机写,直接存入缓冲区
    if(I2C_GetITStatus(pdev->device, I2C_IT_RX_FULL)){
        pdev->status             = I2C_RECV_DATA;
        while(I2C_GetFlagStatus(pdev->device, I2C_STATUS_FLAG_RFNE)){
            if(pdev->recv_count < I2C_SLAVE_RX_BUF_SIZE){
                *pdev->precv_buf++ = I2C_ReceiveData(pdev->device);
                pdev->recv_count++;
            }else{
                //用于清除I2C_IT_RX_FULL中断
                I2C_ReceiveData(pdev->device);                                          
            }
        }
    }
    //总线停止中断,STOP中断后i2c slave进入空闲状态
    if(I2C_GetITStatus(pdev->device, I2C_IT_STOP_DET)){
        I2C_ITConfig(pdev->device,I2C_IT_TX_EMPTY,DISABLE);
        I2C_ClearITPendingBit(pdev->device, I2C_IT_TX_EMPTY);
        I2C_ClearITPendingBit(pdev->device, I2C_IT_STOP_DET);
        I2C_ClearFlag(pdev->device, I2C_IT_TX_EMPTY);
        I2C_ClearFlag(pdev->device, I2C_IT_TX_ABRT);
        pdev->status             = I2C_IDLE;
    }
    //主机请求读,只会进一次
    if(I2C_GetITStatus(pdev->device, I2C_IT_RD_REQ)){        
        I2C_ClearITPendingBit(pdev->device, I2C_IT_RD_REQ);
        //打开发送寄存器空中断
        I2C_ITConfig(pdev->device,I2C_IT_TX_EMPTY,ENABLE);
        //复位发送缓冲区指针
        pdev->ptran_buf     = pdev->tran_buffer;
        pdev->tran_count     = I2C_SLAVE_TX_BUF_SIZE;
        pdev->status            = I2C_TRAN_DATA;
    }
    switch(pdev->status){
        case I2C_IDLE:
            if(I2C_GetITStatus(pdev->device, I2C_IT_TX_EMPTY)){
                I2C_ITConfig(pdev->device,I2C_IT_TX_EMPTY,DISABLE);
                I2C_ClearITPendingBit(pdev->device, I2C_IT_TX_EMPTY);
            }
            break;
        case I2C_RECV_DATA:
            break;
        case I2C_TRAN_DATA:
            if(I2C_GetITStatus(pdev->device, I2C_IT_TX_EMPTY)){
                I2C_ClearITPendingBit(pdev->device, I2C_IT_TX_EMPTY);               
                if(pdev->tran_count > 0){
                    I2C_SendData(pdev->device,*pdev->ptran_buf++);
                    pdev->tran_count--;
                    //缓冲区数据发送完毕,退出发送
                    if(pdev->tran_count == 0){
                        pdev->ptran_buf = pdev->tran_buffer;
                        pdev->tran_count = I2C_SLAVE_TX_BUF_SIZE;
                        //中止发送
                        I2C_GenerateSTOP(I2C1, ENABLE);
                        pdev->status = I2C_IDLE;
                        break;
                    }
                }else{
                    //复位发送缓冲区指针
                    pdev->ptran_buf     = pdev->tran_buffer;
                    pdev->tran_count     = I2C_SLAVE_TX_BUF_SIZE;
                    //中止发送
                    I2C_GenerateSTOP(I2C1, ENABLE);
                    pdev->status = I2C_IDLE;
                }
            }
            break;
        default:
            break;
    }
}
/*******************************************************************************
* Function Name  : I2C1_IRQHandler
* Description    : This function handles I2C1 global interrupt request.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void I2C1_IRQHandler(void)
{
#if (USING_I2C1)
    i2c_slave_isr(&i2c1_slave_dev);
#endif
}
/******************************************************************************************
                                                                        endfile
*******************************************************************************************/

使用特权

评论回复
板凳
4c1l|  楼主 | 2023-2-24 23:25 | 只看该作者
使用方法

初始化

i2c_slave_init(&i2c1_slave_dev,1);

处理例程

/******************************************************************************************
                                                                    i2c_slave_proc
*******************************************************************************************/
static void i2c_slave_proc(void)
{
    int8u_t buffer[I2C_SLAVE_RX_BUF_SIZE];
    int16s_t len;
    len =  i2c_slave_read_recv_data(&i2c1_slave_dev,buffer);
    if(len > 0){
        switch(buffer[0]){
        case FLW4C_CONF_REG: //控制捕获命令
            break;
        case FLW4C_DOUT_PWR_REG:
            if(buffer[1] == 1){
                GPIO_SetBits(GPIOA,GPIO_Pin_15);
            }else{
                GPIO_ResetBits(GPIOA,GPIO_Pin_15);
            }
            break;
        }
    }else{
        int32u_t ufreq = 100, count = 150;
        int8u_t *pd = buffer;
        rt_memcpy(pd,&ufreq,4);pd += 4;
        rt_memcpy(pd,&count,4);pd += 4;
        
        rt_memcpy(pd,&ufreq,4);pd += 4;
        rt_memcpy(pd,&count,4);pd += 4;
        
        rt_memcpy(pd,&ufreq,4);pd += 4;
        rt_memcpy(pd,&count,4);pd += 4;
        
        rt_memcpy(pd,&ufreq,4);pd += 4;
        rt_memcpy(pd,&count,4);
        i2c_slave_write_txbuffer_nbyte(&i2c1_slave_dev,0,buffer,32);
    }
}

使用特权

评论回复
地板
tpgf| | 2023-3-6 16:12 | 只看该作者
iic使用工作状态机简直不要太方便了

使用特权

评论回复
5
木木guainv| | 2023-3-6 16:57 | 只看该作者
如果我们自己使用io口模拟iic  是不是一般就不会有什么bug了啊

使用特权

评论回复
6
磨砂| | 2023-3-6 17:06 | 只看该作者
处理例程里边 变量的自加4是什么意思呢

使用特权

评论回复
7
晓伍| | 2023-3-6 17:19 | 只看该作者
看到楼主的中断处理函数了  处理的太干净利索了

使用特权

评论回复
8
八层楼| | 2023-3-7 08:08 | 只看该作者
如果想要在iic上挂满外设  是否对带载能力有一定的要求 呀

使用特权

评论回复
9
观海| | 2023-3-7 08:53 | 只看该作者
如果iic总线上挂好多个设备 怎么一次性的写入那么多地址呢

使用特权

评论回复
10
Undshing| | 2023-3-9 18:52 | 只看该作者
观海 发表于 2023-3-7 08:53
如果iic总线上挂好多个设备 怎么一次性的写入那么多地址呢

不行,iic一次只能和一个设备通信

使用特权

评论回复
11
Jacquetry| | 2023-3-12 22:16 | 只看该作者
设备挂载多了会不会带不动啊

使用特权

评论回复
12
星辰大海不退缩| | 2023-3-13 15:56 | 只看该作者
一般不会涉及到IIC总线上挂载多主机多从机的情况。但挂载单个主机多个从机的情况还是有的。

使用特权

评论回复
13
wsnsyy| | 2024-11-12 22:18 | 只看该作者
做从机,怎么像EEPROM那样配置从机地址和寄存器寄存器地址

使用特权

评论回复
14
suncat0504| | 2024-11-16 21:27 | 只看该作者
处理中间的各种状态,根据状态值确定下一步的动作,感觉挺麻烦的。谢谢楼主分享!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

55

主题

566

帖子

2

粉丝