GD32中的IIC
今天来了解一下GD32中的硬件IIC,其实我个人是觉得软件IIC比较方便的,不过之前文章里用的都是软件IIC,今天就算是走出自己的舒适圈,我们来了解了解GD32中的硬件IIC。
我这里用的型号是GD32F407,不同型号的只需要看准自己板子的资源引脚即可。
关于IIC以及本文中演示的SHT20,在之前的文章里都有,并且也不是本文的重点,因此这里就不介绍了,不了解且感兴趣的小伙伴可以去看看之前的文章。
【STM32F103】I2C通信协议&SHT20温湿度传感器_sht20通信方式-CSDN博客文章浏览阅读2.3k次,点赞29次,收藏33次。I2C是Inter IC BUS=IIC=I²C=I2C,一般我们读作“挨方C”。简述一下I2C,是只需要两根通信线就能实现多主多从半双工的串行通信协议。传输速度会偏慢一点点,一般是100Kbps,是属于标准模式。另外还有快速模式,400Kbps;高速模式3.4Mbps;超快速模式5Mbps(后两种没接触过)。分别是SCL和SDA。SCL是Serial Clock,也就是统一时间的。SDA是Serial Data,也就是传输数据的。_sht20通信方式https://blog.csdn.net/m0_63235356/article/details/135734887?spm=1001.2014.3001.5501
接下来我们来看看GD32的IIC。
GD32F407一共有三个IIC资源,挂载在了APB1上面。
硬件IIC0的数据线在GPIOB的8和9号引脚上。因此我们首先就是要打开GPIOB的外设时钟,以及初始化一下这俩引脚,因为我们用的是硬件资源,因此要设置为复用模式。
rcu_periph_clock_enable(RCU_GPIOB);
gpio_af_set(GPIOB, GPIO_AF_4,GPIO_PIN_8|GPIO_PIN_9);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_8|GPIO_PIN_9);
gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,GPIO_PIN_8|GPIO_PIN_9);
引脚复用我们选择4号复用。
不同型号需要查阅自己型号的固件库手册,比如说GD32E230的I2C就是0号复用。
上一篇文章写串口的在这方面是直接跳过了,现在在这边补充一下。
包括输出模式设置成什么上一篇好像也是跳过的,其实这一点我们可以查阅手册。
甚至我们可以直接参考STM32的手册,因为STM32里在GPIO章节里直接有个表格方便我们查阅,而GD32里可能也有但是我没有找到。
关于GPIO的设置我们就说到这边,其实我们使用到硬件资源的时候初始化GPIO都是这一套流程,具体细节在文档中都能找到。
接下来就是关于IIC的固件库函数了。
i2c_deinit(I2C0);
首先一样是复位函数,在设置IIC之前我们最好都调用一遍。
i2c_clock_config(I2C0, 100000, I2C_DTCY_2);
设置IIC的时钟,参数二理论上我们可以随便填入一个32bit大小的值,但是我们最好还是按照IIC常见的速率来设置,例如100k,400k这样。
i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0X80);
接下来是设置IIC通信的模式与地址,模式我们自然是选择I2C模式的,而地址可以选择7位或者是10位的(10位的参数截图没截上,因为卡在手册的下一页了),这个根据我们通信的模块的从机地址而定。
我们今天示范的SHT20是7位从机地址,是100 000,对应到十六进制就是0x80。
i2c_ack_config(I2C0, I2C_ACK_ENABLE);
设置发送应答,我们一般都设置为发送,当发送我们结束IIC通信的上一个时序的时候我们再关闭应答。
i2c_master_addressing(I2C0,0x80,I2C_TRANSMITTER);
发送从机地址,在IIC中我们开始时序发送之后第一个要发送的数据就是从机地址。我们知道从机地址是7位,剩下一位就是读写位了,0为写,1为读,我们不用手动去修改,直接调用这个函数就可以达到设置收发状态的从机地址并发送了。
不嫌麻烦的话,调用发送时序的函数再手动修改从机地址也是一样的效果。
i2c_enable(I2C0);
使能IIC,设置完IIC后使能,我们就可以使用IIC了。
IIC时序其实不多,就是开始,结束,发送,接收。应答的话硬件IIC会自动帮我们发送接收可以不用管。接下来我们就来看看这些时序对应的固件库函数是哪一些。
i2c_start_on_bus(I2C0);
这个就是起始时序了。起始时序就是在SCL高电平的时候,SDA从高电平切换到低电平。
i2c_stop_on_bus(I2C0);
发送结束时序。结束时序就是在SCL高电平的时候,SDA从低电平切换到高电平。
i2c_data_transmit(I2C0,data);
发送时序,在SCL低电平的时候,主机将数据放置到SDA(1为高电平,0为低电平) 主机拉高SCL的时候,在SCL高电平时,从机读取SDA的数据。可以一次发送8位数据。
i2c_data_receive(I2C0)
接收时序,一次收8bit。
至此我们就集齐了IIC的时序了,可以开始IIC了……吗?
硬件IIC麻烦的地方来了。那就是每个时序我们都需要等待标志位以及清除标志位。
获取标志位的函数在上面,我们讲过的时序用到的标志位参数我用红框框出来了。
获取完之后还得清除,传入的参数和上面获取的函数基本一样,我就不贴出来了。不过有些标志位是不用我们手动清除的,这个具体要查看手册,手册中没有对应的标志位参数就代表我们不用手动清除。
那么接下来我们就可以进行IIC通信了,时序都凑齐了,我们每发完一个时序都需要等待标志位置位(获取标志位)并且清除。
接下来我直接贴出代码(GD32F407),我会尽量写出注释,关于SHT20的看不懂的部分可以回顾一下开头链接的文章。
串口部分可以参考上一篇文章。
#include "board.h"
#include <stdio.h>
#include "Z_UART.h"
float SHT20_GetData(uint8_t command){
uint16_t res = 0;
i2c_start_on_bus(I2C0); //起始时序
while(!i2c_flag_get(I2C0,I2C_FLAG_SBSEND) ); //等待起始位发送完. 这个不用手动清除标志位
i2c_master_addressing(I2C0, 0x80, I2C_TRANSMITTER); //发送从机地址(0x80)+写命令(0)
while(!i2c_flag_get(I2C0,I2C_FLAG_ADDSEND) ); //等待从机发送完毕之后得到回应(即从机地址正确)
i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND); //清除标志位
while(!i2c_flag_get(I2C0,I2C_FLAG_TBE)); //等待发送缓冲区空
if(command == 'w') i2c_data_transmit(I2C0,0xF3); //发送数据,发送SHT20的指令,F3为获取温度,F5为获取湿度
else i2c_data_transmit(I2C0,0xF5);
while(!i2c_flag_get(I2C0,I2C_FLAG_BTC) ); //等待字节传输完毕
i2c_stop_on_bus(I2C0); //发送结束时序
uint8_t count = 0; //计数,因为SHT20采集数据需要时间,我们设置个超时时间
do{
i2c_start_on_bus(I2C0); //起始时序
while(!i2c_flag_get(I2C0,I2C_FLAG_SBSEND)); //等待起始位发送完毕
i2c_master_addressing(I2C0, 0x80, I2C_RECEIVER);//发送从机地址(0x80)+读命令(1)
delay_ms(10); //延时10ms
if(++count >= 10) return 0; //超过100ms我们就算读取失败
}while(!i2c_flag_get(I2C0,I2C_FLAG_ADDSEND)); //等待回应
i2c_flag_clear(I2C0,I2C_FLAG_ADDSEND); //清除标志位
i2c_ack_config(I2C0, I2C_ACK_ENABLE); //开启应答
while(!i2c_flag_get(I2C0,I2C_FLAG_RBNE) ); //等待接收缓冲区不为空
res = i2c_data_receive (I2C0); //读取SHT传来的数据的高8位
res <<= 8;
i2c_ack_config(I2C0, I2C_ACK_DISABLE); //关闭应答,因为我们就获取俩8bit数据
while(!i2c_flag_get(I2C0,I2C_FLAG_RBNE) ); //等待接收缓冲区不为空
res |= i2c_data_receive (I2C0); //读取SHT传来的数据的低8位
i2c_stop_on_bus(I2C0); //结束时序
res &= 0xFFFC; //清除最后两位,这是SHT20要求的
//根据指令的不同(获取温度/湿度)来计算数据
if(command == 'w') return ((res / 65536.0) * 175.72 - 46.85);
return (( res / 65536.0) * 125 - 6);
}
int main(void){
board_init();
//初始化串口,为了将结果打印到串口助手上,不懂怎么操作的小伙伴可以看看上一篇文章
Z_UART_Init();
//开启时钟
rcu_periph_clock_enable(RCU_I2C0);
rcu_periph_clock_enable(RCU_GPIOB);
//初始化硬件IIC的引脚
gpio_af_set(GPIOB, GPIO_AF_4,GPIO_PIN_8|GPIO_PIN_9);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_8|GPIO_PIN_9);
gpio_output_options_set(GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ,GPIO_PIN_8|GPIO_PIN_9);
i2c_deinit(I2C0); //复位IIC0
i2c_clock_config(I2C0, 100000, I2C_DTCY_2); //设置IIC速率为100k
i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0X80); //设置SHT20的七位地址
i2c_ack_config(I2C0, I2C_ACK_ENABLE); //使能应答
i2c_enable(I2C0); //使能IIC
printf("hello world!\r\n");
while (1){
printf("%f\t%f\r\n",SHT20_GetData('w'),SHT20_GetData('s'));
delay_ms(1000);
}
}
可以正常接收数据。