介绍
很荣幸又能再次的参加技术社区的开发板试用活动,此次参加用的是GD32F427系列的芯片,该芯片相较于GDF310系列的资源可以说非常丰富了。本次试用的项目是试用板子上面的iic外设读取传感器的温度。
准备工作
拿到板子以后,LED闪烁正常,说明板子正常。
由于以前用的MDK开发环境,本次想要改用IAR的环境,此处需要添加支持包,首先要把这个后缀名改为exe
另外,必须以管理员身份运行,能正确识别到本机IAR的安装路径才算成功,否则安装是不成功的。
另外这个板子配置串口也有些小波折,这里参考了这位的教程:https://aijishu.com/a/1060000000367387
之后实现了printf的输出。
硬件连接
如图所示,iic的通信只需要SDA和SCL两根线。
IIC通信过程由开始、结束、发送、响应、接收五个部分构成。
1、(在发送、接收数据的时候)当SCL为高电平时,SDA线不允许变化;当SCL线为低电平时,SDA线可以任意0、1变化。
2、(在任意时候)只有当SCL为高电平时,IIC电路才对SDA线上的电平(0或者1)进行记录,当SCL线为低电平时,无论SDA是高还是低,IIC电路都不对SDA进行采样。
空闲状态:SDA和SCL都为高
开始和停止:
开始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平。
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号
应答:
发送器每发送一个字节,就在时钟脉冲9期间释放数据先,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间位稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P
软件实现
通过参考demo,我们可以很快的试用iic,我们使用PB8 和PB9来做scl和sdl
iic配置的代码如下
时钟配置
/* enable GPIOB clock */
rcu_periph_clock_enable(RCU_GPIOB);
/* enable I2C1 clock */
rcu_periph_clock_enable(RCU_I2C1);
GPIO配置
/* I2C0 and I2C1 GPIO ports */
/* connect PB8 to I2C0_SCL */
gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_8);
/* connect PB9 to I2C0_SDA */
gpio_af_set(GPIOB, GPIO_AF_4, GPIO_PIN_9);
iic 配置
/* configure GPIO pins of I2C0 */
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_8);
gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
/* configure I2C clock */
i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
/* configure I2C address */
i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, I2C0_SLAVE_ADDRESS7);
/* enable I2C0 */
i2c_enable(I2C0);
/* enable acknowledge */
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
上述配置好了以后,我们就能正常使用i2c了,我们需要对原始api进行一些简单的封装。
void I2C_LeaderWrite(uint16_t followerAddress, , uint8_t targetAddress, uint8_t *txBuff,
uint8_t numBytes) {
/* wait until I2C bus is idle */
while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY))
;
/* send a start condition to I2C bus */
i2c_start_on_bus(I2C0);
/* wait until SBSEND bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_SBSEND))
;
/* send slave address to I2C bus */
i2c_master_addressing(I2C0, followerAddress, I2C_TRANSMITTER);
/* wait until ADDSEND bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND))
;
/* clear ADDSEND bit */
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
/* wait until the transmit data buffer is empty */
while (!i2c_flag_get(I2C0, I2C_FLAG_TBE))
;
for (i = 0; i < numBytes; i++) {
/* data transmission */
i2c_data_transmit(I2C0, txBuff[i]);
/* wait until the TBE bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_TBE))
;
}
/* send a stop condition to I2C bus */
i2c_stop_on_bus(I2C0);
/* wait until stop condition generate */
while (I2C_CTL0(I2C0) & 0x0200)
;
}
void I2C_LeaderRead(uint16_t followerAddress, uint8_t targetAddress, uint8_t *rxBuff,
uint8_t numBytes) {
/* wait until I2C bus is idle */
while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY))
;
/* send a start condition to I2C bus */
i2c_start_on_bus(I2C0);
/* wait until SBSEND bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_SBSEND))
;
/* send slave address to I2C bus */
i2c_master_addressing(I2C0, followerAddress, I2C_TRANSMITTER);
/* wait until ADDSEND bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND))
;
/* clear the ADDSEND bit */
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
/* wait until the transmit data buffer is empty */
while (SET != i2c_flag_get(I2C0, I2C_FLAG_TBE))
;
/* enable I2C0*/
i2c_enable(I2C0);
/* send the EEPROM's internal address to write to */
i2c_data_transmit(I2C0, targetAddress);
/* wait until BTC bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_BTC))
;
/* send a start condition to I2C bus */
i2c_start_on_bus(I2C0);
/* wait until SBSEND bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_SBSEND))
;
/* send slave address to I2C bus */
i2c_master_addressing(I2C0, followerAddress, I2C_RECEIVER);
/* wait until ADDSEND bit is set */
while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND))
;
/* clear the ADDSEND bit */
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
/* while there is data to be read */
for (int i = 0; i < numBytes; i++) {
/* code */
/* read a data from I2C_DATA */
rxBuff[i++] = i2c_data_receive(I2C0);
/* send a stop condition */
i2c_stop_on_bus(I2C0);
}
/* wait until the stop condition is finished */
while (I2C_CTL0(I2C0) & 0x0200)
;
/* enable acknowledge */
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
i2c_ackpos_config(I2C0, I2C_ACKPOS_CURRENT);
}
如上,我们的MCU就能作为主机来发送和接受了
查阅sht40的手册可以得到读取的命令以及转换关系
主函数代码如下
int main(void)
{
my_iic_init():
init_sensor_sht40();
while(1) {
delay_1ms(1000);
get_sensor_sht40(&temp,&hum);
printf ("get T:%d, H: %d",temp,hum);
}
}
结果定时采集当前的环境的数据。