描述
<h2>视频链接:</h2>
<p><a href="https://www.bilibili.com/video/BV17uYTedETb/?pop_share=1&vd_source=4948554f342901cce6a4269d440c97d3" target="_blank">B站视频--功能演示及介绍</a></p>
<h2>项目简介</h2>
<p>该项目使用“盛思锐传感器”,高精度、小体积,非常适用于温湿度项目制作。</p>
<h2>项目功能</h2>
<p>温湿度检测<br>
桌面温湿度检测仪通过SHT40检测室内温湿度,通过两个3位共阴极数码管显示数据,整体使用两节5号电池进行供电,非触发时处于休眠状态,预计使用时长6个月。</p>
<p>温湿度检测仪项目使用STM32G030K6T6芯片作为主控芯片,该芯片使用ARM-Cortex-M0+内核;最大主频64Mhz;具有32KB的FLash和8K的SRAM,供电电压在2.0V~3.6V之间。</p>
<h2>软件代码</h2>
<p>使用STM32CubeMx软件生成代码为共阳极,并把显示效果改成温湿度同显示。</p>
<p>电池进行供电,考虑到功耗问题,默认不开机,进入睡眠模式。当有按键按下时,触发中断,此时结束睡眠模式,执行按键判断,获取温湿度并启动数码管进行显示;同时启动定时器中断,循环显示两次数据后再次进入睡眠,等待下次唤醒。</p>
<p>重点是中断,STM32G0系列使用Arm Cortex M0+内核,是两级流水线的冯诺依曼结构,具有4位中断优先级。这些与STM32F103是不一致的。</p>
<p>重点介绍一下如何进入休眠模式以及定时器中断。</p>
<p>STM32G0系列有四种休眠模式;</p>
<p>低功耗运行模式(降低CPU频率,系统仍在运行)
睡眠模式(系统进入睡眠,任意中断/事件唤醒)
停止模式(系统进入停止,支持任意外部中断和RTC闹钟唤醒)
待机模式(系统进入待机,支持RTC闹钟唤醒,WKUP、NRST引脚唤醒以及IWDG复位唤醒,打开了LSI和LSE)</p>
<p>注意以上模式均不可进行Debuge调试,系统时钟已经关闭;且设置为低功耗模式后,无法下载代码,需要唤醒后才能进行下载;详细说明可以查看具体数据手册。</p>
<p>大家根据实际情况进行设置,综合代码设置为睡眠模式。</p>
<p>睡眠模式代码</p>
<pre><code>//进入睡眠模式
HAL_SuspendTick(); //暂停滴答定时器,防止通过滴答定时器中断唤醒
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); /* 执行WFI指令, 进入睡眠模式 */
//退出睡眠模式,任意中断/事件退出
HAL_ResumeTick(); //恢复滴答定时器</code></pre>
<p>在Cubemx中设置了定时器14中断,代码中默认是不开启的,启动后自动开始计时。计时时间到来后进入溢出中断。
定时器溢出中断回调函数,由于计时时间较短,在回调函数中加入累加变量,表示数据显示时间,假设一1s为周期,前半秒显示温度数据,后半s显示湿度数据,显示完两次后进入休眠。 </p>
<p>定时器相关代码</p>
<pre><code>HAL_TIM_Base_Start_IT(&htim14); //开始定时器
HAL_TIM_Base_Stop_IT(&htim14); //停止定时器
/*
函数内容:定时器溢出中断回调函数
函数参数:TIM_HandleTypeDef *htim--定时器句柄
返回值: 无
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM14)
{
HAL_TIM_Base_Stop_IT(&htim14);
updata_flag++;
if(updata_flag <= 5000)
{
ShowNum(1,1,(device_paramter.Temp/100));
ShowNum(1,2,(device_paramter.Temp / 10 % 10));
ShowNum(1,3,device_paramter.Temp%10);
}
else if(updata_flag <= 10000)
{
ShowNum(2,1,(device_paramter.Humi/100));
ShowNum(2,2,(device_paramter.Humi / 10 % 10));
ShowNum(2,3,device_paramter.Humi%10);
}
else
{
updata_flag = 0;
sleep_flag++;
}
__HAL_TIM_SetCounter(&htim14,0);
if(sleep_flag >= 2)
{
sleep_flag = 0;
device_paramter.sleepStatus = 1;
SN74HC595_Send_Data(SN_DIG,0xFF);
SN74HC595_Send_Data(SN_LED1,0x00);
SN74HC595_Send_Data(SN_LED2,0x00);
}
else{
HAL_TIM_Base_Start_IT(&htim14);
}
}
}</code></pre>
<p>数码管优化,在前面的案例中,会看到数码管亮度不尽人意,这是因为此时主频太快导致显示过短;在原来的代码中,显示数据后立刻进行消影操作。此时CPU主频为64Mhz,执行速度较快,导致数据显示过暗。
在原有的基础上,加入一个us延时,数据显示出来后,延时一点点时间再消影,这样显示效果就好很多了。</p>
<p>优化数码管显示</p>
<pre><code>/* 64Mhz时钟时,当ulCount为1,函数耗时3个时钟,延时=3*1/64us */
__asm void SysCtlDelay(unsigned long ulCount)
{
SUBS r0,#1;
BNE SysCtlDelay;
BX lr;
}
/*
函数内容:数码管显示数据函数
函数参数:uint8_t row----行号
uint8_t column-列号
uint8_t value--显示数据
返回值:无
*/
void ShowNum(uint8_t row, uint8_t column, uint8_t value)
{
if(row == 1)
{
switch(column)
{
case 1: //如果是第一排第一个
SN74HC595_Send_Data(SN_DIG,0xFE);
SN74HC595_Send_Data(SN_LED1,sgh_value[value]);//显示值对应16进制数
break;
case 2:
SN74HC595_Send_Data(SN_DIG,0xFD);
SN74HC595_Send_Data(SN_LED1,(sgh_value[value]|0x80));
break;
case 3:
SN74HC595_Send_Data(SN_DIG,0xFB);
SN74HC595_Send_Data(SN_LED1,sgh_value[value]);
break;
default:
break;
}
SysCtlDelay(1000); //大致延时50us
SN74HC595_Send_Data(SN_LED1,0x00); //消影,防止错位
}
else
{
switch(column)
{
case 1:
SN74HC595_Send_Data(SN_DIG,0xF7);
SN74HC595_Send_Data(SN_LED2,sgh_value[value]);
break;
case 2:
SN74HC595_Send_Data(SN_DIG,0xEF);
SN74HC595_Send_Data(SN_LED2,(sgh_value[value]|0x80));
break;
case 3:
SN74HC595_Send_Data(SN_DIG,0xDF);
SN74HC595_Send_Data(SN_LED2,sgh_value[value]);
break;
default:
break;
}
SysCtlDelay(1000); //大致延时50us
SN74HC595_Send_Data(SN_LED2,0x00);
}
}</code></pre>
<h3>ADC电压采集</h3>
<p>桌面温湿度仪使用两节5号电池用于整个系统供电,这里加入一个分压电阻用于检测电池电压
<img src="//image.lceda.cn/oshwhub/1c8a8eca248d4835b6fe7de3d6ed62f9.png" alt="image.png"></p>
<p>使用两个10K对电压值进行分压,然后通过单片机ADC进行电压读取,对于3V电池,其实可以直接输入到单片机的IO口进行读取,但是大多数情况都是使用分压电阻来进行,这里我也不做改动。</p>
<p>在工程中,会增加一个adc文件,存放ADC相关初始化代码,重要的是ADC句柄,后续启动读取操作通过句柄来进行指定。
注意在gpio.c文件中添加数码管相关驱动代码,这些在之前的课程中都有介绍,我就不重复讲解了。
直接在main.c中添加ADC转换代码,这里配置的是软件触发,若像获得更高的精度,可以在初始化后进行ADC校准;</p>
<p>注意此处我们使用DAPLink进行测试,DAPLink是3.3V,所以转换的AD值是✖3.3V,后续使用电池供电是3V,需要进行修改。
且随着电池电压的降低,此处的测量精度也会逐渐下降,准确的测量应该是使用内部基准源做参考。</p>
<pre><code>
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t ADC_Value = 0;
float Data = 0;
uint16_t Vol_Value = 0;
/* USER CODE END 1 */</code></pre>
<p>/<em> MCU Configuration--------------------------------------------------------</em>/</p>
<p>/<em> Reset of all peripherals, Initializes the Flash interface and the Systick. </em>/
HAL_Init();</p>
<p>/<em> USER CODE BEGIN Init </em>/</p>
<p>/<em> USER CODE END Init </em>/</p>
<p>/<em> Configure the system clock </em>/
SystemClock_Config();</p>
<p>/<em> USER CODE BEGIN SysInit </em>/</p>
<p>/<em> USER CODE END SysInit </em>/</p>
<p>/<em> Initialize all configured peripherals </em>/
MX_GPIO_Init();
MX_ADC1_Init();
/<em> USER CODE BEGIN 2 </em>/
HAL_Delay(100);
SN74HC595_Send_Data(SN_DIG,0x00);
SN74HC595_Send_Data(SN_LED1,0x00);
SN74HC595_Send_Data(SN_LED2,0x00);</p>
<pre><code>HAL_ADCEx_Calibration_Start(&hadc1); //ADC校准
HAL_ADC_Start(&hadc1); //启动ADC转换
HAL_ADC_PollForConversion(&hadc1, 50); //等待转换完成,50为最大等待时间,单位为ms
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
ADC_Value = HAL_ADC_GetValue(&hadc1); //获取AD值
Data = (ADC_Value*3.3f)/4095.0f;
}</code></pre>
<p>/<em> USER CODE END 2 </em>/
Vol_Value = (uint16_t)(Data <em> 100)</em>2;
/<em> Infinite loop </em>/
/<em> USER CODE BEGIN WHILE </em>/
while (1)
{
/<em> USER CODE END WHILE </em>/
ShowNum(1,1,Vol_Value / 100);
ShowNum(1,2,Vol_Value /10 %10);
ShowNum(1,3,Vol_Value % 10);
/<em> USER CODE BEGIN 3 </em>/
}
/<em> USER CODE END 3 </em>/
}</p>
<pre><code>
## 注意事项
注意不要用洗板水刷到数码管以及电池盒这类橡胶材料的
我干脆直接刷白了,刷不干净也不要再刷了。不然整个腐蚀了。焊接完贴片的就可以洗下板子
## 实物图
![image.png](//image.lceda.cn/oshwhub/6ae2484a15014ab6b102f2be19c704d3.png)</code></pre>
评论(0)