|

楼主 |
发表于 2022-1-26 19:43:31
|
显示全部楼层
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/***********************************************
* 函数名:DisplayMain(void)
* 作 用:显示主屏幕
* 参 数:无
* 返 回:无
************************************************/
void DisplayMain(void)
{
uint8_t i;
OLED_Clear(0,4);
for(i=0; i<5; i++){
OLED_ShowHanzi(i*16+24,0,i);
}
OLED_ShowStr(0,2,"You Aer Wellcome",16);
HAL_Delay(2000);
}
/***********************************************
* 函数名:Display_Temp(uint8_t n)
* 作 用:显示温湿度数据
* 参 数:n(传感器号0~2)
* 返 回:无
************************************************/
void DisplayTemp(uint8_t n)
{
OLED_Clear(0,4);
switch(n){
case 0:
OLED_ShowHanzi(0,0,20); // 顶
OLED_ShowHanzi(16,0,23); // 部
break;
case 1:
OLED_ShowHanzi(0,0,21); // 底
OLED_ShowHanzi(16,0,23); // 部
break;
case 2:
OLED_ShowHanzi(0,0,22); // 外
OLED_ShowHanzi(16,0,23); // 部
break;
case 3:
OLED_ShowHanzi(0,0,5); // 设
OLED_ShowHanzi(16,0,6); // 定
OLED_ShowNum(70,0,Temp,2,16); // 显示温度整数
OLED_ShowStr(88,0,".0",16);
}
OLED_ShowHanzi(32,0,3); // 温
OLED_ShowHanzi(48,0,7); // 度
OLED_ShowChar(110,0,127,16); // 显示温度符号
if(n<3){
OLED_ShowNum(70,0,DHT[2][n],2,16); // 显示温度整数
OLED_ShowChar(88,0,'.',16);
OLED_ShowChar(97,0,DHT[3][n]+48,16);// 显示温度小数
OLED_ShowHanzi(32,2,13); // 湿
OLED_ShowHanzi(48,2,7); // 度
OLED_ShowNum(70,2,DHT[0][n],2,16); // 显示湿度整数
OLED_ShowChar(88,2,'.',16);
OLED_ShowChar(97,2,DHT[1][n]+48,16);// 显示湿度小数
OLED_ShowChar(110,2,'%',16); // 显示湿度符号
}
}
/***********************************************
* 函数名:Device_Error()
* 作 用:温湿度传感器数据错误,恒温箱无法加热
* 参 数:无
* 返 回:无
************************************************/
void Device_Error(void)
{
OLED_Clear(0,4);
OLED_ShowHanzi(20,0,26); // 传
OLED_ShowHanzi(36,0,27); // 感
OLED_ShowHanzi(52,0,28); // 器
OLED_ShowHanzi(68,0,29); // 错
OLED_ShowHanzi(84,0,30); // 误
OLED_ShowHanzi(8,2,2); // 恒
OLED_ShowHanzi(24,2,3); // 温
OLED_ShowHanzi(40,2,4); // 箱
OLED_ShowHanzi(56,2,33); // 无
OLED_ShowHanzi(72,2,34); // 法
OLED_ShowHanzi(88,2,35); // 加
OLED_ShowHanzi(104,2,36); // 热
}
/***********************************************
* 函数名:DHT_Error(uint8_t n)
* 作 用:温湿度传感器数据错误
* 参 数:n 0~2
* 返 回:无
************************************************/
void DHT_Error(uint8_t n)
{
OLED_Clear(0,2);
if(0==n)
OLED_ShowHanzi(0,0,20); // 顶
else if(1==n)
OLED_ShowHanzi(0,0,21); // 底
else
OLED_ShowHanzi(0,0,22); // 外
OLED_ShowHanzi(16,0,23); // 部
OLED_ShowHanzi(32,0,26); // 传
OLED_ShowHanzi(48,0,27); // 感
OLED_ShowHanzi(64,0,28); // 器
OLED_ShowHanzi(80,0,31); // 故
OLED_ShowHanzi(96,0,32); // 障
}
/***********************************************
* 函数名:DHT_GetVal(void)
* 作 用:同时读取三个温湿度传感器的数据,存放在全局数组DHT中
* 参 数:n=0~2(顶部、底部、外部三个传感器)
* 返 回:0=读取成功,1=读取失败
************************************************/
uint8_t DHT_GetVal(uint8_t n)
{
uint8_t i,j,dat,retry;
switch(n){
case 0:
DHT_GpioDir(0); //总线切换到输出模式
HAL_GPIO_WritePin(GPIOB, DHT11_1_Pin, GPIO_PIN_RESET); //拉低DQ,发送启动信号
HAL_Delay(20); //等待18~30ms
HAL_GPIO_WritePin(GPIOB, DHT11_1_Pin, GPIO_PIN_SET); //DQ=1
DelayUs(20); //主机拉高20~40us
DHT_GpioDir(1); //总线切换到输入模式
retry = 100;
while((!HAL_GPIO_ReadPin(GPIOB, DHT11_1_Pin))&&retry){
retry--;
DelayUs(1);
}
if(retry==0){ //低电平响应信号超时
return 1;
}
retry = 100;
while((HAL_GPIO_ReadPin(GPIOB, DHT11_1_Pin))&&retry){
retry--;
DelayUs(1);
}
if(retry==0){ //高电平响应信号超时
return 1;
}
for(j=0; j<5; j++){ //持续读入五个字节数据
dat = 0;
for(i=0; i<8; i++){
retry = 100;
while((0==HAL_GPIO_ReadPin(GPIOB, DHT11_1_Pin))&&retry>0){
retry--;
DelayUs(1);
}
retry = 100;
while((HAL_GPIO_ReadPin(GPIOB, DHT11_1_Pin)>0)&&retry>0){
retry--;
DelayUs(1);
}
dat<<=1; //左移一位
if(retry<80)
dat|=0x01;
}
DHT[j][n] = dat;
}
break;
case 1:
DHT_GpioDir(0); //总线切换到输出模式
HAL_GPIO_WritePin(GPIOB, DHT11_2_Pin, GPIO_PIN_RESET); //拉低DQ,发送启动信号
HAL_Delay(20); //等待18~30ms
HAL_GPIO_WritePin(GPIOB, DHT11_2_Pin, GPIO_PIN_SET); //DQ=1
DelayUs(20); //主机拉高20~40us
DHT_GpioDir(1); //总线切换到输入模式
retry = 100;
while((!HAL_GPIO_ReadPin(GPIOB, DHT11_2_Pin))&&retry){
retry--;
DelayUs(1);
}
if(retry==0){ //低电平响应信号超时
return 1;
}
retry = 100;
while((HAL_GPIO_ReadPin(GPIOB, DHT11_2_Pin))&&retry){
retry--;
DelayUs(1);
}
if(retry==0){ //高电平响应信号超时
return 1;
}
for(j=0; j<5; j++){ //持续读入五个字节数据
dat = 0;
for(i=0; i<8; i++){
retry = 100;
while((0==HAL_GPIO_ReadPin(GPIOB, DHT11_2_Pin))&&retry>0){
retry--;
DelayUs(1);
}
retry = 100;
while((HAL_GPIO_ReadPin(GPIOB, DHT11_2_Pin)>0)&&retry>0){
retry--;
DelayUs(1);
}
dat<<=1; //左移一位
if(retry<80)
dat|=0x01;
}
DHT[j][n] = dat;
}
break;
case 2:
DHT_GpioDir(0); //总线切换到输出模式
HAL_GPIO_WritePin(GPIOB, DHT11_3_Pin, GPIO_PIN_RESET); //拉低DQ,发送启动信号
HAL_Delay(20); //等待18~30ms
HAL_GPIO_WritePin(GPIOB, DHT11_3_Pin, GPIO_PIN_SET); //DQ=1
DelayUs(20); //主机拉高20~40us
DHT_GpioDir(1); //总线切换到输入模式
retry = 100;
while((!HAL_GPIO_ReadPin(GPIOB, DHT11_3_Pin))&&retry){
retry--;
DelayUs(1);
}
if(retry==0){ //低电平响应信号超时
return 1;
}
retry = 100;
while((HAL_GPIO_ReadPin(GPIOB, DHT11_3_Pin))&&retry){
retry--;
DelayUs(1);
}
if(retry==0){ //高电平响应信号超时
return 1;
}
for(j=0; j<5; j++){ //持续读入五个字节数据
dat = 0;
for(i=0; i<8; i++){
retry = 100;
while((0==HAL_GPIO_ReadPin(GPIOB, DHT11_3_Pin))&&retry>0){
retry--;
DelayUs(1);
}
retry = 100;
while((HAL_GPIO_ReadPin(GPIOB, DHT11_3_Pin)>0)&&retry>0){
retry--;
DelayUs(1);
}
dat<<=1; //左移一位
if(retry<80)
dat|=0x01;
}
DHT[j][n] = dat;
}
}
if(DHT[4][n]==DHT[0][n]+DHT[1][n]+DHT[2][n]+DHT[3][n])
DHT[4][n] = 0;
else
DHT[4][n] = 1;
return 0;
}
/***********************************************
* 函数名:DHT_GpioDir(uint8_t dir)
* 作 用:同时设置三个温湿度传感器数据引脚的方向
* 参 数:dir 0=输出(写)、1=输入(读)
* 返 回:无
************************************************/
void DHT_GpioDir(uint8_t dir)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = DHT11_1_Pin|DHT11_2_Pin|DHT11_3_Pin;
if(1==dir){
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
}
else{
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
}
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void DelayUs(uint16_t us)
{
volatile uint8_t i,j;
for(i=0; i<us; i++)
for(j=0; j<1; j++);
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
[/code]
设定湿度的上限为60度,由于不具备制冷功能,所以设定湿度的下限为外部温度,在转动温度旋钮时,数值随转动方向增减,屏幕上显示出设定的温度,达到上下限时数值不再变动,同时会给出中文提示信息。
为了防止加热元件频繁开关操作,程序以30秒为一个操作时间段,即每隔30秒进行一次判断,执行加热或停止加热操作。在加热时,红色LED灯以2秒的周期闪烁。在关闭加热时,绿色的LED灯以2秒的周期闪烁。
电源旋钮向左旋到底,内部会响起一声铃声,同时切断全部电源。
四、使用效果
1、由于箱体内部的温度靠自然流动,且加热元件在底部,因此上部和下部有少许温差,上部会略高于底部,但对家庭使用应该无影响。
2、加热部分的功率较低,如果设定的温度与外部环境温度相差比较大的话,升温的时间会比较长,实测从13度升温到35度大约需要一个小时左右,如需快速升温,需要放置热水辅助初期升温。
3、为减少频繁开关加热元件,程序设置每隔30执行一次开通或关闭加热元件的操作,因此控制的温度会在设定温度高低1~2度范围内变动,这是考虑到家庭使用不需要那么精确,而且测温传感器也是使用最普通的廉价产品,本身也存在不小的测量误差。
五、总结
这个恒温箱实质上类似保温箱,主要用于小家庭发酵面粉或者酿制米酒,尤其是在冬天,可以确保正常发酵。在冬季也可以短时放置饭菜,以便迟到的家人能吃上热乎乎的饭菜。 |
|