本篇主要讲基于GD32 mcu硬件I2C主从机DMA模式收发、PEC错误校验、多主机仲裁功能程序例程。
1.1 主机DMA接收和发送
#include "gd32f3x0.h"
#include <stdio.h>
#include "gd32f350r_eval.h"
#include "main.h"
#include "systick.h"
#define I2C0_Measure
//#define I2C1_Measure
#if defined I2C0_Measure
#define I2C_x I2C0
#define DMACH_Tx DMA_CH1
#define DMACH_Rx DMA_CH2
#define I2C_Measuring I2C0_Seven_Address_standard
#elif defined I2C1_Measure
#define I2C_x I2C1
#define DMACH_Tx DMA_CH3
#define DMACH_Rx DMA_CH4
#define I2C_Measuring I2C1_Seven_Address_standard
#endif
//#define Master_DMA_send
#define Master_DMA_receive
#define BufferNum 20
uint8_t write_buffer[BufferNum]={0};//send data buffer
uint8_t receive_buffer[BufferNum]={0};//receive data buffer
Param_Typedef I2Cx_Param;
uint8_t Data=0,Send_InterruptFlag=0;
void I2C_DMA_init(void);
/*define I2Cx Param struct array*/
static Param_Typedef Para_Aarry[]=
{
/*I2C0 param array*/
{I2C0,I2C0_SDA_GPIOx2,I2C0_SCL_GPIOy2,RCU_I2C0,standard_speed,I2Cx_Address_7bit,Send_7Address,Slaver_7Address},
{I2C0,I2C0_SDA_GPIOx2,I2C0_SCL_GPIOy2,RCU_I2C0,standard_speed,I2Cx_Address_10bit,Send_10Address,Slaver_10Address},
{I2C0,I2C0_SDA_GPIOx2,I2C0_SCL_GPIOy2,RCU_I2C0,High_speed,I2Cx_Address_7bit,Send_7Address,Slaver_7Address},
{I2C0,I2C0_SDA_GPIOx2,I2C0_SCL_GPIOy2,RCU_I2C0,High_speed,I2Cx_Address_10bit,Send_10Address,Slaver_10Address},
{I2C0,I2C0_SDA_GPIOx2,I2C0_SCL_GPIOy2,RCU_I2C0,More_HighSpeed,I2Cx_Address_7bit,Send_7Address,Slaver_7Address},
/*I2C1 param array*/
{I2C1,I2C1_SDA_GPIOx5,I2C1_SCL_GPIOy5,RCU_I2C1,standard_speed,I2Cx_Address_7bit,Send_7Address,Slaver_7Address},
{I2C1,I2C1_SDA_GPIOx5,I2C1_SCL_GPIOy5,RCU_I2C1,standard_speed,I2Cx_Address_10bit,Send_10Address,Slaver_10Address},
{I2C1,I2C1_SDA_GPIOx5,I2C1_SCL_GPIOy5,RCU_I2C1,High_speed,I2Cx_Address_7bit,Send_7Address,Slaver_7Address},
{I2C1,I2C1_SDA_GPIOx5,I2C1_SCL_GPIOy5,RCU_I2C1,High_speed,I2Cx_Address_10bit,Send_10Address,Slaver_10Address},
{I2C1,I2C1_SDA_GPIOx5,I2C1_SCL_GPIOy5,RCU_I2C1,More_HighSpeed,I2Cx_Address_7bit,Send_7Address,Slaver_7Address},
};
/*testing sequence enum*/
typedef enum
{
/*Measure I2C0*/
I2C0_Seven_Address_standard=0,
I2C0_Ten_Address_standard,
I2C0_Seven_Address_high,
I2C0_Ten_Address_high,
I2C0_Seven_Address_more_high,
/*Measure I2C1*/
I2C1_Seven_Address_standard,
I2C1_Ten_Address_standard,
I2C1_Seven_Address_high,
I2C1_Ten_Address_high,
I2C1_Seven_Address_more_high
}Test_Sequence_Enum;
/*Configure Param of I2Cx Send data*/
void Configure_I2CX_Send(Test_Sequence_Enum measure_sequence);
/*Configure Param of I2Cx receive data*/
void Configure_I2CX_Receive(Test_Sequence_Enum measure_sequence);
/*I2Cx param init*/
void Param_Init(Param_Typedef* i2c_param,Param_Typedef* Param_Array,Test_Sequence_Enum measure_sequence);
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
uint8_t num=0;
/*configure EVAL_COM1*/
gd_eval_com_init(EVAL_COM);
systick_config();
for(num=0;num<BufferNum;num++){
write_buffer[num]=num;
}
#ifdef Master_DMA_send
/*--------DMA send measuring-----------------*/
Param_Init(&I2Cx_Param,Para_Aarry,I2C_Measuring);
I2Cx_Init(&I2Cx_Param);
I2C_DMA_init();
delay_1ms(3000);
Master_DMA_SendData(write_buffer,BufferNum,&I2Cx_Param);
printf("I2C_DMA send data sucess!\n");
#elif defined Master_DMA_receive
/*--------DMA receive measuring-----------------*/
Param_Init(&I2Cx_Param,Para_Aarry,I2C_Measuring);
I2Cx_Init(&I2Cx_Param);
I2C_DMA_init();
delay_1ms(3000);
Master_DMA_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);
printf("I2C_DMA receive data sucess!\n");
#endif
while(1){
}
}
void I2C_DMA_init()
{
dma_parameter_struct dma_init_struct;
/* enable DMA clock */
rcu_periph_clock_enable(RCU_DMA);
/*deinitialize DMA TX*/
dma_deinit(DMACH_Tx);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = (uint32_t)write_buffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = 20;
dma_init_struct.periph_addr = (uint32_t)&I2C_DATA(I2C_x);
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(DMACH_Tx,&dma_init_struct);
/*configure DMA mode*/
dma_circulation_disable(DMACH_Tx);
dma_memory_to_memory_disable(DMACH_Tx);
dma_channel_enable(DMACH_Tx);
/*deinitialize DMA RX*/
dma_deinit(DMACH_Rx);
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)receive_buffer;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = 20;
dma_init_struct.periph_addr = (uint32_t)&I2C_DATA(I2C_x);
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
dma_init(DMACH_Rx,&dma_init_struct);
/*configure DMA mode*/
dma_circulation_disable(DMACH_Rx);
dma_memory_to_memory_disable(DMACH_Rx);
dma_channel_enable(DMACH_Rx);
}
/*!
\brief Configure Param of I2Cx receive data
\param[in] Test_Sequence_Enum measure_sequence //test sequence
\param[out] none
\retval none
*/
void Param_Init(Param_Typedef* i2c_param,Param_Typedef* Param_Array,Test_Sequence_Enum measure_sequence)
{
i2c_param->Memory_address=Param_Array[measure_sequence].Memory_address;
i2c_param->gpio_sclpin=Param_Array[measure_sequence].gpio_sclpin;
i2c_param->gpio_sdapin=Param_Array[measure_sequence].gpio_sdapin;
i2c_param->address=Param_Array[measure_sequence].address;
i2c_param->address_model=Param_Array[measure_sequence].address_model;
i2c_param->I2Cx_Periph=Param_Array[measure_sequence].I2Cx_Periph;
i2c_param->I2c_speed=Param_Array[measure_sequence].I2c_speed;
i2c_param->slaver_address=Param_Array[measure_sequence].slaver_address;
}
/*!
\brief I2C init
\param[in] I2Cx_PinSDA_Enum SDA pin
I2Cx_PinSCL_Enum SCL pin
rcu_periph_enum I2C periph
I2Cx_Speed_enum check clock frequence
Address_Model_Enum 7 or 10 address check
Slaver_Address_Enum 7 or 10 address
\param[out] none
\retval none
*/
void I2Cx_Init(Param_Typedef *i2c_init)
{
uint32_t gpiox_sda,pinx_sda,afx_sda,gpiox_scl,pinx_scl,afx_scl;
I2Cx_Pin_ClockEnable(i2c_init->gpio_sdapin,i2c_init->gpio_sclpin);//SCL and SDA GPIO enable
/*GPIO sda configure*/
gpiox_sda=(uint32_t)((i2c_init->gpio_sdapin)& 0xffffff00);
pinx_sda=(0x01<<((i2c_init->gpio_sdapin & 0xf0) >> 4));
afx_sda=AF((i2c_init->gpio_sdapin & 0x0f));
I2Cx_Pin_ClockEnable(i2c_init->gpio_sdapin,i2c_init->gpio_sclpin);
gpio_af_set(gpiox_sda, afx_sda, pinx_sda);//IO口引脚复用功能
gpio_mode_set(gpiox_sda, GPIO_MODE_AF, GPIO_PUPD_PULLUP,pinx_sda);//GPIO口、备用功能、端口上拉模式、具体引脚
gpio_output_options_set(gpiox_sda, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,pinx_sda);//GPIO口、推挽输出及开漏输出、输出速率、具体引脚
/*GPIO scl configure*/
gpiox_scl=(uint32_t)((i2c_init->gpio_sclpin)& 0xffffff00);
pinx_scl=(0x01<<((i2c_init->gpio_sclpin & 0xf0) >> 4));
afx_scl=AF((i2c_init->gpio_sclpin & 0x0f));
gpio_af_set(gpiox_scl, afx_scl,pinx_scl);//IO口引脚复用功能
gpio_mode_set(gpiox_scl, GPIO_MODE_AF, GPIO_PUPD_PULLUP,pinx_scl);//GPIO口、备用功能、端口上拉模式、具体引脚
/*--------------------------------I2C module initaial configure------------------------------------*/
/*enable I2C clock*/
rcu_periph_clock_enable(i2c_init->I2Cx_Periph);//使能I2C时钟
/*configure I2C clock*/
i2c_clock_config(i2c_init->Memory_address,i2c_init->I2c_speed,I2C_DTCY_2);//I2C的基地址、I2C数据传输速度、I2C的快速模式
/*configure I2C address*/
i2c_mode_addr_config(i2c_init->Memory_address,I2C_I2CMODE_ENABLE,i2c_init->address_model,i2c_init->slaver_address);//I2C基地址、I2C模式、7位地址模式、I2C从机地址
/*enable I2C0*/
i2c_enable(i2c_init->Memory_address);//I2C外设使能
/*enable acknowledge*/
i2c_ack_config(i2c_init->Memory_address,I2C_ACK_ENABLE);//选择是否发送应答信号
}
/*!
\brief I2C GPIO Clock enable
\param[in] I2Cx_PinSDA_Enum SDA pin
I2Cx_PinSCL_Enum SCL pin
\param[out] none
\retval none
*/
void I2Cx_Pin_ClockEnable(I2Cx_PinSDA_Enum gpio_sdapin,I2Cx_PinSCL_Enum gpio_sclpin)
{
uint32_t gpiox_sda,gpiox_scl;
gpiox_sda=(uint32_t)(gpio_sdapin & 0xffffff00);
gpiox_scl=(uint32_t)(gpio_sclpin & 0xffffff00);
/*SDA enable*/
if(gpiox_sda==GPIOB){
rcu_periph_clock_enable(RCU_GPIOB);
}else if(gpiox_sda==GPIOA){
rcu_periph_clock_enable(RCU_GPIOC);
}else if(gpiox_sda==GPIOF){
rcu_periph_clock_enable(RCU_GPIOF);
}
/*SCL enable*/
if(gpiox_scl==GPIOB){
rcu_periph_clock_enable(RCU_GPIOB);
}else if(gpiox_scl==GPIOA){
rcu_periph_clock_enable(RCU_GPIOA);
}
}
/*!
\brief I2C DMA send data
\param[in] Param_Typedef *i2c_init //I2C Param
SendBuffer //send array
DataNum send //data num
\param[out] none
\retval none
*/
void Master_DMA_SendData(uint8_t* SendBuffer,uint8_t DataNum,Param_Typedef *i2c_init)
{
/*wait until I2C bus is idle*/
while(i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_I2CBSY));
/*send a start condition to I2C bus*/
i2c_start_on_bus(i2c_init->Memory_address);//在I2C总线上产生一个开始信号
/*wait until SBSEND bit is set judge start whether send*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_SBSEND));
if(i2c_init->address==Send_7Address){
Send_7Prepare(i2c_init);
}
else if(i2c_init->address==Send_10Address){
Send_10Prepare(i2c_init);
}
/*wait until the transmit data buffer is empty*/
while(SET != i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_TBE));//等待TBE寄存器置1
/*deinitialize DMA0 channe7*/
i2c_dma_enable(i2c_init->Memory_address,I2C_DMA_ON);
while(!dma_flag_get(DMACH_Tx, DMA_FLAG_FTF));//等待dma数据传输完成
/*wait until BTC bit is set*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_BTC));//等待字节发送位结束
/*send a stop condition to I2C bus*/
i2c_stop_on_bus(i2c_init->Memory_address);//在总线上发送停止信号位
/*wait until the stop condition is finished*/
while(I2C_CTL0(i2c_init->Memory_address)&0x0200);//等待结束信号发送
//printf("I2C DMA send data success");
}
/*!
\brief I2C DMA receive data
\param[in] Param_Typedef *i2c_init // I2C Param
SendBuffer // send array
DataNum send //data num
\param[out] none
\retval none
*/
void Master_DMA_ReceiveData(uint8_t* ReceiveBuffer,uint8_t DataNum,Param_Typedef *i2c_init)
{
uint32_t adrH_10,adrL_10;
if(DataNum>=2){
i2c_dma_last_transfer_config(i2c_init->Memory_address,I2C_DMALST_ON);
}else if(DataNum==1){
i2c_ack_config(i2c_init->Memory_address,I2C_ACK_DISABLE);
}
/*wait until I2C bus is idle*/
while(i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_I2CBSY));
/*send a start condition to I2C bus*/
i2c_start_on_bus(i2c_init->Memory_address);//在I2C总线上产生一个开始信号
/*wait until SBSEND bit is set judge start whether send*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_SBSEND));
if(i2c_init->address==Send_7Address){
/*send slave address to I2C bus */
i2c_master_addressing(i2c_init->Memory_address, i2c_init->address,I2C_RECEIVER);//发送从机地址+写、清除SBSEND位、I2C设备发送方
/*wait until ADDSEND bit is set*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND)){
}
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
}
else if(i2c_init->address==Send_10Address){
adrH_10=(((i2c_init->address & 0x300)>>7)|0xf0);//10位地址头(高2位)
adrL_10=i2c_init->address & 0xff;//10位地址(低8位)
/*send slave address to I2C bus */
i2c_master_addressing(i2c_init->Memory_address,(uint8_t)adrH_10, I2C_TRANSMITTER);//发送从机10地址头+写、清除SBSEND位、I2C设备发送方
/*wait until ADDSEND bit is set*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADD10SEND));
i2c_data_transmit(i2c_init->Memory_address,adrL_10);
/*wait until ADDSEND bit is set*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND));
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
/*send a start condition to I2C bus*/
i2c_start_on_bus(i2c_init->Memory_address);//在I2C总线上产生一个开始信号
/*wait until SBSEND bit is set judge start whether send*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_SBSEND));
i2c_master_addressing(i2c_init->Memory_address,(uint8_t)adrH_10,I2C_RECEIVER);
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND));//等待ADDSEND位置1 硬件置1
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
}
i2c_dma_last_transfer_config(i2c_init->Memory_address,I2C_DMALST_ON);//next DMA EOT is the last transfer
i2c_dma_enable(i2c_init->Memory_address,I2C_DMA_ON);
while(!dma_flag_get(DMACH_Rx,DMA_FLAG_FTF));//等待dma数据传输完成
i2c_stop_on_bus(i2c_init->Memory_address);
/*wait until the stop condition is finished*/
while(I2C_CTL0(i2c_init->Memory_address)&0x0200);//判断stop结束位已经发送
/*enable acknowledge*/
i2c_ack_config(i2c_init->Memory_address,I2C_ACK_ENABLE);//软件发送应答信号使能
i2c_ackpos_config(i2c_init->Memory_address,I2C_ACKPOS_CURRENT);//ACKEN位决定对当前正在接收的字节是否发送ACK/NACK
}
/*!
\brief Master send 7 address prepare
\param[in] none
\param[out] none
\retval none
*/
void Send_7Prepare(Param_Typedef *i2c_init)
{
/*send slave address to I2C bus*/
i2c_master_addressing(i2c_init->Memory_address,i2c_init->address,I2C_TRANSMITTER);//发送从机地址+写、清除SBSEND位、I2C设备发送方
/*wait until ADDSEND bit is set*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND)){
}
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
}
/*!
\brief Master send 10 address prepare
\param[in] none
\param[out] none
\retval none
*/
void Send_10Prepare(Param_Typedef *i2c_init)
{
uint32_t adrH10,adrL10;
adrH10=(((i2c_init->address & 0x300)>>7)|0xf0);//10位地址头(高2位)
adrL10=i2c_init->address & 0xff;//10位地址(低8位)
/*send slave address adr10 to I2C bus */
i2c_master_addressing(i2c_init->Memory_address,(uint8_t)adrH10, I2C_TRANSMITTER);
/* wait until ADDSEND bit is set */
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADD10SEND));
//i2c_master_addressing(i2c_init->Memory_address,(uint8_t)adrL10, I2C_TRANSMITTER);
i2c_data_transmit(i2c_init->Memory_address,adrL10);
/*wait until ADDSEND bit is set*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND));
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
}
/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
usart_data_transmit(EVAL_COM, (uint8_t)ch);
while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE));
return ch;
}
/*!
\brief this function handles SysTick exception
\param[in] none
\param[out] none
\retval none
*/
void SysTick_Handler(void)
{
delay_decrement();
}
DMA传输可以提高cpu传输效率,由DMA负责存储器和外设之间数据的搬运,注意这里的存储器指的是mcu内部自带的sram,外设指I2C DATA寄存器。当I2C发送时dma传输方向sram-data(存储器到外设),接收时为data-sram(外设到存储器),dma只是一个搬运工,也可以理解为mcu的一个助手小弟,帮mcu处理一些数据搬运的杂活,所以dma也叫直接存储器访问控制器。
1.2 从机DMA收发
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
uint8_t num=0;
//systick_config();
for(num=0;num<BufferNum;num++){
write_buffer[num]=num;
}
#ifdef slaver_DMA_send
/*--------DMA send measuring-----------------*/
Param_Init(&I2Cx_Param,Para_Aarry,I2C_Measuring);
I2Cx_Init(&I2Cx_Param);
I2C_DMA_init();
Slaver_DMA_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);
#elif defined slaver_DMA_receive
/*--------DMA receive measuring-----------------*/
Param_Init(&I2Cx_Param,Para_Aarry,I2C_Measuring);
I2Cx_Init(&I2Cx_Param);
I2C_DMA_init();
Slaver_DMA_SendData(write_buffer,BufferNum,&I2Cx_Param);
#endif
while(1){
}
}
/*!
\brief I2C DMA send data
\param[in] Param_Typedef *i2c_init //I2C Param
SendBuffer //send array
DataNum send //data num
\param[out] none
\retval none
*/
void Slave_DMA_SendData(uint8_t* SendBuffer,uint8_t DataNum,Param_Typedef *i2c_init)
{
/*waiting I2C_FLAG_ADDSEND is set 1*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND));
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
if(i2c_init->address_model==I2Cx_Address_10bit){
/*wait until ADDSEND bit is set*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND));
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
}
//delay_1ms(1);
/*wait until the transmit data buffer is empty*/
while(SET != i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_TBE));//等待TBE寄存器置1
/*deinitialize DMA0 channe7*/
i2c_dma_enable(i2c_init->Memory_address,I2C_DMA_ON);
while(!dma_flag_get(DMACH_Tx, DMA_FLAG_FTF));//等待dma数据传输完成
/*wait until the transmit data buffer is empty */
while(SET != i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_AERR));
//clear I2C AERR flag
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_AERR);
//printf("I2C DMA send data success");
}
/*!
\brief I2C DMA receive data
\param[in] Param_Typedef *i2c_init // I2C Param
SendBuffer // send array
DataNum send //data num
\param[out] none
\retval none
*/
void Slave_DMA_ReceiveData(uint8_t* ReceiveBuffer,uint8_t DataNum,Param_Typedef *i2c_init)
{
/*waiting I2C_FLAG_ADDSEND is set 1*/
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_ADDSEND));
/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/
i2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_ADDSEND);//清除ADDSEND位
i2c_dma_enable(i2c_init->Memory_address,I2C_DMA_ON);
while(!dma_flag_get(DMACH_Rx, DMA_FLAG_FTF));//等待dma数据传输完成
while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_STPDET));
/*clear STPDET flag*/
I2C_STAT0(i2c_init->Memory_address);
I2C_CTL0(i2c_init->Memory_address)=0x0000;
}
主机dma使能位的打开在接收到从机的地址响应ack信号并清除ADDSEND标志位后,从机dma使能开关打开在接收到总线上主机发送的地址与自身地址匹配时,并清除ADDSEND标志位之后。
1.3 主从机数据发送PEC错误校验
当对数据传输的准确率要求比较高时,就需要用到CRC错误校验。pec发送:主从每发送一个byte数据时,其CRC寄存器便计算一次CRC校验值,当最后一个数据发送完时,硬件自动发送计算的CRC值。pec接收方:每接收一个字节,也计算一次CRC值,当接收到发送方发送的CRC值与自身计算的值相等时,则接收数据正确,反之则会产生PEC校验错误。如果我们需要测试硬件I2C PEC校验错误情况,可以使发送方关闭crc校验,接收方打开crc校验,在发送方发完最后一个字节数据时,随便再发送一个数据,接收方则会把最后一个字节数据作为crc计算值与自身计算的crc值比较,然后查看对应的pec错误校验位是否置位。
//pec错误校验使能
i2c_pec_enable(I2C_x,I2C_PEC_ENABLE);
//发送pec校验值
i2c_pec_transfer_enable(I2C_x,I2C_PECTRANS_ENABLE);
1.4 I2C多主仲裁
我们都知道I2C总线可以同时挂载多个从设备(一主多从),那在某一时刻总线上同时有两个主机对同一个从机地址发起访问时,那如何规定哪一个主机赢得竞争权呢?I2C总线空闲时默认电平为高电平,同时SDA数据线和SCL时钟线都具有线与关系,则低电平优先原则。两个主机同时发送START信号、从机地址后,到发送数据阶段如果两者总线上发送的都为高电平,则继续传送数据,如果一个为高,一个为低,则发送低电平的I2C主机赢得总线竞争权,仲裁失败的主机则恢复从机模式。I2C模块每发送一个bit位时,都会判断总线上的电平与自己实际发送的是否一致,若不一致,则产生仲裁错误。
使用硬件I2C多主仲裁功能时,应注意在PCB板上两个主机对从设备的SCL线和SDA线要尽可能的等长,保证信号同时到达。