1.1 硬件I2C介绍
在项目中遇见与MCU通信接口协议为I2C的设备时,可通过GPIO口软件模拟I2C和使用MCU自身的硬件I2C资源。两者各有优势,软件模拟可以不受GPIO口限制,也可以主控制器MCU的选择上降低成本,但也存在I2C设备对通信速率有特定的要求或者需要较高的传输速率时,会存在时序调试困难、start/stop/ack/noack/read/write等都需要软件实现、GPIO口翻转速率限制等原因导致软件模拟I2C通信速率有一定的限制。硬件I2C则不存在上述问题,I2C通信速率可配置100k/400k/1M(NXP芯片做到了3.4M),通信时序中的绝大部分都由硬件完成,我们只需要按照逻辑配置好相应的寄存器即可,但也存在受特定GPIO口限制、相应成本高等缺点。本文主要是基GD32MCU介绍硬件I2C的配置和注意的事项,关于I2C协议概念介绍只是简要提及。
1.2 GD32 mcu硬件I2C的主要特性
1、同一接口既可实现主机功能又可实现从机功能;
2、主从机之间的双向数据传输;
3、支持7位和10位的地址模式和广播寻址;
4、支持I2C多主机模式;
5、速率100k\400k\1M;
6、从机模式下可配置SCL主动拉低;
7、支持DMA模式;
8、支持SMBUS;
9、事件中断和错误中断;
10、支持PEC错误校验。
1.3 主机发送和接收实现(速率100k/400k/1M 地址10bit or 7bit)

 //.h文件
