翻遍了网络,国产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
*******************************************************************************************/
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
*******************************************************************************************/
3.使用方法
初始化
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);
}
}