【AT-START-F425测评】温湿度计(硬件I2C)AHT10
[复制链接]
本帖最后由 lugl4313820 于 2022-3-29 14:08 编辑
前面驱动了SPI屏,今天学习了AT32F425的I2C硬件。参考学习了网友的例子,拆腾一天,终于把I2C硬件驱动好了温湿计,下面汇报如下:
【驱动的步骤】
1、在drivers目录下新建AHT驱动文件AHT10.c与头文件AHT10.h。
2、选择I2C1的IO为PB6—SCL,PB7—SDA 。查看数据手册为GPIO_MUX_1。
3、宏定义好I2C超时时间、通信速度、AHT10的器件地址以及CLK、GPIO端口引脚,复用序号等:
#define I2C_TIMEOUT 0xFFFFFFF
//#define I2Cx_CLKCTRL 0x2170FAFA //10K
//#define I2Cx_CLKCTRL 0x80E06565 //50K
#define I2Cx_CLKCTRL 0x80E03030 //100K
//#define I2Cx_CLKCTRL 0x20E0355F //200K
#define I2Cx_ADDRESS 0xA0
#define AHT10_ADDRESS 0x70
#define I2Cx_PORT I2C1
#define I2Cx_CLK CRM_I2C1_PERIPH_CLOCK
#define I2Cx_DMA DMA1
#define I2Cx_DMA_CLK CRM_DMA1_PERIPH_CLOCK
//SCL 引脚配置重定义
#define I2Cx_SCL_GPIO_CLK CRM_GPIOB_PERIPH_CLOCK
#define I2Cx_SCL_GPIO_PIN GPIO_PINS_6
#define I2Cx_SCL_GPIO_PinsSource GPIO_PINS_SOURCE6
#define I2Cx_SCL_GPIO_PORT GPIOB
#define I2Cx_SCL_GPIO_MUX GPIO_MUX_1
//SDA 引脚配置重定义
#define I2Cx_SDA_GPIO_CLK CRM_GPIOB_PERIPH_CLOCK
#define I2Cx_SDA_GPIO_PIN GPIO_PINS_7
#define I2Cx_SDA_GPIO_PinsSource GPIO_PINS_SOURCE7
#define I2Cx_SDA_GPIO_PORT GPIOB
#define I2Cx_SDA_GPIO_MUX GPIO_MUX_1
3、初始化i2c设备结构体 i2c_handle_type hi2cx;
4、修改void i2c_lowlevel_init,去i2c例程中去复制一个函数。这里没有用的DMA以及中断,可以把中断与DMA配置的函数去掉:
/**
* @brief initializes peripherals used by the i2c.
* @param none
* @retval none
*/
void i2c_lowlevel_init(i2c_handle_type* hi2c)
{
gpio_init_type gpio_init_structure;
if(hi2c->i2cx == I2Cx_PORT)
{
/* i2c 时钟使能 */
crm_periph_clock_enable(I2Cx_CLK, TRUE);
crm_periph_clock_enable(I2Cx_SCL_GPIO_CLK, TRUE);
crm_periph_clock_enable(I2Cx_SDA_GPIO_CLK, TRUE);
/* gpio 复用端口配置 */
gpio_pin_mux_config(I2Cx_SCL_GPIO_PORT, I2Cx_SCL_GPIO_PinsSource, I2Cx_SCL_GPIO_MUX);
gpio_pin_mux_config(I2Cx_SDA_GPIO_PORT, I2Cx_SDA_GPIO_PinsSource, I2Cx_SDA_GPIO_MUX);
/* 配置 i2c pins: scl */
gpio_init_structure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_structure.gpio_mode = GPIO_MODE_MUX;
gpio_init_structure.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
gpio_init_structure.gpio_pull = GPIO_PULL_UP;
gpio_init_structure.gpio_pins = I2Cx_SCL_GPIO_PIN;
gpio_init(I2Cx_SCL_GPIO_PORT, &gpio_init_structure);
/* 配置 i2c pins: sda */
gpio_init_structure.gpio_pins = I2Cx_SDA_GPIO_PIN;
gpio_init(I2Cx_SDA_GPIO_PORT, &gpio_init_structure);
/* config i2c */
i2c_init(hi2c->i2cx, 0, I2Cx_CLKCTRL);
i2c_own_address1_set(hi2c->i2cx, I2C_ADDRESS_MODE_7BIT, AHT10_ADDRESS);
i2c_enable(hi2c->i2cx,TRUE);
}
}
5、AHT10写寄存器函数:
void AHT_Reset_REG(uint8_t addr)
{
uint8_t dat1[2]={0};
uint8_t dat2[3]={0};
// i2c_status_type i2c_status;
rt_thread_mdelay(100);
i2c_memory_write(&hi2cx,AHT10_ADDRESS,addr,dat1,2,0xffffff);
rt_thread_mdelay(5);
i2c_master_receive(&hi2cx,AHT10_ADDRESS,dat2,3,0xffffff);
dat2[0]=0xb0|addr;
i2c_master_transmit(&hi2cx,AHT10_ADDRESS,dat2,3,0xffffff);
}
6、AHT10初始化函数:
void InitAHT10(void)
{
unsigned char rest_dat[2]={0,0},dat1[2]={0x00,0x80};
hi2cx.i2cx = I2Cx_PORT;
i2c_config(&hi2cx);
//unsigned char status;
rt_thread_mdelay(150);
AHT_Reset_REG(0x1b);
rt_thread_mdelay(50);
AHT_Reset_REG(0x1c);
rt_thread_mdelay(50);
AHT_Reset_REG(0x1e);
rt_thread_mdelay(200);
i2c_memory_write(&hi2cx,AHT10_ADDRESS,0xa8,rest_dat,2,0xffffff);//0xA8进入NOR工作模式
rt_thread_mdelay(5);
i2c_memory_write(&hi2cx,AHT10_ADDRESS,0xe1,dat1,2,0xfffff);//0xBE初始化命令,AHT20的初始化命令是0xBE, AHT10的初始化命令是0xE1
rt_thread_mdelay(5);
}
7、读取温度与数据:
void AHT10_Read_CTdata(uint32_t *ct) //没有CRC校验,直接读取AHT10的温度和湿度数据
{
// i2c_status_type i2c_status;
unsigned char dat1[3]={0xac,0x33,0x00},retuenDAT[6]={0},dat2[3]={0xbe,0x08,0x00};//dat1 AC读取命令,retuenDAT 缓存数组,dat2为校准命令
// unsigned char status=0;
//uint16_t cnt = 0;
rt_thread_mdelay(50);
i2c_master_transmit(&hi2cx,AHT10_ADDRESS,dat2,3,0xffffff);//向AHT20发送校准命令
rt_thread_mdelay(50);
i2c_master_transmit(&hi2cx,AHT10_ADDRESS,dat1,3,0xffffff);//向AHT20发送AC命令
// printf("i2c_read_status=%d\n\r",i2c_status);
rt_thread_mdelay(50);
i2c_master_receive(&hi2cx,AHT10_ADDRESS,retuenDAT,6,0xffffff);
// printf("i2c_receive_status=%d\n\r",i2c_status);
while((retuenDAT[0]&0x80)==0x80)
{
i2c_master_receive(&hi2cx,AHT10_ADDRESS,retuenDAT,6,0xffffff);
rt_thread_mdelay(10);
printf("busy =0x%x\n\r",retuenDAT[0]);
}
ct[0]=(((uint32_t)retuenDAT[1]<<12)+((uint32_t)retuenDAT[2]<<4)+((uint32_t)retuenDAT[3]>>4));
ct[1]=((((uint32_t)retuenDAT[3]&0xf)<<16)+((uint32_t)retuenDAT[4]<<8)+((uint32_t)retuenDAT[5]));
}
至此,AHT10的函数已经写好,在main函数中引入AHT10.h,创建一个任务,在LCD屏上显示采集到的数据。
#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 1024
#define THREAD_TIMESLICE 5
static rt_thread_t tid_AHT10 = RT_NULL;
static void tid_AHT10_entry(void *parameter)
{
uint32_t temp[2]={0};
volatile float t1;
u8 humidity;
while(1)
{
AHT10_Read_CTdata(temp);
humidity=(int)(temp[0]*100.0/1024/1024+0.5);
t1=((int)(temp[1]*2000.0/1024/1024+0.5))/10.0-50;
LCD_ShowIntNum(90, 100, humidity, 2, BLUE,WHITE, 32);
LCD_ShowFloatNum1(80, 60, t1, 4, BLUE,WHITE, 32);
rt_thread_mdelay(500);
}
}
【感受】
经过一天的学习,AT32F425的学习资源还是很多的,特别是雅特力这次做这个开发板的测评活动,出的贴子非常多,关于i2c驱动OLED屏的文章也很多,只要花点时间,i2c的驱动还是很容易的。我这次花了一些时间来阅读官方提供的源码,收获也挺多。
但是昨晚本来驱动好了,准备收工时,突然程序卡死,找了好久的原因,直到今天中午才找到,就是USB供电一定要稳定、电流要足够,要不就找不到AHT10,这样就一直等待超时。其实官方出的这个驱动的中间件在处理超时的函数还是阻塞式的,在RT—Thread OS下,会因为阻塞时间处发生跑飞现象。要用到生产环境,还是要加以修改为非阻塞的超时等待。
这个I2C的程序还有很大的修改空间,比如I2C在等待接收ACK、NACK、STOP等超时返回错误的判断等。用硬的i2C相比软件模拟的就要更多的考虑到极端环境的问题,要不会出现很多意想不到的结果。
|