#ifndef MAIN_H
#define MAIN_H
#include "gd32f3x0.h"
#define I2Cx_Pinx_AFx_BIT(gpiox,pinx,afx) (((uint32_t)gpiox)|((uint32_t)(pinx)<<4)|(uint32_t)(afx))
/*send_slaver address*/
typedef enum
{Send_7Address=0x42,Send_7Address_2=0x44, send_gual=0x00,Send_10Address=0x232
}Send_Address_Enum;
/*define pin speed enum*/
typedef enum
{standard_speed=100000,High_speed=400000,More_HighSpeed=1000000
}I2Cx_Speed_enum;/*define I2Cx SDA Pin enum*/
typedef enum
{//define I2C0I2C0_SDA_GPIOx1=I2Cx_Pinx_AFx_BIT(GPIOA,10,4),I2C0_SDA_GPIOx2=I2Cx_Pinx_AFx_BIT(GPIOB,7,1), I2C0_SDA_GPIOx3=I2Cx_Pinx_AFx_BIT(GPIOB,9,1),  //define I2C1I2C1_SDA_GPIOx4=I2Cx_Pinx_AFx_BIT(GPIOA,1,4),I2C1_SDA_GPIOx5=I2Cx_Pinx_AFx_BIT(GPIOB,11,1),I2C1_SDA_GPIOx6=I2Cx_Pinx_AFx_BIT(GPIOF,7,0)
}I2Cx_PinSDA_Enum;/*define I2Cx SCL Pin enum*/
typedef enum
{//define I2C0  I2C0_SCL_GPIOy1=I2Cx_Pinx_AFx_BIT(GPIOA,9,4),I2C0_SCL_GPIOy2=I2Cx_Pinx_AFx_BIT(GPIOB,6,1),I2C1_SCL_GPIOy3=I2Cx_Pinx_AFx_BIT(GPIOB,8,1),//define I2C1I2C1_SCL_GPIOy4=I2Cx_Pinx_AFx_BIT(GPIOA,0,4),I2C1_SCL_GPIOy5=I2Cx_Pinx_AFx_BIT(GPIOB,10,1),I2C1_SCL_GPIOy6=I2Cx_Pinx_AFx_BIT(GPIOB,6,0)
}I2Cx_PinSCL_Enum;
/*define I2Cx address model*/
typedef enum
{I2Cx_Address_7bit=I2C_ADDFORMAT_7BITS,I2Cx_Address_10bit=I2C_ADDFORMAT_10BITS
}Address_Model_Enum;
/*define I2Cx slaver address*/
typedef enum
{Slaver_7Address=0x40,              Slaver_10Address=0x230
}Slaver_Address_Enum;
/*define I2CX init param*/
typedef struct
{uint32_t Memory_address;I2Cx_PinSDA_Enum gpio_sdapin;I2Cx_PinSCL_Enum gpio_sclpin;rcu_periph_enum I2Cx_Periph;I2Cx_Speed_enum I2c_speed;Address_Model_Enum address_model;Send_Address_Enum address;Slaver_Address_Enum slaver_address;
}Param_Typedef;
/*I2C gpio init function*/
void I2Cx_Init(Param_Typedef* i2c_init);
/*I2C gpiopin enable*/
void I2Cx_Pin_ClockEnable(I2Cx_PinSDA_Enum gpio_sdapin,I2Cx_PinSCL_Enum gpio_sclpin);
/*I2C master send buffer 7 and 10 address*/
void Master_SendData_address(uint8_t* SendBuffer,uint8_t DataNum,Param_Typedef *i2c_init);
/*I2C master receive data 7 and 10 address*/
void Master_ReceiveData(uint8_t* ReceiveBuffer,uint8_t DataNum,Param_Typedef *i2c_init);
/*I2C 7 bit address prepare send*/
void Send_7Prepare(Param_Typedef *i2c_init);
/*I2C 10 bit address prepare send*/
void Send_10Prepare(Param_Typedef *i2c_init);
/*I2C 10 bit address prepare receive*/
void Receive_10Prepare(Param_Typedef *i2c_init);
/*Master receive 7 address prepare*/
void Receive_7Prepare(Param_Typedef *i2c_init);#endif /* MAIN_H */
//.c文件
#include "gd32f3x0.h"
#include <stdio.h>
#include "gd32f350r_eval.h"
#include "main.h"
#include "systick.h"
#define I2C0_Measure
//#define I2C1_Measure
#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;/*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 I2C0_Measure/*Test Sequence I2C0 send*/Configure_I2CX_Send(I2C0_Seven_Address_standard);Configure_I2CX_Send(I2C0_Ten_Address_standard);Configure_I2CX_Send(I2C0_Seven_Address_high);Configure_I2CX_Send(I2C0_Ten_Address_high);Configure_I2CX_Send(I2C0_Seven_Address_more_high);/*Test Sequence I2C0 receive*/Configure_I2CX_Receive(I2C0_Seven_Address_standard);Configure_I2CX_Receive(I2C0_Ten_Address_standard);Configure_I2CX_Receive(I2C0_Seven_Address_high);Configure_I2CX_Receive(I2C0_Ten_Address_high);Configure_I2CX_Receive(I2C0_Seven_Address_more_high);#elif defined I2C1_Measure/*Test Sequence I2C1 send*/Configure_I2CX_Send(I2C1_Seven_Address_standard);Configure_I2CX_Send(I2C1_Ten_Address_standard);Configure_I2CX_Send(I2C1_Seven_Address_high);Configure_I2CX_Send(I2C1_Ten_Address_high);Configure_I2CX_Send(I2C1_Seven_Address_more_high);/*Test Sequence I2C1 receive*/Configure_I2CX_Receive(I2C1_Seven_Address_standard);Configure_I2CX_Receive(I2C1_Ten_Address_standard);Configure_I2CX_Receive(I2C1_Seven_Address_high);Configure_I2CX_Receive(I2C1_Ten_Address_high);Configure_I2CX_Receive(I2C1_Seven_Address_more_high);#endifwhile(1){}
}/*!\brief      Configure Param of I2Cx I2Cx Send data\param[in]  Test_Sequence_Enum measure_sequence //test sequence\param[out] none\retval     none
*/
void Configure_I2CX_Send(Test_Sequence_Enum measure_sequence)
{   switch(measure_sequence){case I2C0_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_standard);I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0 Seven Address standard send success\n");break;case I2C0_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_standard send success\n");       break;case I2C0_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_high);  I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Seven_Address_high send success\n");      break;case I2C0_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_high);  I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_high send success\n");break;case I2C0_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_more_high);    I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Seven_Address_more_high send success\n");break;case I2C1_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_standard);   I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1 Seven Address standard send success\n");break;case I2C1_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_standard send success\n");    break;case I2C1_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_high);  I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_high send success\n");break;case I2C1_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_high);    I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_high send success\n");break;case I2C1_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_more_high);    I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_more_high send success\n");break;default:break;}
}/*!\brief      Configure Param of receive data\param[in]  Test_Sequence_Enum measure_sequence //test sequence\param[out] none\retval     none
*/
void Configure_I2CX_Receive(Test_Sequence_Enum measure_sequence)
{switch(measure_sequence){case I2C0_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_standard);I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Seven_Address_standard receive data success!!\n");break;case I2C0_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);   printf("I2C0_Ten_Address_standard receive data success!!\n");break;case I2C0_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_high);   I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);    printf("I2C0_Seven_Address_high receive data success!!\n");break;case I2C0_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_high); I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_high receive data success!!\n");break;case I2C0_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_more_high); I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_high receive data success!!\n");break;case I2C1_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_standard);   I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_standard receive data success!!\n");break;case I2C1_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_standard receive data success!!\n");break;case I2C1_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_high);    I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_high receive data success!!\n");break;case I2C1_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_high); I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_high receive data success!!\n");break;case I2C1_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_more_high); I2Cx_Init(&I2Cx_Param);delay_1ms(3000);//delay 3s wait slaver init success!Master_ReceiveData(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_high receive data success!!\n");break;default:break;}
}/*!\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 pinI2Cx_PinSCL_Enum SCL pinrcu_periph_enum  I2C periphI2Cx_Speed_enum check clock frequenceAddress_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 pinI2Cx_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      Master send data 7 and 10 address\param[in]  SendBuffer send arrayDataNum send data num\param[out] none\retval     none
*/
void Master_SendData_address(uint8_t* SendBuffer,uint8_t DataNum,Param_Typedef *i2c_init)
{uint8_t num;/*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)){}//I2C_STAT0(i2c_init->Memory_address);//read I2C_STAT0 to clear ADDSENDif(i2c_init->address_model==I2Cx_Address_7bit){Send_7Prepare(i2c_init);}else if(i2c_init->address_model==I2Cx_Address_10bit){Send_10Prepare(i2c_init);}/*wait until the transmit data buffer is empty */while(SET != i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_TBE)){}for(num=0;num<DataNum;num++){i2c_data_transmit(i2c_init->Memory_address,SendBuffer[num]); 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("master send data success");
}/*!\brief      Master receive data\param[in]  ReceiveBuffer receive data arrayDataNum receive data numaddress  slaver address\param[out] none\retval     none
*/
void Master_ReceiveData(uint8_t* ReceiveBuffer,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){Receive_7Prepare(i2c_init);}else if(i2c_init->address==Send_10Address){Receive_10Prepare(i2c_init);}if(DataNum==1){/*disable acknowledge*/i2c_ack_config(i2c_init->Memory_address,I2C_ACK_DISABLE);//选择是否发送应答信号/*send a stop condition to I2C bus*/i2c_stop_on_bus(i2c_init->Memory_address);//在总线上发送停止信号位/*I2C_DATA is not empty*/while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_RBNE));*ReceiveBuffer=i2c_data_receive(i2c_init->Memory_address);//read data from I2C_DATA}while(DataNum){if(DataNum==2){while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_RBNE));*ReceiveBuffer=i2c_data_receive(i2c_init->Memory_address);//read data from I2C_DATADataNum-=1;ReceiveBuffer++; /*disable acknowledge*/i2c_ack_config(i2c_init->Memory_address,I2C_ACK_DISABLE);//选择是否发送应答信号/*send a stop condition to I2C bus*/i2c_stop_on_bus(i2c_init->Memory_address);//在总线上发送停止信号位 }/*I2C_DATA is not empty*/while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_RBNE));*ReceiveBuffer=i2c_data_receive(i2c_init->Memory_address);//read data from I2C_DATADataNum-=1;ReceiveBuffer++;        }       /*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;PECTRANS位表明PEC 是否处于移位寄存器中//printf("master receive data success");
}/*!\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 receive 7 address prepare\param[in]  none\param[out] none\retval     none
*/
void Receive_7Prepare(Param_Typedef *i2c_init)
{/*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位
}/*!\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_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位
}/*!\brief      Master receive 10 address prepare\param[in]  none\param[out] none\retval     none
*/
void Receive_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 to I2C bus*/i2c_master_addressing(i2c_init->Memory_address,(uint8_t)adrH10, 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,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位/*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)adrH10,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位
}/* 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();
}

上述程序实现了主机发送和接收,速率100k/400k/1M,发送从机地址10bit、7bit,程序方便移植,只需将对应的.c和.h添加到工程中即可。当I2C的通信速率为1M时,需配置I2C模块所挂载的pclk时钟为3的整数倍或者25的整数倍,否则主机的SCL实际的时钟会有一定的误差,大于或者小于1M,大于1M可能导致通信失败(GD mcu官方手册有提到)。I2C总线空闲时,默认为高电平,因为GPIO口内部自带的上拉驱动能力不够,配置为开漏输出,需外接上拉电阻,GPIO的输出速率要选择最大(代码中选择的是50M)增加GPIO口输出能力,否则通过示波器查看发现SCL时钟波形不是正规的波形,也可能造成通信失败。不同GPIO口输出速率下,SCL时钟输出波形如下图所示。

1.4 从机发送和接收(速率100k\400k\1M,地址10bit or 7bit)
I2C的从机的通信时钟由主机提供,故不需要配置时钟,I2C初始化后默认为从机模式,发送start信号后,由从机模式转换为主机模式。

#include "gd32f3x0.h"
#include <stdio.h>
#include "gd32f350r_eval.h"
#include "main.h"
#include "systick.h"
#define I2C0_Measure
//#define I2C1_Measure
#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;/*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;for(num=0;num<BufferNum;num++){write_buffer[num]=num;}//printf("I2C slaver measuring\n");#ifdef I2C0_Measure/*Test Sequence I2C0 receive*/Configure_I2CX_Receive(I2C0_Seven_Address_standard);Configure_I2CX_Receive(I2C0_Ten_Address_standard);Configure_I2CX_Receive(I2C0_Seven_Address_high);Configure_I2CX_Receive(I2C0_Ten_Address_high);Configure_I2CX_Receive(I2C0_Seven_Address_more_high);/*Test Sequence I2C0 send*/Configure_I2CX_Send(I2C0_Seven_Address_standard);Configure_I2CX_Send(I2C0_Ten_Address_standard);Configure_I2CX_Send(I2C0_Seven_Address_high);Configure_I2CX_Send(I2C0_Ten_Address_high);Configure_I2CX_Send(I2C0_Seven_Address_more_high);#elif defined I2C1_Measure/*Test Sequence I2C1 receive*/Configure_I2CX_Receive(I2C1_Seven_Address_standard);Configure_I2CX_Receive(I2C1_Ten_Address_standard);Configure_I2CX_Receive(I2C1_Seven_Address_high);Configure_I2CX_Receive(I2C1_Ten_Address_high);Configure_I2CX_Receive(I2C1_Seven_Address_more_high);/*Test Sequence I2C1 send*/Configure_I2CX_Send(I2C1_Seven_Address_standard);Configure_I2CX_Send(I2C1_Ten_Address_standard);Configure_I2CX_Send(I2C1_Seven_Address_high);Configure_I2CX_Send(I2C1_Ten_Address_high);Configure_I2CX_Send(I2C1_Seven_Address_more_high);#endifwhile(1){}
}/*!\brief      Configure Param of I2Cx I2Cx Send data\param[in]  Test_Sequence_Enum measure_sequence //test sequence\param[out] none\retval     none
*/
void Configure_I2CX_Send(Test_Sequence_Enum measure_sequence)
{   switch(measure_sequence){case I2C0_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_standard);I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0 Seven Address standard send success\n");break;case I2C0_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_standard send success\n");       break;case I2C0_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_high);  I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Seven_Address_high send success\n");      break;case I2C0_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_high);  I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_high send success\n");break;case I2C0_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_more_high);    I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Seven_Address_more_high send success\n");break;case I2C1_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_standard);   I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1 Seven Address standard send success\n");break;case I2C1_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_standard send success\n");    break;case I2C1_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_high);  I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_high send success\n");break;case I2C1_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_high);    I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_high send success\n");break;case I2C1_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_more_high);    I2Cx_Init(&I2Cx_Param);Slaver_SendData_address(write_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_more_high send success\n");break;default:break;}
}/*!\brief      Configure Param of receive data\param[in]  Test_Sequence_Enum measure_sequence //test sequence\param[out] none\retval     none
*/
void Configure_I2CX_Receive(Test_Sequence_Enum measure_sequence)
{switch(measure_sequence){case I2C0_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_standard);I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Seven_Address_standard receive data success!!\n");break;case I2C0_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);   printf("I2C0_Ten_Address_standard receive data success!!\n");break;case I2C0_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_high);   I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);    printf("I2C0_Seven_Address_high receive data success!!\n");break;case I2C0_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Ten_Address_high); I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_high receive data success!!\n");break;case I2C0_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C0_Seven_Address_more_high); I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C0_Ten_Address_high receive data success!!\n");break;case I2C1_Seven_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_standard);   I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_standard receive data success!!\n");break;case I2C1_Ten_Address_standard:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_standard);I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_standard receive data success!!\n");break;case I2C1_Seven_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_high);    I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Seven_Address_high receive data success!!\n");break;case I2C1_Ten_Address_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Ten_Address_high); I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_high receive data success!!\n");break;case I2C1_Seven_Address_more_high:Param_Init(&I2Cx_Param,Para_Aarry,I2C1_Seven_Address_more_high); I2Cx_Init(&I2Cx_Param);Slaver_ReceiveData_address(receive_buffer,BufferNum,&I2Cx_Param);printf("I2C1_Ten_Address_high receive data success!!\n");break;default:break;}
}/*!\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 pinI2Cx_PinSCL_Enum SCL pinrcu_periph_enum  I2C periphI2Cx_Speed_enum check clock frequenceAddress_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口、备用功能、端口上拉模式、具体引脚gpio_output_options_set(gpiox_scl, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,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 pinI2Cx_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      Slaver receive data\param[in]  uint8_t* SendBuffer //receive bufferuint8_t DataNum //receive data numParam_Typedef *i2c_init //define I2CX init param\param[out] none\retval     none
*/
void Slaver_ReceiveData_address(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位   while(DataNum){/*I2C_DATA is not empty*/while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_RBNE));*ReceiveBuffer=i2c_data_receive(i2c_init->Memory_address);//read data from I2C_DATAReceiveBuffer+=1;DataNum-=1;}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;//printf("slaver receive data success!!\n");
}/*!\brief      I2C slaver send buffer 7 and 10 address\param[in]  uint8_t* SendBuffer  //receive bufferuint8_t DataNum      //receive data numParam_Typedef *i2c_init //define I2CX init param\param[out] none\retval     none
*/
void Slaver_SendData_address(uint8_t* SendBuffer,uint8_t DataNum,Param_Typedef *i2c_init)
{uint8_t num;/*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位   }/*wait until the transmit data buffer is empty*/while(SET != i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_TBE));//send one byte to I2C BUSi2c_data_transmit(i2c_init->Memory_address,*SendBuffer);SendBuffer+=1;while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_BTC));//send two byte to I2C BUSi2c_data_transmit(i2c_init->Memory_address,*SendBuffer);SendBuffer+=1;for(num=0;num<(DataNum-2);num++){/*I2C_DATA is not empty*/while(!i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_BTC));i2c_data_transmit(i2c_init->Memory_address,*SendBuffer);SendBuffer+=1;}/*wait until the transmit data buffer is empty*/while(SET != i2c_flag_get(i2c_init->Memory_address,I2C_FLAG_AERR));//clear I2C AERR flagi2c_flag_clear(i2c_init->Memory_address,I2C_FLAG_AERR);
}/* 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();
}

以上从机接收和发送代码和主机一一对应,两者可作为测试使用。I2C从机支持双地址、10bit地址和广播地址模式(0x00)。双地址模式:I2C从机设备可对两个地址响应。广播地址模式:所有I2C从机设备默认的响应地址。

//双地址模式i2c_dualaddr_enable(I2C0,0x44);
//广播地址i2c_slave_response_to_gcall_config(I2C0, I2C_GCEN_ENABLE);

1.5 主机中断接收发送程序

//主机中断发送
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;}printf("I2C master send by interrupt measuring!!!");Param_Init(&I2Cx_Param,Para_Aarry,I2C_Measuring);I2Cx_Init(&I2Cx_Param);nvic_irq_enable(I2Cx_EV_IRQn,0,0);/*!< I2C0 event interrupt priority and enable*/  nvic_irq_enable(I2Cx_ER_IRQn,0,1);/*!< I2C0 erro  interrupt priority and enable*/i2c_interrupt_enable(I2C_x,I2C_INT_ERR);//error interrupt enable i2c_interrupt_enable(I2C_x,I2C_INT_EV);//event interrupt enablei2c_interrupt_enable(I2C_x,I2C_INT_BUF);//buffer interrupt enabledelay_1ms(3000);/*wait until I2C bus is idle*/while(i2c_flag_get(I2C_x,I2C_FLAG_I2CBSY));/*send a start condition to I2C bus*/i2c_start_on_bus(I2C_x);//在I2C总线上产生一个开始信号while(!Send_InterruptFlag);/*wait until the stop condition is finished*/while(I2C_CTL0(I2C_x)&0x0200);//等待结束信号发送printf("I2C0 master interrupt send success!!");        while(1){}
}
/*!\brief      this function handles I2C0 Event\param[in]  none\param[out] none\retval     none
*/
void I2C0_EV_IRQHandler(void)
{if(i2c_flag_get(I2C_x,I2C_FLAG_SBSEND)){I2C_STAT0(I2C_x);/*send slave address to I2C bus */i2c_master_addressing(I2C_x,0x42,I2C_TRANSMITTER);//发送从机地址+写、清除SBSEND位、I2C设备发送方}else if(i2c_flag_get(I2C_x,I2C_FLAG_ADDSEND)){i2c_flag_clear(I2C_x,I2C_FLAG_ADDSEND);//清除ADDSEND位  }else if(i2c_flag_get(I2C_x,I2C_FLAG_TBE)){if(Data<20){i2c_data_transmit(I2C_x,write_buffer[Data]);Data+=1;}else{i2c_stop_on_bus(I2C_x);//在总线上发送停止信号位Send_InterruptFlag=1;    i2c_interrupt_disable(I2C_x,I2C_INT_ERR);//error interrupt enable i2c_interrupt_disable(I2C_x,I2C_INT_EV);//event interrupt enablei2c_interrupt_disable(I2C_x,I2C_INT_BUF);//buffer interrupt enable    }}
}/*!\brief      this function handles I2C0 Error\param[in]  none\param[out] none\retval     none
*/
void I2C0_ER_IRQHandler(void)
{//SMBus Alert statusif(i2c_flag_get(I2C_x,I2C_FLAG_SMBALT)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBALT);}//timeout signal in SMBus modeif(i2c_flag_get(I2C_x,I2C_FLAG_SMBTO)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBTO);}//PEC error when receiving dataif(i2c_flag_get(I2C_x,I2C_FLAG_PECERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_PECERR);}//over-run or under-run situation occurs in slave modeif(i2c_flag_get(I2C_x,I2C_FLAG_OUERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_OUERR);}//acknowledge errorif(i2c_flag_get(I2C_x,I2C_FLAG_AERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_AERR);}//arbitration lost in master modeif(i2c_flag_get(I2C_x,I2C_FLAG_LOSTARB)){ i2c_flag_clear(I2C_x,I2C_FLAG_LOSTARB);}//a bus error if(i2c_flag_get(I2C_x,I2C_FLAG_BERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_BERR);}
}
//主机接收中断
/*!\brief      this function handles I2C0 Event\param[in]  none\param[out] none\retval     none
*/
void I2C0_EV_IRQHandler(void)
{if(i2c_flag_get(I2C_x,I2C_FLAG_SBSEND)){I2C_STAT0(I2C_x);/*send slave address to I2C bus */i2c_master_addressing(I2C_x,0x42,I2C_RECEIVER);//发送从机地址+写、清除SBSEND位、I2C设备发送方}if(i2c_flag_get(I2C_x,I2C_FLAG_ADDSEND)){i2c_flag_clear(I2C_x,I2C_FLAG_ADDSEND);//清除ADDSEND位  }if(i2c_flag_get(I2C_x,I2C_FLAG_RBNE)){if(Data==18){receive_buffer[Data]=i2c_data_receive(I2C_x);Data+=1;/*disable acknowledge*/i2c_ack_config(I2C_x,I2C_ACK_DISABLE);//选择是否发送应答信号i2c_stop_on_bus(I2C_x);//在总线上发送停止信号位}else if(Data<18){receive_buffer[Data]=i2c_data_receive(I2C_x);Data+=1;}else if(Data==19){receive_buffer[Data]=i2c_data_receive(I2C_x);Receive_InterruptFlag=1;    i2c_interrupt_disable(I2C_x,I2C_INT_ERR);//error interrupt enable i2c_interrupt_disable(I2C_x,I2C_INT_EV);//event interrupt enablei2c_interrupt_disable(I2C_x,I2C_INT_BUF);//buffer interrupt enable                         }   }
}/*!\brief      this function handles I2C0 Error\param[in]  none\param[out] none\retval     none
*/
void I2C0_ER_IRQHandle(void)
{//SMBus Alert statusif(i2c_flag_get(I2C_x,I2C_FLAG_SMBALT)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBALT);}//timeout signal in SMBus modeif(i2c_flag_get(I2C_x,I2C_FLAG_SMBTO)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBTO);}//PEC error when receiving dataif(i2c_flag_get(I2C_x,I2C_FLAG_PECERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_PECERR);}//over-run or under-run situation occurs in slave modeif(i2c_flag_get(I2C_x,I2C_FLAG_OUERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_OUERR);}//acknowledge errorif(i2c_flag_get(I2C_x,I2C_FLAG_AERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_AERR);}//arbitration lost in master modeif(i2c_flag_get(I2C_x,I2C_FLAG_LOSTARB)){ i2c_flag_clear(I2C_x,I2C_FLAG_LOSTARB);}//a bus error if(i2c_flag_get(I2C_x,I2C_FLAG_BERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_BERR);}
}

如果项目程序有多个中断,尽可能将I2C主机接收的中断优先级设为最高,否则会存在主机多接收一个数据,原因:当主机接收一个数据时,RBNE置1,在未读取DATA寄存器中的数据时,被更高的优先级任务打断,I2C总线上又接收一个数据到移位寄存器,此时BTC标志位置1。当中断恢复时,主机读取DATA寄存器中的数据。因为此时BTC已经置1,BTC清除的条件是需先读state状态寄存器,再读DATA寄存器,此时读DATA寄存器,无法清除BTC,使移位寄存器中的数据无法移到数据寄存器,I2C总线SCL被拉低,已阻止接收下一个数据,此时I2C还认为接收两个数据,会再次进中断读取DATA寄存器,BTC清零,移位寄存器数据移到Data寄存器,再次读取下一个数据,会造成接收的数据中有两个前后重复的数据。
1.6 从机中断接收和发送

//从机中断接收
/*!\brief      this function handles I2C0 Event\param[in]  none\param[out] none\retval     none
*/
void I2C0_EV_IRQHandler(void)
{if(i2c_interrupt_flag_get(I2C_x,I2C_INT_FLAG_ADDSEND)){/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/i2c_flag_clear(I2C_x,I2C_FLAG_ADDSEND);//清除ADDSEND位  }else if(i2c_interrupt_flag_get(I2C_x,I2C_INT_FLAG_TBE)){   if(Data<20){i2c_data_transmit(I2C_x,writer_buffer[Data]);Data+=1;           }else{Send_InterruptFlag=1;i2c_interrupt_disable(I2C_x,I2C_INT_ERR);i2c_interrupt_disable(I2C_x,I2C_INT_EV);i2c_interrupt_disable(I2C_x,I2C_INT_BUF);}     }
}/*!\brief      this function handles I2C0 Error\param[in]  none\param[out] none\retval     none
*/
void I2C0_ER_IRQHandle(void)
{//SMBus Alert statusif(i2c_flag_get(I2C_x,I2C_FLAG_SMBALT)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBALT);}//timeout signal in SMBus modeif(i2c_flag_get(I2C_x,I2C_FLAG_SMBTO)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBTO);}//PEC error when receiving dataif(i2c_flag_get(I2C_x,I2C_FLAG_PECERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_PECERR);}//over-run or under-run situation occurs in slave modeif(i2c_flag_get(I2C_x,I2C_FLAG_OUERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_OUERR);}//acknowledge errorif(i2c_flag_get(I2C_x,I2C_FLAG_AERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_AERR);}//arbitration lost in master modeif(i2c_flag_get(I2C_x,I2C_FLAG_LOSTARB)){ i2c_flag_clear(I2C_x,I2C_FLAG_LOSTARB);}//a bus error if(i2c_flag_get(I2C_x,I2C_FLAG_BERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_BERR);}
}
//从机中断发送
/*!\brief      this function handles I2C0 Event\param[in]  none\param[out] none\retval     none
*/
void I2C0_EV_IRQHandler(void)
{if(i2c_interrupt_flag_get(I2C_x,I2C_INT_FLAG_ADDSEND)){/*read I2C_STAT0 and then read I2C_STAT1 to clear ADDSEND*/i2c_flag_clear(I2C_x,I2C_FLAG_ADDSEND);//清除ADDSEND位  }else if(i2c_interrupt_flag_get(I2C_x,I2C_INT_FLAG_TBE)){   if(Data<20){i2c_data_transmit(I2C_x,writer_buffer[Data]);Data+=1;           }else{Send_InterruptFlag=1;i2c_interrupt_disable(I2C_x,I2C_INT_ERR);i2c_interrupt_disable(I2C_x,I2C_INT_EV);i2c_interrupt_disable(I2C_x,I2C_INT_BUF);}     }
}/*!\brief      this function handles I2C0 Error\param[in]  none\param[out] none\retval     none
*/
void I2C0_ER_IRQHandle(void)
{//SMBus Alert statusif(i2c_flag_get(I2C_x,I2C_FLAG_SMBALT)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBALT);}//timeout signal in SMBus modeif(i2c_flag_get(I2C_x,I2C_FLAG_SMBTO)){ i2c_flag_clear(I2C_x,I2C_FLAG_SMBTO);}//PEC error when receiving dataif(i2c_flag_get(I2C_x,I2C_FLAG_PECERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_PECERR);}//over-run or under-run situation occurs in slave modeif(i2c_flag_get(I2C_x,I2C_FLAG_OUERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_OUERR);}//acknowledge errorif(i2c_flag_get(I2C_x,I2C_FLAG_AERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_AERR);}//arbitration lost in master modeif(i2c_flag_get(I2C_x,I2C_FLAG_LOSTARB)){ i2c_flag_clear(I2C_x,I2C_FLAG_LOSTARB);}//a bus error if(i2c_flag_get(I2C_x,I2C_FLAG_BERR)){ i2c_flag_clear(I2C_x,I2C_FLAG_BERR);}
}

主机发送每一个byte数据时,从机都需发送ACK信号,而从机发送数据时,当主机接收到最后一个字节的数据时,给从机发NACK信号,从机接收到NACK信号后,就知道主机要结束通信了。从机SCL拉低功能作用是:从机在接收数据时,由于需要处理其他事情,比如打印一些调试信息,可能没有及时读取DATA寄存器中的数据,这时从机会拉低SCL时钟线已阻止主机发送下一个字节数据,也就是再告诉主机“我正在忙啦!您稍等片刻”,I2C中对这个拉低时间没有具体要求,但是在SMBUS中,对SCL拉低的时间规定为25ms,如果25ms从机还在拉低SCL硬件就会强制发送stop信号结束此次通信(也就是说主机的忍耐是有限度的哈!!)。
这篇就先写这么多,I2C硬件的其他主要特性及代码实现在后面介绍。

GD32MCU硬件I2C学习(一)相关推荐

  1. STM32CubeMX学习笔记——STM32H743_硬件I2C

    STM32CubeMX学习笔记--STM32H743_硬件I2C Github STM32CubeMX配置 Pinout配置 GPIO Clock Configuration配置 代码部分 main. ...

  2. STM32HAL库学习笔记--硬件I2C读写AT24C512

    摘要:由于需要实现掉电存储功能,在无线传输模块上增加了一块EEPROM芯片,因为工程使用CubeMx配置并生成,为了方便不再使用IO口模拟I2C,而是使用f1自带的硬件I2C来实现.配置和调试过程和遇 ...

  3. STM32CubeMX学习教程之十:硬件I2C读写AT24C02

    完整源码下载: https://github.com/simonliu009/STM32CubeMX-hardware-I2C-AT24C02 网上有流传已久一种说法,就是STM的I2C有bug,不好 ...

  4. STM32F10x_硬件I2C读写EEPROM(标准外设库版本)

    Ⅰ.写在前面 上一篇文章是"STM32F10x_模拟I2C读写EEPROM",讲述使用IO口模拟I2C总线通信,对EEPROM(AT24Xxx)进行读写操作的过程. 上一篇文章主要 ...

  5. 关于 STM32 的硬件I2C

    首先转两个帖子 http://blog.csdn.net/mcu_hong/article/details/8149311 http://bbs.21ic.com/icview-184741-1-1. ...

  6. bme280 环境传感器开发板_STM32Cube14 | 使用硬件I2C读写环境光强度传感器

    更多精彩~点击上面蓝字关注我们呀!   寻求更好的阅读体验,请点击阅读原文移步:Mculover666的个人博客. 本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件I ...

  7. 1.27 Cubemx_STM32H7 软\硬件I2C

    Cubemx_STM32H7 软\硬件I2C 1. I2C简介 1.1 I2C物理拓扑结构 1.2 I2C总线特征 1.3 I2C协议 1.4 I2C总线操作 1.5原理图 2.硬件IIC Cubem ...

  8. 使用MPU6050在STM32F103C8T6中的硬件I2C数据传输

    文章以自学为主,主要用来保存学习记录,有问题大家可以互相讨论,一起进步 使用MPU6050在STM32F103C8T6中的硬件I2C数据传输 前言 一.使用的硬件平台 二.程序移植步骤 1.引入野火官 ...

  9. 基于阿里平头哥的单片机软、硬件i2C驱动oled

    基于RiskV的阿里平头哥MCU开箱文章之软硬件i2C驱动oled 12864 Risk-V简介 国产单片机及开发环境CDK iic简介 iIC代码移植详细介绍 # stm32 iic: # 向国产单 ...

  10. STM32F103硬件I2C

    I2C通信协议是比较常见且常用的一种串行同步半双工通信协议:在单片机开发中,很多独立的功能模块采用I2C通信协议,其通信原理及过程这里就不过多的介绍. 在学习I2C的过程中,通常都会根据其时序图,编写 ...

最新文章

  1. Linux 交换空间管理和技巧
  2. Emscripten 单词_初一(上)掌握这 4 大类发音规律,英语记单词很轻松
  3. Web移动端Fixed布局的解决方案
  4. PL/SQL学习笔记-过程
  5. C专家编程—分析signal函数的原型声明{void (*signal(int sig,void (*func)(int)))(int)}(2)
  6. Option,Some,None
  7. 修改oracle数据库内存参数,物理内存扩容,oracle 11g R1数据库相关参数修改
  8. 深入css布局 (3)完结 — margin问题与格式化上下文
  9. 深入测试CCD坏点和噪点
  10. 官方rom提取原签名工具_ROM修改工具:Dragonface+昂达OTA签名工具
  11. 研究生学习生活日记——slow down
  12. [Java教程 00] 计算机基础
  13. 【DDR3_Electrical Characteristics and AC Timing】_ Setup,Hold and Slew Rate Derating
  14. Failing OffsetCommit request since the consumer is not part of an active group
  15. 系统分析师-2021年下午简答题
  16. form表单提交既有文字也有图片的情况下,增加enctype属性
  17. AD域控的搭建与加入AD域
  18. HBaseAPI——IDEA操作HBase数据库HBase与Hive的集成
  19. 广州宇信易诚科技有限公司面试题
  20. 极光推送Cordova插件适配Capacitor

热门文章

  1. 烟台市建筑物矢量数据(Shp格式+带高度)
  2. R语言:网页抓取之get the data
  3. 通达信资金净流入公式_资金净流入公式——股票实战技术指标公式研究有缘看本博定多活30年——东方财富网博客...
  4. 机器人自动化《RPA应用场景和发展趋势》
  5. c语言编写打字母游戏代码,求一个用C语言编写的小游戏代码
  6. 【面试必会】java分布式事务面试题
  7. 《深入浅出数据分析》读后详解
  8. VB小程序:生成十个不重复的随机数
  9. 达思sql数据库修复软件:用友金蝶管家婆思迅中了勒索病毒怎么办?
  10. 用虚拟机在一台电脑上实现xPC实时仿真