當前介紹的項目是基于 STM32F103ZET6 系列 MCU設計的數顯熱水器,通過顯示屏來顯示熱水器的溫度及其工作狀態,通過 PT100 傳感器來檢測熱水器的溫度變化,并通過電加熱片實現加熱過程,以達到控制熱水器溫度的目的。
二、設計流程2.1 硬件選型STM32F103ZET6 系列 MCUOLED顯示屏PT100 溫度傳感器電加熱片繼電器2.2 軟件設計(1)顯示屏
(資料圖片僅供參考)
使用 OLED 顯示屏來顯示熱水器的溫度及其工作狀態,通過 SPI接口與 STM32 芯片進行通訊。設計溫度值及其單位、熱水器工作狀態等。
(2)溫度傳感器
使用 PT100 溫度傳感器來檢測熱水器內部溫度的變化,并將數據通過 ADC轉換后,傳輸給 STM32 芯片,以實現對熱水器加熱過程的控制。
(3)電加熱片
使用電加熱片模擬熱水器加熱過程,通過繼電器控制電加熱片的通斷,以調節熱水器的溫度。
(4)控制系統
通過 STM32 芯片來實現對熱水器的控制,讀取溫度傳感器的數據。
三、代碼設計3.1 OLED顯示屏(1)SPI 接口初始化需要對 STM32F103ZET6 的 SPI 接口進行初始化配置,設置相關的時鐘和模式,使其能夠與 OLED 顯示屏進行通訊。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); // 打開SPI3時鐘 SPI_InitTypeDef spi_init_type; spi_init_type.SPI_Direction = SPI_Direction_2Lines_FullDuplex; spi_init_type.SPI_Mode = SPI_Mode_Master; spi_init_type.SPI_DataSize = SPI_DataSize_8b; spi_init_type.SPI_CPOL = SPI_CPOL_Low; spi_init_type.SPI_CPHA = SPI_CPHA_1Edge; spi_init_type.SPI_NSS = SPI_NSS_Soft; spi_init_type.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; // 設置 SPI 時鐘頻率為 72 MHz / 32 = 2.25MHz spi_init_type.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI3, &spi_init_type); SPI_Cmd(SPI3, ENABLE);
(2)OLED 顯示屏初始化以下是 OLED 顯示屏的初始化代碼:
void OLED_Init(void) { GPIO_SetBits(GPIOB, GPIO_Pin_6); //RST SET GPIO_ResetBits(GPIOB, GPIO_Pin_6); //RST RESET GPIO_SetBits(GPIOB, GPIO_Pin_6); //RST SET ? write_command(0xAE); // 關閉顯示 write_command(0xD5); // 設置時鐘分頻因子,震蕩頻率 write_command(0x80); // 分頻因子=1 ,震蕩頻率(fosc)=8MHz write_command(0xA8); // 設置驅動路數:MUX(復用方式) write_command(0x1F); // 1/32 duty (0x0F~0x3F) write_command(0xD3); // 設置顯示偏移 write_command(0x00); // 不偏移 write_command(0x40); // 設置顯示開始行[5:0], 對于設置了32行的液晶, // 這里的值為0表示從0行開始顯示 write_command(0x8D); // 對比度設置 write_command(0x14); // AHB參考電壓256等分 移位[3:0]100[n,1/256] write_command(0x20); // 水平方向上的尋址模式 write_command(0x00); // 垂直方向上的尋址模式 write_command(0xA1); // 設置段再映射 write_command(0xC0); // 設置COM掃描方向 write_command(0xDA); // 設置COM引腳硬件配置 write_command(0x12); write_command(0x81); // 對比度設置 write_command(0xBF); // 設置電荷泵電壓 write_command(0xD9); // 設置預充電周期 write_command(0xF1); write_command(0xDB); // 設置VCOMH電壓倍率 write_command(0x40); write_command(0xAF); // 打開顯示 ? OLED_Clear(); // 清屏 }
(3)OLED 顯示函數接下來編寫 OLED 顯示函數,實現字符和數字的顯示功能。
void OLED_show_string(uint8_t x, uint8_t y, char *str) { uint8_t i = 0; while (str[i] != "") { OLED_show_char(x, y + i * 8, str[i]); ++i; } } ? void OLED_show_char(uint8_t x, uint8_t y, char ch) { uint8_t c = ch - 32; if (c >= 96) return; uint8_t* buffer = (uint8_t*)oled_buffer; uint8_t cx, cy; for(cy = 0; cy < 8; cy++) { uint8_t line = font[c][cy]; for (cx = 0; cx < 6; cx++) { if (line & 0x1) { buffer[(y + cy) * OLEDWIDTH + x + cx] = 1; } else { buffer[(y + cy) * OLEDWIDTH + x + cx] = 0; } line > >= 1; } } OLED_Draw_Pixel(x + 6, y, 0); OLED_Draw_Pixel(x + 6, y + 1, 0); OLED_Draw_Pixel(x + 6, y + 6, 0); OLED_Draw_Pixel(x + 6, y + 7, 0); }
(4)結果顯示在代碼中調用 OLED_show_string 函數和 OLED_show_char 函數顯示數值和字符。
OLED_Init(); OLED_Clear(); OLED_show_string(0, 0, "HELLO WORLD!"); OLED_show_string(0, 16, "TEMP:20 C");
3.2 測溫代碼(1)引腳配置需要對 STM32F103ZET6 的 IO 口進行配置,將用于連接 PT100 溫度傳感器的引腳設置為輸入模式。
這里以 PA0 引腳作為 PT100 傳感器的連接口(即 PT100 三線連接中的 R3 端),代碼如下:
GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉輸入模式 GPIO_Init(GPIOA, &GPIO_InitStructure);
(2)ADC 配置接下來需要對 STM32F103ZET6 的 ADC 進行初始化配置,使其能夠讀取 PT100 溫度傳感器輸出的電壓信號。
這里以 ADC1 通道5 作為讀取口,代碼如下:
ADC_InitTypeDef ADC_InitStructure; RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 設置 ADC 時鐘為 PCLK2 的 1/6 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 打開 ADC1 時鐘 ADC_DeInit(ADC1); // 初始化 ADC1 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 連續轉換模式 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); // 開啟 ADC1
(3)溫度轉換函數根據 PT100 溫度傳感器輸出電壓與溫度的關系,可使用線性函數計算出溫度值。
轉換公式如下:
Rt = (Vref - Vpt) / Ipt // Rt 為 PT100 的阻值,Vref 為基準電壓,Vpt 為 PT100 輸出電壓,Ipt 為 PT100 驅動電流 Temp = a * Rt + b // Temp 為溫度值,a 和 b 為經過擬合后的系數
其中 Rt 的計算需要使用差分運算放大器進行轉換,這里不再贅述。假設已經得到 Rt 值,則溫度轉換函數代碼如下:
float PT100_Get_Temperature(float Rt) { float a = 3.9083e-3f, b = -5.775e-7f, R0 = 100.0f; // 根據實際數據進行擬合得到 a、b 和 R0 的值 float Tem, delta; delta = pow(Rt / R0, 2) + a * (Rt / R0) + b; Tem = (delta > 0) ? (-R0*a + sqrt(delta)) / (2 * b) : 0; return Tem; }
(4)數據采集根據差分放大器輸出的電壓值得到 PT100 溫度傳感器的阻值,再根據阻值計算出實際溫度,最后將溫度值通過串口打印出來。以下是數據采集代碼:
float ADC_Get_Voltage(void){ float voltage = 0; uint16_t adc_val = 0; ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_239Cycles5); // 配置 ADC 通道5 ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 使能軟件觸發 ADC 轉換 while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待轉換結束 adc_val = ADC_GetConversionValue(ADC1); // 讀取 ADC 轉換結果 voltage = (float)adc_val * 3.3f / 4096; // 計算基準電壓 return voltage;}float PT100_Get_Rt(float Vpt){ float Rsource = 10e3f, Rpt = 100.0f; // Rsource 為差分放大器輸出電阻,Rpt 為 PT100 阻值 float Ipt = (3.3f - Vpt) / Rsource; // 計算 PT100 驅動電流 float Rt = (3.3f - Vpt) / Ipt; // 根據歐姆定律計算出 PT100 阻值 return Rt;}void USART1_Send_Float(float f){ char buf[32]; sprintf(buf, "%.1f", f); // 轉換為字符串 while (*buf) { USART_SendData(USART1, *buf); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); buf++; }}int main(void){ ... while (1) { float Vpt = ADC_Get_Voltage(); // 獲取差分放大器輸出電壓 float Rt = PT100_Get_Rt(Vpt); // 計算 PT100 阻值 float Temp = PT100_Get_Temperature(Rt); // 根據阻值計算溫度 USART1_Send_Float(Temp); // 將溫度值打印到串口 delay_ms(500); } ...}
審核編輯 黃宇
標簽: