本期视频是得捷电子FastBond活动作品,系统使用亚德诺AD7745电容-数字转换器获取湿度数据,使用美信MAX6608获取温度数据,核心采用CH32V103C8T6单片机,整体单节7号干电池Boost升压供电方案。屏幕型号ST7565P,搭配两个按键,两个状态指示灯。监控系统的显示界面可实时显示当前温度湿度,绘制动态波形并可根据波形数据显示历史最大最小值。温度与湿度界面每30秒自动切换一次,也可通过用户按键强制切换。
系统所使用温度传感器为MAX6608高精度模拟温度传感器。传感器提供SC70和SOT23两种封装形式,工作电压范围:+1.8V~+3.6V,工作电流仅8uA;环境温度+20℃~+50℃精度可达±0.6℃;输出电压与温度增量呈线性关系,0℃基础电压为500mV,按照10mV/℃递增。MAX6608输出模拟电压信号,直接使用MCU的ADC采集即可。换算方式相对简单,通过多次采样求取均值可使数据更加稳定。分享编写好的MAX6608.c文件如下:
void MAX6608_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1))
;
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1))
;
}
uint16_t MAX6608_GetVal(void)
{
uint16_t adc_val = 0;
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
;
adc_val = ADC_GetConversionValue(ADC1);
return adc_val;
}
float MAX6608_GetT(void)
{
float temp = 0;
uint16_t adc_val = 0;
float adc_mean_val = 0;
for (uint8_t i = 0; i < 10; i++)
adc_val = adc_val + MAX6608_GetVal();
adc_val = adc_val / 10;
adc_mean_val = 3.3 * 1000 / 4096 * adc_val;
temp = (adc_mean_val - 500) / 10;
return temp;
}
亚德诺AD7745电容数字转换器可采用I2C两线串行通信,TSSOP16引脚封装,工作温度范围–40℃至+125℃;共模电容量程 17 pF,满量程变化范围:±4 pF,刷新率:10 Hz 至 90 Hz,线性度:0.01%,分辨率:4 aF,精度:4 fF。芯片内置温度传感器,分辨率0.1℃,精度±2℃。
AD7745采用电荷平衡电路测量外部输入电容。对电容器而言,Q=V×C,若保持电荷Q不超过量程,减小激励电压V至原来的1/F,则输入电容可拓展至F倍。AD7745具有两个独立的激励电压源A和B。参考电路原理图,将A与B反相设置,可以得到衰减后的激励电压幅值与原电压的倍数关系。
系统所使用CHS1101电容式湿度传感器,在湿度为55%时,标称电容值为178.4pF。传感器动态变化范围在162.4pF~194.4pF;取最大值为200pF,除以共模量程17pF,则量程至少需要拓展11倍,原理图中,R2为100k,R3为120k即可满足上述要求。
AD7745使用时需通过内部数据对测量结果进行修正,修正参数在芯片出厂前预先固定于芯片内,实际量程可看作标称值与增益之积。由于传感器电容量仍然大于拓展后的共模量程,使用芯片内CAPDAC补偿共模分量,使剩余电容落在量程以内。本系统必须使用CAPDAC+,具体使用方法请参考官方文档,也可参考如下代码。
void AD7746_Config(void)
{
uint8_t p = 0;
{
AD7746_Read(AD7746_REG_STATUS, &p, 1); //读状态寄存器
AD7746_Read(AD7746_REG_CAP_GAINH, &cap_gain_h, 1); //读校准寄存器高位
AD7746_Read(AD7746_REG_CAP_GAINL, &cap_gain_l, 1); //读校准寄存器低位
cap_gain = ((float)((1 << 16) + ((cap_gain_h << 8) | cap_gain_l))) / (1 << 16);
cap_ref = 4.096 * cap_gain;
cap_dac = cap_ref * 3.2;
cap_lsb_dac = cap_dac / 127;
cap_eff = cap_lsb_dac * 11;
cap_set = h_std_pf / cap_eff;
p = 0x80 | cap_set;
AD7746_Write(AD7746_REG_CAPDACA, &p, 1); //使能CAPDAC+
p = AD7746_CAPSETUP_CAPEN;
AD7746_Write(AD7746_REG_CAP_SETUP, &p, 1); //电容转换使能,单端模式
p = AD7746_VTSETUP_VTEN;
AD7746_Write(AD7746_REG_VT_SETUP, &p, 1); //温度转换使能
p = AD7746_EXCSETUP_EXCA | AD7746_EXCSETUP_NEXCB | AD7746_EXCSETUP_EXCLVL(2);
AD7746_Write(AD7746_REG_EXC_SETUP, &p, 1); //AB通道激励使能
//CDC采样速率设置、温度传感器采样速率设置
//16.1Hz、16.1Hz连续采样
p = AD7746_CONF_VTFS(2) | AD7746_CONF_CAPFS(4) | AD7746_CONF_MODE_CONT_CONV;
AD7746_Write(AD7746_REG_CFG, &p, 1);
//50.0Hz、49.8Hz连续采样
//p = AD7746_CONF_VTFS(0) | AD7746_CONF_CAPFS(2) | AD7746_CONF_MODE_CONT_CONV;
//AD7746_Write(AD7746_REG_CFG, &p, 1);
}
}
float AD7746_GetC(void)
{
uint32_t cap_u32 = 0;
float cap_f = 0;
cap_u32 = AD7746_GetCapData();
cap_f = ((float)(((int32_t)cap_u32) - 0x800000)) * 4.096 * cap_gain / 0x800000 * 11 + h_std_pf;
return cap_f;
}
显示界面定时推送,主要是缓存帧操作,涉及一些字符串处理,感兴趣可以看下。
//绘制界面,整帧刷新
void Draw_Desktop(void)
{
float cap_f = 0; //湿度传感器电容值
float h_f = 0; //湿度值
float h_fmax = 0; //湿度最大值
float h_fmin = 0; //湿度最小值
float t_f = 0; //温度值
float t_fmax = 0; //温度最大值
float t_fmin = 0; //温度最小值
char h_str[10] = {0}; //湿度字符串
char t_str[10] = {0}; //温度字符串
uint8_t str_length = 0; //字符串长度
char *piont_p; //小数点位置指针
uint8_t wave_mark = 0; //波形标记
Delay_Ms(1000);
display_sw++;
if (display_sw < 30)
{
//获取湿度传感器电容值
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_SET);
cap_f = AD7746_GetC();
GPIO_WriteBit(GPIOA, GPIO_Pin_3, Bit_RESET);
//获取温度传感器电压值
//电容值-湿度转换
h_f = (cap_f - h_std_pf) / 0.32 + 55;
//更新湿度序列
for (uint8_t i = 0; i < 99; i++)
{
h_sq[99 - i] = h_sq[98 - i];
}
h_sq[0] = h_f;
//绘制湿度
//绘制桌面元素
CF_BMP2FrameBuffer(gImage_humidity);
CF_Display_12x24_A(20, 0, asiic_12x24_R);
CF_Display_12x24_A(32, 0, asiic_12x24_H);
CF_Display_12x24_A(116, 0, asiic_12x24_percent);
//绘制数据
sprintf(h_str, "%.2lf", h_f); //将湿度换为字符串数组
str_length = strlen(h_str); //获取字符串数组的长度
piont_p = strchr(h_str, '.'); //获取字符串数组中'.'的地址
//清空上次写入显示缓存的数据
if (str_length == 5) //当字符串长度为5,即aa.bb时
{
CF_Display_12x24_A(50, 0, asiic_12x24[*(piont_p - 2) - '0']);
}
if (str_length == 4) //当字符串长度为4,即a.bb时
{
CF_Display_12x24_A(50, 0, asiic_12x24_space);
}
CF_Display_12x24_A(62, 0, asiic_12x24[*(piont_p - 1) - '0']);
CF_Display_12x24_A(74, 0, asiic_12x24_point);
CF_Display_12x24_A(86, 0, asiic_12x24[*(piont_p + 1) - '0']);
CF_Display_12x24_A(98, 0, asiic_12x24[*(piont_p + 2) - '0']);
//绘制动态波形
h_fmax = h_fmin = h_sq[0];
for (uint8_t i = 0; i < 100; i++)
{
h_fmax = h_fmax > h_sq[i] ? h_fmax : h_sq[i];
h_fmin = h_fmin < h_sq[i] ? h_fmin : h_sq[i];
}
sprintf(h_str, "%.2lf", h_fmax); //将最大湿度换为字符串数组
str_length = strlen(h_str); //获取字符串数组的长度
piont_p = strchr(h_str, '.'); //获取字符串数组中'.'的地址
//清空上次写入显示缓存的数据
if (str_length == 5) //当字符串长度为5,即aa.bb时
{
CF_Display_5X8_A(0, 34, asiic_5x8[*(piont_p - 2) - '0']);
}
if (str_length == 4) //当字符串长度为4,即a.bb时
{
CF_Display_5X8_A(0, 56, asiic_5x8_space);
}
CF_Display_5X8_A(5, 34, asiic_5x8[*(piont_p - 1) - '0']);
CF_Display_5X8_A(10, 34, asiic_5x8_point);
CF_Display_5X8_A(15, 34, asiic_5x8[*(piont_p + 1) - '0']);
CF_Display_5X8_A(20, 34, asiic_5x8[*(piont_p + 2) - '0']);
CF_Display_5X8_A(0, 26, asiic_5x8_M);
CF_Display_5X8_A(5, 26, asiic_5x8_A);
CF_Display_5X8_A(10, 26, asiic_5x8_X);
sprintf(h_str, "%.2lf", h_fmin); //将最小湿度换为字符串数组
str_length = strlen(h_str); //获取字符串数组的长度
piont_p = strchr(h_str, '.'); //获取字符串数组中'.'的地址
//清空上次写入显示缓存的数据
if (str_length == 5) //当字符串长度为5,即aa.bb时
{
CF_Display_5X8_A(0, 56, asiic_5x8[*(piont_p - 2) - '0']);
}
if (str_length == 4) //当字符串长度为4,即a.bb时
{
CF_Display_5X8_A(0, 56, asiic_5x8_space);
}
CF_Display_5X8_A(5, 56, asiic_5x8[*(piont_p - 1) - '0']);
CF_Display_5X8_A(10, 56, asiic_5x8_point);
CF_Display_5X8_A(15, 56, asiic_5x8[*(piont_p + 1) - '0']);
CF_Display_5X8_A(20, 56, asiic_5x8[*(piont_p + 2) - '0']);
CF_Display_5X8_A(0, 48, asiic_5x8_M);
CF_Display_5X8_A(5, 48, asiic_5x8_I);
CF_Display_5X8_A(10, 48, asiic_5x8_N);
for (uint8_t i = 0; i < 100; i++)
{
wave_mark = (h_sq[i] - h_fmin) / (h_fmax + 1.2 - h_fmin) * 38;
for (uint8_t j = 0; j < 38; j++)
{
CF_DrawPoint(28 + i, 26 + j, (38 - j) > wave_mark ? 0 : 1);
}
}
}
if (display_sw >= 30)
{
if (display_sw == 60)
{
display_sw = 0;
}
//获取温度传感器数值
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_SET);
t_f = MAX6608_GetT();
Delay_Ms(10);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, Bit_RESET);
//更新温度序列
for (uint8_t i = 0; i < 99; i++)
{
t_sq[99 - i] = t_sq[98 - i];
}
t_sq[0] = t_f;
//绘制湿度
//绘制桌面元素
CF_BMP2FrameBuffer(gImage_temperature);
//绘制数据
sprintf(t_str, "%.2lf", t_f); //将湿度换为字符串数组
str_length = strlen(t_str); //获取字符串数组的长度
piont_p = strchr(t_str, '.'); //获取字符串数组中'.'的地址
//清空上次写入显示缓存的数据
if (str_length == 5) //当字符串长度为5,即aa.bb时
{
CF_Display_12x24_A(50, 0, asiic_12x24[*(piont_p - 2) - '0']);
}
if (str_length == 4) //当字符串长度为4,即a.bb时
{
CF_Display_12x24_A(50, 0, asiic_12x24_space);
}
CF_Display_12x24_A(62, 0, asiic_12x24[*(piont_p - 1) - '0']);
CF_Display_12x24_A(74, 0, asiic_12x24_point);
CF_Display_12x24_A(86, 0, asiic_12x24[*(piont_p + 1) - '0']);
CF_Display_12x24_A(98, 0, asiic_12x24[*(piont_p + 2) - '0']);
//绘制动态波形
t_fmax = t_fmin = t_sq[0];
for (uint8_t i = 0; i < 100; i++)
{
t_fmax = t_fmax > t_sq[i] ? t_fmax : t_sq[i];
t_fmin = t_fmin < t_sq[i] ? t_fmin : t_sq[i];
}
sprintf(t_str, "%.2lf", t_fmax); //将最大湿度换为字符串数组
str_length = strlen(t_str); //获取字符串数组的长度
piont_p = strchr(t_str, '.'); //获取字符串数组中'.'的地址
//清空上次写入显示缓存的数据
if (str_length == 5) //当字符串长度为5,即aa.bb时
{
CF_Display_5X8_A(0, 34, asiic_5x8[*(piont_p - 2) - '0']);
}
if (str_length == 4) //当字符串长度为4,即a.bb时
{
CF_Display_5X8_A(0, 56, asiic_5x8_space);
}
CF_Display_5X8_A(5, 34, asiic_5x8[*(piont_p - 1) - '0']);
CF_Display_5X8_A(10, 34, asiic_5x8_point);
CF_Display_5X8_A(15, 34, asiic_5x8[*(piont_p + 1) - '0']);
CF_Display_5X8_A(20, 34, asiic_5x8[*(piont_p + 2) - '0']);
CF_Display_5X8_A(0, 26, asiic_5x8_M);
CF_Display_5X8_A(5, 26, asiic_5x8_A);
CF_Display_5X8_A(10, 26, asiic_5x8_X);
sprintf(t_str, "%.2lf", t_fmin); //将最小湿度换为字符串数组
str_length = strlen(t_str); //获取字符串数组的长度
piont_p = strchr(t_str, '.'); //获取字符串数组中'.'的地址
//清空上次写入显示缓存的数据
if (str_length == 5) //当字符串长度为5,即aa.bb时
{
CF_Display_5X8_A(0, 56, asiic_5x8[*(piont_p - 2) - '0']);
}
if (str_length == 4) //当字符串长度为4,即a.bb时
{
CF_Display_5X8_A(0, 56, asiic_5x8_space);
}
CF_Display_5X8_A(5, 56, asiic_5x8[*(piont_p - 1) - '0']);
CF_Display_5X8_A(10, 56, asiic_5x8_point);
CF_Display_5X8_A(15, 56, asiic_5x8[*(piont_p + 1) - '0']);
CF_Display_5X8_A(20, 56, asiic_5x8[*(piont_p + 2) - '0']);
CF_Display_5X8_A(0, 48, asiic_5x8_M);
CF_Display_5X8_A(5, 48, asiic_5x8_I);
CF_Display_5X8_A(10, 48, asiic_5x8_N);
for (uint8_t i = 0; i < 100; i++)
{
wave_mark = (t_sq[i] - t_fmin) / (t_fmax + 0.2 - t_fmin) * 38;
for (uint8_t j = 0; j < 38; j++)
{
CF_DrawPoint(28 + i, 26 + j, (38 - j) > wave_mark ? 0 : 1);
}
}
}
//将温度信息写入缓存帧
CF_PushFrame();
}
视频分为三部分,第一部分是系统结构简要说明,包括软硬件总体框架与核心器件介绍;第二部分是关键内容介绍,包括电容数字转换芯片AD7745量程拓展,容型湿度传感器电容值与湿度换算,使用CH32V单片机读取MAX6608模拟温度传感器电压并换算温度;视频末尾是实物展示,活动提交页面可获取源码包,提供ST7565P显示屏的驱动与帧缓存实现。