Files
CHJ/user/MCU/lhl_i2c_slave.c
2026-03-20 21:19:53 +08:00

466 lines
16 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "../main/SystemInclude.h"
#ifndef ENABLE_USER_I2C
#pragma message("[undefined] ENABLE_USER_I2C")
#elif(ENABLE_USER_I2C)
DMA_HandleTypeDef DMA_Handle_IIC_Rx,DMA_Handle_IIC_Tx;
//初始化IIC
//i2c_slave_init(100000 , 0x02);
/**------------------------------------------------------------------------
* @brief 初始化 I2C 从机模式
* @param clockSpeed: I2C 时钟速度,如 100000 表示 100kHz
* @param slaveAddress: 从机地址7位
* @note 配置 P0.1 为 SCL复用为 SCLP0.2 为 SDA复用为 SDA
* 均为开漏输出。使能 I2C 事件、缓冲和错误中断。
* @example I2C_Slave_Init(100000, 0x02); // 100kHz, 从机地址 0x02
**/
void I2C_Slave_Init(u32 clockSpeed ,u16 slaveAddress)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
/* 1. 设置 I2C GPIO复用端口 */
GPIO_InitStructure.Pin = GPIO_PIN_1;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD; // I2C SCL开漏输出
GPIO_InitStructure.Current = GPIO_CURRENT_8mA;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.SchmittTrigger = ENABLE;
GPIO_InitStructure.Alternate = GPIO0_1_AF_SCL; // AFIO设置
LHL_GPIO_Init(GPIO0, &GPIO_InitStructure); //P0.1 P2.0 -> SCL
GPIO_InitStructure.Pin = GPIO_PIN_2;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD; // I2C SDA开漏输出
GPIO_InitStructure.Current = GPIO_CURRENT_8mA;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.SchmittTrigger = ENABLE;
GPIO_InitStructure.Alternate = GPIO0_2_AF_SDA; // AFIO设置
LHL_GPIO_Init(GPIO0, &GPIO_InitStructure); //P0.2 P1.4 -> SDA
/* 2. 配置 I2C 外设 */
I2C_InitStructure.ClockSpeed = clockSpeed; //100KHz Standard I2C Speed
I2C_InitStructure.DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.OwnAddress1 = slaveAddress; //Temporary slave device address
I2C_InitStructure.I2CAck = I2C_Ack_Enable;
I2C_InitStructure.AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
LHL_I2C_Init(&I2C_InitStructure);
// LHL_I2C_StretchClockCmd(DISABLE);//关闭时钟延展
LHL_I2C_ITConfig(I2C_IT_EVT|I2C_IT_BUF|I2C_IT_ERR, ENABLE);
NVIC_SetPriority(I2C1_EV_IRQn,0);
NVIC_EnableIRQ(I2C1_EV_IRQn);
}
/*********************************************************************************************************************************************/
/*中断 方式*/
/*********************************************************************************************************************************************/
I2C_DATA_TypeDef I2CData;
/**------------------------------------------------------------------------
* @brief I2C1 事件中断处理函数(中断方式)
* @note 处理 I2C 从机通信中的各种事件:地址匹配、数据接收、数据发送、
* 停止条件、应答失败等。根据 R_W 位判断主机的读写操作,并调用
* 对应的处理函数(需外部定义 I2CStartWriteProcess, I2CStartReadProcess,
* I2CReceiveProcess, I2CTransmitProcess, I2CResetProcess
* 该函数适用于不使用 DMA 的中断驱动模式。
**/
void I2C1_EV_IRQHandler()
{
__IO uint32_t sta1,sta2;
__IO uint32_t R_W;
sta1 = pI2C->SR1;
sta2 = pI2C->SR2; //清除STOPF
R_W = (sta2&I2C_SR2_TRA_Msk)>>I2C_SR2_TRA_Pos; //判断当前是读Master读Slave还是写Master写Slave
if(sta1 & I2C_SR1_ADDR_Msk)//I2C地址匹配
{
LHL_I2C_ClearFlag(I2C_FLAG_ADDR);
LHL_I2C_ClearFlag(I2C_FLAG_TXE);
if(R_W == MASTER_READ_SLAVE) I2CStartWriteProcess(); //更新状态参数 IIC从机开始发送(向主机写)寄存器地址
else I2CStartReadProcess();//更新状态参数 IIC开始接收数据
}
if(sta1 & I2C_SR1_STOPF_Msk)
{
LHL_I2C_ClearFlag(I2C_FLAG_STOPF);
return;
}
if(sta1 & I2C_SR1_AF_Msk) //主机发送NACK
{
I2CResetProcess(); //重置状态参数
LHL_I2C_Cmd(ENABLE);//保持IIC开启
LHL_I2C_ClearFlag(I2C_FLAG_TXE | I2C_FLAG_RXNE | I2C_FLAG_AF);
LHL_I2C_ClearFlag(I2C_FLAG_PECERR | I2C_FLAG_OVR | I2C_FLAG_BERR);
return;
}
if((sta1 & I2C_SR1_RXNE_Msk) && (R_W==MASTER_WRITE_SLAVE)) //IIC写入
{
LHL_I2C_ClearFlag(I2C_FLAG_RXNE);
I2CData.rxData[I2CData.rxCounter++] = LHL_I2C_ReceiveData();
I2CReceiveProcess();//发送处理,准备发送数据(一直收等I2C_SR1_ADDR_Msk变成主机读命令(即从机发)
return;
}
if((sta1 & I2C_SR1_TXE_Msk) && (R_W==MASTER_READ_SLAVE)) //IIC发送(单字节)
{
I2CTransmitProcess();//发送处理,当前设置为每3字节处理一次主机没有停止接收前一直可发
LHL_I2C_SendData(I2CData.txData[I2CData.txCounter++]);
return;
}
if(sta1 & I2C_SR1_BERR_Msk) { LHL_I2C_ClearFlag(I2C_FLAG_BERR); return;} //clear BERR
__DSB();//make sure intterupt flag cleared
}
//中断 测试代码========================================================
#define TEST_BUF_LEN 20
uint8_t iic_tx_buf[TEST_BUF_LEN] = {0} ,iic_tx_len ;
uint8_t iic_rx_buf[TEST_BUF_LEN] = {0};
uint16_t iic_tx_cnt = 0;
uint16_t iic_rx_cnt = 0;
void I2C1_EV_IRQHandler2(void)//
{
__IO uint32_t sta1,sta2;
__IO uint32_t R_W;
sta1 = pI2C->SR1;
sta2 = pI2C->SR2; //清除STOPF
R_W = (sta2&I2C_SR2_TRA_Msk)>>I2C_SR2_TRA_Pos; //判断当前是读Master读Slave还是写Master写Slave
if(sta1 & I2C_SR1_ADDR_Msk)//I2C address match
{
if(R_W == MASTER_READ_SLAVE)iic_tx_cnt = 0;//主机读取数据,从机发送模式
else iic_rx_cnt = 0;//主机写入数据,从机接收模式
}
if(sta1 & I2C_SR1_STOPF_Msk)
{
pI2C->CR1 |= I2C_CR1_STOP_Msk;
}
if(sta1 & I2C_SR1_AF_Msk)
{
pI2C->CR1 |= I2C_SR1_AF_Msk; //主机nack
pI2C->SR1 &= ~I2C_SR1_AF_Msk;
}
if((sta1 & I2C_SR1_RXNE_Msk) && (R_W==0)) // write
{
//接收到写入数据
iic_rx_buf[iic_rx_cnt++] = pI2C->DR;
if(iic_rx_cnt==2)IIC_Cmd_Check_Test();
}
if((sta1 & I2C_SR1_TXE_Msk) && (R_W==1)) //trasmitted one byte
{
pI2C->DR = iic_tx_buf[iic_tx_cnt++];
}
__DSB();//make sure intterupt flag cleared
}
void IIC_Cmd_Check_Test(void)
{
u16 regcmd = (I2CData.rxData[0]<<8) + I2CData.rxData[1];
u8 i;
switch(regcmd)
{
case 0x821A:
iic_tx_buf[0] = 0x00;
iic_tx_buf[1] = 0x01;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_len=3;
break;
case 0x30:
iic_tx_buf[0] = 0x30;
iic_tx_buf[1] = 0x31;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_buf[3] = 0x40;
iic_tx_buf[4] = 0x41;
iic_tx_buf[5] = I2CWordCrc(iic_tx_buf[3], iic_tx_buf[4]);
iic_tx_buf[6] = 0x32;
iic_tx_buf[7] = 0x33;
iic_tx_buf[8] = I2CWordCrc(iic_tx_buf[6], iic_tx_buf[7]);
iic_tx_buf[9] = 0x34;
iic_tx_buf[10] = 0x35;
iic_tx_buf[11] = I2CWordCrc(iic_tx_buf[9], iic_tx_buf[10]);
iic_tx_buf[12] = 0x36;
iic_tx_buf[13] = 0x37;
iic_tx_buf[14] = I2CWordCrc(iic_tx_buf[12], iic_tx_buf[13]);
iic_tx_buf[15] = 0x38;
iic_tx_buf[16] = 0x39;
iic_tx_buf[17] = I2CWordCrc(iic_tx_buf[15], iic_tx_buf[16]);
iic_tx_len=18;
break;
case 0x36:
iic_tx_buf[0] = 0x32;
iic_tx_buf[1] = 0x33;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_len=3;
break;
case 0x81:
iic_tx_buf[0] = 0x01;
iic_tx_buf[1] = 0x41;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_len=3;
break;
case 0x3A:
iic_tx_buf[0] = 0x01;
iic_tx_buf[1] = 0x41;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_buf[3] = 0x40;
iic_tx_buf[4] = 0x41;
iic_tx_buf[5] = I2CWordCrc(iic_tx_buf[3], iic_tx_buf[4]);
iic_tx_len=6;
break;
case 0x3C:
iic_tx_buf[0] = 0x01;
iic_tx_buf[1] = 0x41;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_buf[3] = 0x40;
iic_tx_buf[4] = 0x41;
iic_tx_buf[5] = I2CWordCrc(iic_tx_buf[3], iic_tx_buf[4]);
iic_tx_buf[6] = 0x32;
iic_tx_buf[7] = 0x33;
iic_tx_buf[8] = I2CWordCrc(iic_tx_buf[6], iic_tx_buf[7]);
iic_tx_len=9;
break;
case 0x56:
iic_tx_buf[0] = 0x01;
iic_tx_buf[1] = 0x41;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_len=12;
break;
case 0x85:
iic_tx_buf[0] = 0x01;
iic_tx_buf[1] = 0x41;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_buf[3] = 0x40;
iic_tx_buf[4] = 0x41;
iic_tx_buf[5] = I2CWordCrc(iic_tx_buf[3], iic_tx_buf[4]);
iic_tx_len=6;
break;
default:
iic_tx_buf[0] = 0x30;
iic_tx_buf[1] = 0x31;
iic_tx_buf[2] = I2CWordCrc(iic_tx_buf[0], iic_tx_buf[1]);
iic_tx_len=3;
break;
}
}
void IIC_Cmd_Check_Test1(void)
{
I2CData.readAddr = (I2CData.rxData[0]<<8) + I2CData.rxData[1];
MBBuf.StartAddr = I2CData.readAddr;
MBBuf.ByteNumber = 2 ; //一次读取的数据长度
MBBuf.RxPointer = &I2CData.rxData[2]; // 读取的数据从2开始放01用于存储接收到的地址
MBBuf.Index = 0;
// ModBusReadMultiByte();//根据MBBuf.StartAddr得到对应寄存器数据
// I2CData.rxData[2] = tempL.Word[0];
// I2CData.rxData[3] = tempL.Word[1];
// 获取数据个数
u8 count = 1;
switch(MBBuf.StartAddr) {
case 0x30: count = 6; break;
case 0x3A: count = 2; break;
case 0x3C: count = 3; break;
case 0x56: count = 4; break;
case 0x85: count = 2; break;
case 0xAE: count = 2; break;
default : count = 1;
}
// 生成响应
// UserReadDataProtocol();
u16 index = MBBuf.StartAddr - 0x30;//STD_REG_START
u16 data ;
u8 i , tx_idx = 0;
if(MBBuf.StartAddr > 0x30 && MBBuf.StartAddr <0x130)
{
for( i = 0; i < count; i++)
{
data = MBReg[index + i];
I2CData.txData[tx_idx++] = (data >> 8) & 0xFF;
I2CData.txData[tx_idx++] = data & 0xFF;
I2CData.txData[tx_idx++] = I2CWordCrc((data >> 8) & 0xFF, data & 0xFF);
}
}
else
{
I2CData.txData[tx_idx++] =0x00;
I2CData.txData[tx_idx++] =0x01;
I2CData.txData[tx_idx++] = I2CWordCrc(0x00, 0x01);
}
iic_tx_len = tx_idx;
}
//中断 测试代码 end========================================================
/*********************************************************************************************************************************************/
/*DMA 方式*/
/*********************************************************************************************************************************************/
/**------------------------------------------------------------------------
* @brief 初始化 I2C 从机接收的 DMA 通道通道0
* @note 配置 DMAMUX 将 I2C0_RX 请求映射到 DMA 通道0设置 DMA 为直接模式、
* 硬件触发、外设到内存、数据宽度1字节、循环模式并使能传输完成中断。
* 接收数据存入 I2CData.rxData 缓冲区。
* @example DMA_I2C_SlaveRx_Init();
**/
void DMA_I2C_SlaveRx_Init(void)
{
DMAMUX_InitTypeDef DMAMUX_InitStructure;
/* 配置DMAUX 请求源 RX触发DMA通道0 */
DMAMUX_InitStructure.DMA_Channel = DMA_CHANNEL_0; // 配置DMA0触发源
DMAMUX_InitStructure.Request_Source = REQUEST_SOURCE_I2C0_RX; // 触发源I2C0_RX
DMAMUX_InitStructure.Periodic_Trigger = DISABLE; // 禁用周期触发
DMAMUX_InitStructure.Cmd = ENABLE; // 使能
LHL_DMAMUX_Init(&DMAMUX_InitStructure);
/* 配置DMA通道0负责I2C RX数据搬运到缓存区 */
DMA_Handle_IIC_Rx.Channel = DMA_CHANNEL_0; // DMACH0
DMA_Handle_IIC_Rx.Mode = DMA_DIRECT_MODE; // 直接模式
DMA_Handle_IIC_Rx.Request = DMA_HARDWARE_REQUEST; // 硬件触发
DMA_Handle_IIC_Rx.Init.Direction = DMA_PERIPH_TO_MEMORY; // 外设->内存
DMA_Handle_IIC_Rx.Init.Src_Address = (uint32_t)(&pI2C->DR); // 源I2C DR寄存器
DMA_Handle_IIC_Rx.Init.Dest_Address = (uint32_t)&I2CData.rxData; // 目的:缓存区
DMA_Handle_IIC_Rx.Init.Data_Width = DMA_DATA_WIDTH_1B; // 数据宽度等于寄存器宽度
DMA_Handle_IIC_Rx.Init.Data_Size = 1; // 每次读取1个寄存器
DMA_Handle_IIC_Rx.Init.Repetition = 2; // 重复读取 次
DMA_Handle_IIC_Rx.Init.Trans_Mode = DMA_CYCLIC_TRANSMISSION; // 循环模式
if (LHL_DMA_Init(&DMA_Handle_IIC_Rx) != LHL_OK)
{
while(1); // DMA Init Error
}
LHL_DMA_ITConfig(&DMA_Handle_IIC_Rx, DMA_IT_MAJOR, ENABLE); // 开启DMA0主中断
NVIC_EnableIRQ(DMA1_CH0_IRQn);
}
/**------------------------------------------------------------------------
* @brief 初始化 I2C 从机发送的 DMA 通道通道1
* @note 配置 DMAMUX 将 I2C0_TX 请求映射到 DMA 通道1设置 DMA 为直接模式、
* 硬件触发、内存到外设、数据宽度1字节、单次模式传输长度由 iic_tx_len
* 全局变量决定),并使能传输完成中断。发送数据取自 I2CData.txData 缓冲区。
* @example DMA_I2C_SlaveTx_Init();
**/
void DMA_I2C_SlaveTx_Init(void)
{
DMAMUX_InitTypeDef DMAMUX_InitStructure;
/* 配置DMAUX 请求源 TX触发DMA通道1 */
DMAMUX_InitStructure.DMA_Channel = DMA_CHANNEL_1; // 配置DMA1触发源
DMAMUX_InitStructure.Request_Source = REQUEST_SOURCE_I2C0_TX; // 触发源I2C0_TX
DMAMUX_InitStructure.Periodic_Trigger = DISABLE;
DMAMUX_InitStructure.Cmd = ENABLE;
LHL_DMAMUX_Init(&DMAMUX_InitStructure);
/* 配置DMA通道1负责缓存区数据搬运到I2C TX数据 */
DMA_Handle_IIC_Tx.Channel = DMA_CHANNEL_1; // 句柄更改为DMA1
DMA_Handle_IIC_Tx.Mode = DMA_DIRECT_MODE;
DMA_Handle_IIC_Tx.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_IIC_Tx.Init.Direction = DMA_MEMORY_TO_PERIPH; // 内存->外设
DMA_Handle_IIC_Tx.Init.Src_Address = (uint32_t)&I2CData.txData; // 源:缓存区
DMA_Handle_IIC_Tx.Init.Dest_Address = (uint32_t)(&pI2C->DR); // 目的I2C DR寄存器
DMA_Handle_IIC_Tx.Init.Data_Width = DMA_DATA_WIDTH_1B;
DMA_Handle_IIC_Tx.Init.Data_Size = 1;
DMA_Handle_IIC_Tx.Init.Repetition = iic_tx_len;
DMA_Handle_IIC_Tx.Init.Trans_Mode = DMA_SINGLE_TRANSMISSION; // 单次模式 DMA_SINGLE_TRANSMISSION DMA_INTERVAL_TRANSMISSION
if (LHL_DMA_Init(&DMA_Handle_IIC_Tx) != LHL_OK)
{
while(1); // DMA Init Error
}
LHL_DMA_ITConfig(&DMA_Handle_IIC_Tx, DMA_IT_MAJOR, ENABLE); // 开启DMA1主中断
NVIC_EnableIRQ(DMA1_CH1_IRQn);
}
/**------------------------------------------------------------------------
* @brief 启动 I2C 从机 DMA 接收模式
* @param clockSpeed: I2C 时钟速度
* @param slaveAddress: 从机地址
* @note 初始化 I2C 从机,使能 I2C DMA 请求,初始化接收 DMA 并启动。
* 适用于使用 DMA 接收数据的场景。
* @example StartDMA_I2C_Slave_RX(100000, 0x02);
**/
void StartDMA_I2C_Slave_RX(u32 clockSpeed ,u16 slaveAddress)
{
I2C_Slave_Init(clockSpeed,slaveAddress);
LHL_I2C_DMACmd(ENABLE);//DMA请求使能
DMA_I2C_SlaveRx_Init();
LHL_DMA_Start(&DMA_Handle_IIC_Rx);
}
/**------------------------------------------------------------------------
* @brief I2C1 事件中断处理函数DMA 方式)
* @note 该版本配合 DMA 使用处理地址匹配事件当主机读从机R_W=1
* 停止并重新初始化发送 DMA 并启动;当主机写从机时,可在此处添加
* 接收处理逻辑(当前为空)。同时处理停止条件和应答失败。
* @example 用于 DMA 模式下的 I2C 中断处理。
**/
void I2C1_EV_IRQHandler3()//
{
__IO uint32_t sta1,sta2;
uint32_t R_W;
uint8_t data;
sta1 = pI2C->SR1;
sta2 = pI2C->SR2;
R_W = (sta2 & I2C_SR2_TRA_Msk)>>I2C_SR2_TRA_Pos; //write frame or read frame
if(sta1 & I2C_SR1_ADDR_Msk) //I2C address match
{
if (R_W == 1)
{
LHL_DMA_Stop(&DMA_Handle_IIC_Tx);
DMA_I2C_SlaveTx_Init();
LHL_DMA_Start(&DMA_Handle_IIC_Tx);
}
if (R_W == 0)
{
}
}
if(sta1 & I2C_SR1_STOPF_Msk)
{
pI2C->CR1 |= I2C_CR1_STOP_Msk;
}
if(sta1 & I2C_SR1_AF_Msk)
{
pI2C->CR1 |= I2C_SR1_AF_Msk; //主机nack
}
if(sta1 & I2C_SR1_AF_Msk)
{
pI2C->SR1 &= ~I2C_SR1_AF_Msk;
}
__DSB(); //make sure intterupt flag cleared
}
#endif