【AT-START-F425 Review】Thermohygrometer (Hardware I2C) AHT10
[Copy link]
This post was last edited by lugl4313820 on 2022-3-29 14:08
I have driven the SPI screen before, and today I learned about the I2C hardware of AT32F425. After referring to the examples of netizens and working on it for a day, I finally got the I2C hardware to drive the thermometer and hygrometer. The report is as follows:
【Drive steps】
1. Create a new AHT driver file AHT10.c and header file AHT10.h in the drivers directory.
2. Select I2C1 IO as PB6-SCL, PB7-SDA. Check the data sheet for GPIO_MUX_1.
3. Define the I2C timeout, communication speed, AHT10 device address, CLK, GPIO port pins, multiplexing number, etc. in the macro:
#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. Initialize the i2c device structure i2c_handle_type hi2cx;
4. Modify void i2c_lowlevel_init and copy a function in the i2c routine. There is no useful DMA and interrupt here, so you can remove the interrupt and DMA configuration functions:
/**
* @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 write register function:
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 initialization function:
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. Read temperature and data:
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]));
}
At this point, the AHT10 function has been written. Import AHT10.h into the main function and create a task to display the collected data on the LCD screen.
#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);
}
}
【Feelings】
After a day of study, I found that there are still a lot of learning resources for AT32F425, especially the evaluation activity of this development board by Arteli, which has published a lot of posts and articles about driving OLED screens with i2c. As long as you spend some time, driving i2c is still very easy. I spent some time reading the official source code this time and gained a lot.
But last night, when the driver was ready and I was about to finish work, the program suddenly got stuck. I searched for the reason for a long time until noon today. The reason was that the USB power supply must be stable and the current must be sufficient, otherwise it cannot find AHT10 and will keep waiting for a timeout. In fact, the middleware of this official driver is still blocking when processing timeout functions. Under RT-Thread OS, it will run away due to the blocking time. If you want to use it in a production environment, you still need to modify it to a non-blocking timeout wait.
There is still a lot of room for modification in this I2C program, such as the judgment of I2C returning an error when waiting for ACK, NACK, STOP, etc. Compared with software simulation, using hard I2C requires more consideration of extreme environment issues, otherwise there will be many unexpected results.
|