當(dāng)前介紹的項(xiàng)目是基于 STM32F103ZET6 系列 MCU設(shè)計(jì)的數(shù)顯熱水器,通過顯示屏來顯示熱水器的溫度及其工作狀態(tài),通過 PT100 傳感器來檢測熱水器的溫度變化,并通過電加熱片實(shí)現(xiàn)加熱過程,以達(dá)到控制熱水器溫度的目的。
二、設(shè)計(jì)流程2.1 硬件選型STM32F103ZET6 系列 MCUOLED顯示屏PT100 溫度傳感器電加熱片繼電器2.2 軟件設(shè)計(jì)(1)顯示屏
(資料圖片僅供參考)
使用 OLED 顯示屏來顯示熱水器的溫度及其工作狀態(tài),通過 SPI接口與 STM32 芯片進(jìn)行通訊。設(shè)計(jì)溫度值及其單位、熱水器工作狀態(tài)等。
(2)溫度傳感器
使用 PT100 溫度傳感器來檢測熱水器內(nèi)部溫度的變化,并將數(shù)據(jù)通過 ADC轉(zhuǎn)換后,傳輸給 STM32 芯片,以實(shí)現(xiàn)對(duì)熱水器加熱過程的控制。
(3)電加熱片
使用電加熱片模擬熱水器加熱過程,通過繼電器控制電加熱片的通斷,以調(diào)節(jié)熱水器的溫度。
(4)控制系統(tǒng)
通過 STM32 芯片來實(shí)現(xiàn)對(duì)熱水器的控制,讀取溫度傳感器的數(shù)據(jù)。
三、代碼設(shè)計(jì)3.1 OLED顯示屏(1)SPI 接口初始化需要對(duì) STM32F103ZET6 的 SPI 接口進(jìn)行初始化配置,設(shè)置相關(guān)的時(shí)鐘和模式,使其能夠與 OLED 顯示屏進(jìn)行通訊。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); // 打開SPI3時(shí)鐘 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; // 設(shè)置 SPI 時(shí)鐘頻率為 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); // 關(guān)閉顯示 write_command(0xD5); // 設(shè)置時(shí)鐘分頻因子,震蕩頻率 write_command(0x80); // 分頻因子=1 ,震蕩頻率(fosc)=8MHz write_command(0xA8); // 設(shè)置驅(qū)動(dòng)路數(shù):MUX(復(fù)用方式) write_command(0x1F); // 1/32 duty (0x0F~0x3F) write_command(0xD3); // 設(shè)置顯示偏移 write_command(0x00); // 不偏移 write_command(0x40); // 設(shè)置顯示開始行[5:0], 對(duì)于設(shè)置了32行的液晶, // 這里的值為0表示從0行開始顯示 write_command(0x8D); // 對(duì)比度設(shè)置 write_command(0x14); // AHB參考電壓256等分 移位[3:0]100[n,1/256] write_command(0x20); // 水平方向上的尋址模式 write_command(0x00); // 垂直方向上的尋址模式 write_command(0xA1); // 設(shè)置段再映射 write_command(0xC0); // 設(shè)置COM掃描方向 write_command(0xDA); // 設(shè)置COM引腳硬件配置 write_command(0x12); write_command(0x81); // 對(duì)比度設(shè)置 write_command(0xBF); // 設(shè)置電荷泵電壓 write_command(0xD9); // 設(shè)置預(yù)充電周期 write_command(0xF1); write_command(0xDB); // 設(shè)置VCOMH電壓倍率 write_command(0x40); write_command(0xAF); // 打開顯示 ? OLED_Clear(); // 清屏 }
(3)OLED 顯示函數(shù)接下來編寫 OLED 顯示函數(shù),實(shí)現(xiàn)字符和數(shù)字的顯示功能。
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)結(jié)果顯示在代碼中調(diào)用 OLED_show_string 函數(shù)和 OLED_show_char 函數(shù)顯示數(shù)值和字符。
OLED_Init(); OLED_Clear(); OLED_show_string(0, 0, "HELLO WORLD!"); OLED_show_string(0, 16, "TEMP:20 C");
3.2 測溫代碼(1)引腳配置需要對(duì) STM32F103ZET6 的 IO 口進(jìn)行配置,將用于連接 PT100 溫度傳感器的引腳設(shè)置為輸入模式。
這里以 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 配置接下來需要對(duì) STM32F103ZET6 的 ADC 進(jìn)行初始化配置,使其能夠讀取 PT100 溫度傳感器輸出的電壓信號(hào)。
這里以 ADC1 通道5 作為讀取口,代碼如下:
ADC_InitTypeDef ADC_InitStructure; RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 設(shè)置 ADC 時(shí)鐘為 PCLK2 的 1/6 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 打開 ADC1 時(shí)鐘 ADC_DeInit(ADC1); // 初始化 ADC1 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 連續(xù)轉(zhuǎn)換模式 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)溫度轉(zhuǎn)換函數(shù)根據(jù) PT100 溫度傳感器輸出電壓與溫度的關(guān)系,可使用線性函數(shù)計(jì)算出溫度值。
轉(zhuǎn)換公式如下:
Rt = (Vref - Vpt) / Ipt // Rt 為 PT100 的阻值,Vref 為基準(zhǔn)電壓,Vpt 為 PT100 輸出電壓,Ipt 為 PT100 驅(qū)動(dòng)電流 Temp = a * Rt + b // Temp 為溫度值,a 和 b 為經(jīng)過擬合后的系數(shù)
其中 Rt 的計(jì)算需要使用差分運(yùn)算放大器進(jìn)行轉(zhuǎn)換,這里不再贅述。假設(shè)已經(jīng)得到 Rt 值,則溫度轉(zhuǎn)換函數(shù)代碼如下:
float PT100_Get_Temperature(float Rt) { float a = 3.9083e-3f, b = -5.775e-7f, R0 = 100.0f; // 根據(jù)實(shí)際數(shù)據(jù)進(jìn)行擬合得到 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)數(shù)據(jù)采集根據(jù)差分放大器輸出的電壓值得到 PT100 溫度傳感器的阻值,再根據(jù)阻值計(jì)算出實(shí)際溫度,最后將溫度值通過串口打印出來。以下是數(shù)據(jù)采集代碼:
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); // 使能軟件觸發(fā) ADC 轉(zhuǎn)換 while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待轉(zhuǎn)換結(jié)束 adc_val = ADC_GetConversionValue(ADC1); // 讀取 ADC 轉(zhuǎn)換結(jié)果 voltage = (float)adc_val * 3.3f / 4096; // 計(jì)算基準(zhǔn)電壓 return voltage;}float PT100_Get_Rt(float Vpt){ float Rsource = 10e3f, Rpt = 100.0f; // Rsource 為差分放大器輸出電阻,Rpt 為 PT100 阻值 float Ipt = (3.3f - Vpt) / Rsource; // 計(jì)算 PT100 驅(qū)動(dòng)電流 float Rt = (3.3f - Vpt) / Ipt; // 根據(jù)歐姆定律計(jì)算出 PT100 阻值 return Rt;}void USART1_Send_Float(float f){ char buf[32]; sprintf(buf, "%.1f", f); // 轉(zhuǎn)換為字符串 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); // 計(jì)算 PT100 阻值 float Temp = PT100_Get_Temperature(Rt); // 根據(jù)阻值計(jì)算溫度 USART1_Send_Float(Temp); // 將溫度值打印到串口 delay_ms(500); } ...}
審核編輯 黃宇
標(biāo)簽: