本應用筆記介紹如何使用MAXQ2000微控制器控制直流風扇速度,并通過熱敏電阻或i按鈕?監測溫度。
介紹
MAXQ2000具有眾多的特性,可以創建多種有用的應用,例如通過脈寬調制(PWM)控制風扇的速度。MAXQ2000的眾多特性包括帶PWM和串行外設接口(SPI)的定時器。?)和1-Wire功能。本應用筆記描述了如何使用MAXQ2000驅動風扇,并通過PWM實時改變風扇速度。該過程需要使用Maxim的另一種產品,即MAX1407多通道數據采集系統(DAS)。利用SPI,MAXQ2000可以與MAX1407通信(包含16位模數轉換器[ADC]和數模轉換器[DAC])。作為使用熱敏電阻的替代方法,MAXQ2000的1-Wire總線主控器可與溫度i按鈕(DS1920)配合使用。
概述
該程序在MAXQ2000評估板(修訂版B)上運行,借助溫度i按鈕(DS1920)或外部熱敏電阻。MAXQ2000評估板(修訂版B)包括一個LCD顯示屏、兩個按鈕、一個MAX1407 ADC、兩個UART、三個定時器、1-Wire和許多其他特性。程序使用LCD顯示屏、按鈕和MAX1407或1-Wire讀取溫度。此外,還需要直流風扇和適用的驅動電路、熱敏電阻和電源。MAXQ2000評估板將驅動外部電路控制風扇速度。LCD顯示屏顯示當前溫度,并定期從攝氏度到華氏度變化。按鈕允許用戶更改風扇的低速和全速溫度閾值。
檢測溫度的默認方法是使用i按鈕,但如果正確的i按鈕不可用或通信出現問題,程序通過MAXQ2000的SPI使用熱敏電阻來獲取溫度讀數。i按鈕(DS1920)是一款溫度檢測1-Wire器件。該程序將RL1005-5744-103-SA熱敏電阻與MAX1407配合使用。
讀取溫度后,將顯示被發送到LCD,并根據溫度讀數調整PWM占空比。
程序中有兩個閾值,最低溫度和最高溫度。如果溫度低于最低溫度閾值,風扇關閉;如果高于最大溫度閾值,則風扇設置為其最大速度。如果溫度介于最小閾值和最大閾值之間,則速度與兩個溫度閾值之間的分數距離成正比。
兩個閾值可通過標記為SW4和SW5的兩個按鈕進行配置。SW4 切換閾值以更改 - 基本或最大值。為更改選擇的閾值顯示在數值數據和 C/F 字符之間。下劃線表示將更改基本閾值,而高劃線表示將更改最大閾值。SW5 將當前選擇的閾值增加 1。每更改一個閾值,就會重新計算風扇速度。
該應用需要使用MAXQ2000上三個定時器中的兩個。定時器1專用于通過熱敏電阻定期檢查溫度,而定時器0使用P6.5的PWM輸出來控制風扇。定時器1不能用于PWM,因為它具有與MAX1407通信期間使用的硬件資源。
硬件設置
圖1.將熱敏電阻硬件連接到MAXQ2000評估板。
首先,使用RL1005-5744-103-SA熱敏電阻和10kΩ電阻構建熱敏電阻設置。這是通過首先將J7引腳7和8連接到熱敏電阻的一側來完成的。在熱敏電阻的另一側連接J1引腳7的導線。接下來,將J10的引腳1和地之間的7kΩ電阻連接(J71的引腳72或2接地)。如果使用i按鈕選項,則應將i按鈕放置在i按鈕夾DS9094FS(或類似器件)中,該夾子應焊接到電路板的1-Wire部分。接下來,通過打開 SW6.2 和 SW6.5 啟用按鈕。液晶顯示屏連接到J3。為了通過熱敏電阻讀取溫度,打開SW3的全部,以便與MAX1407通信。MAX1407的ADC部分將來自熱敏電阻的模擬信號轉換為可用于計算溫度的數字值。使用 i 按鈕需要 JU7 和 JU8 上的跳線以及 i按鈕夾。
圖2.用于將 PWM 輸出連接到風扇的可能硬件設置。
解釋軟件定義和包含
#define PWMFREQ 1000 // Change this for desired PWM frequency #define CPUFREQ 13500000 // Change this to match current clock // frequency #define MIN_TICKS 0 // Minimum number of timer ticks // for fan to overcome static friction #define POLLING_INTERVAL 500 // Number of milliseconds between // temperature checks
以下是編譯時要檢查的一些基本定義:
#define LCD0_PATTERN_C 0x039 #define LCD0_PATTERN_F 0x071 #define LCD_PATTERN_0 0x03F #define LCD_PATTERN_1 0x006 #define LCD_PATTERN_2 0x05B #define LCD_PATTERN_3 0x04F #define LCD_PATTERN_4 0x066 #define LCD_PATTERN_5 0x06D #define LCD_PATTERN_6 0x07D #define LCD_PATTERN_7 0x007 #define LCD_PATTERN_8 0x07F #define LCD_PATTERN_9 0x067 int PATTERNS[] = { LCD_PATTERN_0, LCD_PATTERN_1, LCD_PATTERN_2, LCD_PATTERN_3, LCD_PATTERN_4, LCD_PATTERN_5,LCD_PATTERN_6, LCD_PATTERN_7, LCD_PATTERN_8, LCD_PATTERN_9 };
使用上述定義,數字的LCD顯示非常容易。LCD 圖案是預定義的,然后添加到名為 PATTERNS 的數組中以供檢索。顯示某個數字就像:
LCD2 = PATTERNS[desired_digit];
定時器 1 初始化
T2V1 = 0xFFFF - (CPUFREQ/128/1000*POLLING_INTERVAL); // Set reload value. T2R1 = T2V1; // Set current timer value T2C1 = 0x00000; // Set compare value T2CFG1_bit.T2DIV = 7 // Set Divide mode to Divide by 128 T2CNA1_bit.TR2 = 1; // Start Timer T2CNA1_bit.T2POL0 = 1; // Set polarity high T2CNA1_bit.ET2 = 1; // Enable timer interrupts T2CNB1_bit.T2OE1 = 1; // Enable timer output IMR_bit.IM4 = 1; // Enable interrupts from module 4
定時器1負責定期檢查溫度。初始值 (T2V1) 的設置取決于溫度輪詢之間的時間長度(以毫秒 (POLLING_INTERVAL) 為單位)。在兩行不同的行上完成計算的原因是防止值變得太大而寄存器無法處理。T2R1 指定計時器在達到 65,535 時返回的值。T2C1 設置為低于重新加載值 (T2R0) 以確保它永遠不會生成中斷。T2CFG1 是配置許多計時器選項的寄存器。計時器的時鐘分頻設置為將系統時鐘除以 128,這意味著系統時鐘的每 128 個周期相當于計時器的 1 個周期。系統時鐘也可以除以,然后將 2 個計時器周期的時間量(以 1 的冪為單位)相乘。T2CNA1 是定時器 0 本身的寄存器。這將啟動定時器運行并啟用定時器中斷,而 T2CNB1 使能定時器的輸出。最后,啟用模塊 4 中的中斷。
定時器 0 初始化
T2V0 = 0xFFFF - (CPUFREQ / PWMFREQ); // Set current timer value T2R0 = T2V0; // Set reload value T2C0 = T2R0+1; // Set compare value to reload value +1 T2CNA0_bit.T2OE0 = 0; // Turns PWM output off T2CNA0_bit.T2POL0 = 0; // Changes polarity of PWM so it starts "off" T2CNA0_bit.T2OE0 = 1; // Turns PWM output on T2CFG0 = 0x00; // Set timer divide at 1
下一個要初始化的項目是定時器0,它控制PWM,并通過它控制風扇。計時器 0 是一個 16 位計時器,與計時器 1 完全相同。當定時器達到比較值時(即,當 T2V0 == T2C0 時),端口引腳狀態反轉。當定時器重新加載時,端口引腳也會反轉(圖3)。T2V0 設置計時器的初始值,而 T2R2 設置重新加載值,兩者都設置為 0xFFFF - (CPU 頻率/PWM 頻率)。此計算用于更輕松地將代碼移植到具有不同 CPU 時鐘速度的系統或更改所需的 PWM 頻率。
圖3.端口引腳圖。
子選項 T2EO0 可確保 PWM 的輸出關閉,以便改變極性 (T2POL0)。當極性開關設置為0時,PWM的啟動狀態為關閉;如上圖所示。T2CFG0 確保計時器不會劃分系統時鐘,并告訴計時器將 T2V0 與 T2C0 進行比較。
getADCReading負責將模擬信號轉換為數字值。下面列出了從熱敏電阻讀取溫度的步驟。
sendSPI(RD_ADC); // Read the 1407's ADC register. spiData = sendSPI(0x0FF); spiData |= 0x01; // Set the start conversion bit. sendSPI(WR_ADC); // Write the new value back into sendSPI(spiData); // the 1407 register. do { sendSPI(RD_STAT); // Read the 1407's Status register. spiData = sendSPI(0x0FF); } while((spiData & 0x02) == 0x00); // Bit 1 indicates AtoD conversion complete. sendSPI(RD_DATA); // Send the command to read the 1407's Data register. SPICF = 0x04; // Set up SPI to 16-bit mode. spiData = sendSPI(0x0FFFF); // Read the Data register. SPICF = 0x00; // Put SPI back into 8-bit mode. return spiData;
液晶屏初始化
//LCRA_bit.FRM = 7; // Set up frame frequency. //LCRA_bit.LCCS = 1; // Set clock source to HF/128. //LCRA_bit.DUTY = 0; // Set up static duty cycle. //LCRA_bit.LRA = 0; // Set R-adj to 0. //LCRA_bit.LRIGC = 1; // Select external LCD drive power. LCRA = 0x03E0; // Do all configuration changes at once LCFG_bit.PCF = 0x0F; // Set up all segments as outputs. LCFG_bit.OPM = 1; // Set to normal operation mode. LCFG_bit.DPE = 1; // Enable display. LCD1 = 0x08;
為了顯示當前溫度,您必須打開LCD顯示屏。前五行被注釋掉了代碼,因為它們已被壓縮成一個語句。LCRA是LCD調整寄存器,它控制LCD顯示設置。更改 FRM 設置幀頻率;有了這個,您可以降低頻率并使用更少的功率。幀頻率是用于在 LCD 上顯示的電源頻率。LCCS 將 LCD 時鐘分頻更改為 128。Duty 將顯示設置為靜態,這意味著顯示輸出永遠不會更改。LCFG 是 LCD 配置寄存器。由于LCD只需要顯示,因此將PCF更改為0x0F會將LCD中的所有段設置為輸出。將 OPM 和 DPE 設置為 1 將分別打開 LCD 并啟用顯示器。LCD1 = 0x08顯示下劃線,表示按鈕將增加最小閾值。通過下面的兩行簡單操作,LCD顯示屏現在處于活動狀態并準備好顯示。
LCRA = 0x03E0; LCFG = 0xF3;
所有需要做的就是將值(如PATTERNS[數字]中的項目)加載到LCD寄存器中,這是非常快速和容易的。
按鈕初始化
EIE1 = 0x84; // enable interrupts 15 and 10 EIES1 = 0x84; // set up edge transitions IMR_bit.IM1 = 1; // turn on interrupts for module 1
現在是時候初始化按鈕了。與按鈕相關的中斷是 10 和 15,對應于 EIE4(外部中斷使能 80)上的 1 和 1(十六進制)。EIE1 啟用這些外部中斷,而 EIES1(外部中斷邊沿選擇 1)將中斷觸發器設置為下降沿(按下按鈕)。如果將其清除為零,則釋放按鈕是觸發中斷的操作。IMR 是中斷掩碼寄存器,它跟蹤允許中斷的模塊,IM1 啟用模塊 1 的中斷。
按鈕中斷
按鈕允許用戶更改base_temp和max_temp閾值。SW4(中斷 10)更改將要更改的閾值,而 SW5(中斷 15)將閾值增加 1。如果max_temp達到 149,則自身降低到 base_temp+1。如果base_temp比max_temp低 50,則base_temp變為 <>。
#pragma vector = 1 __interrupt void pushButtonInterrupt() { if (EIF1 & 0x04) //interrupt 10 // Modify which threshold to changed { if(last_state == 0) // Modify max_temp instead of base_temp { LCD1 = 0x01; // Change display to underscore last_state = 1; } else // Modify base_temp instead of max_temp { LCD1 = 0x08; last_state = 0; //Change display to overscore } }
“#pragma vector = 1”是一個編譯器指令,指示此函數處理來自模塊 1 的任何中斷,模塊 <> 是外部中斷的來源。
pushButtonInterrupt 函數首先檢查觸發了哪個中斷。中斷 10 0x04中斷,中斷 15 0x80。如果要更改閾值,則會更改切換并更新顯示。如果是閾值增量器,則閾值增加 1。如果閾值已達到其上限,則繞到下限。
if (EIF1 & 0x80) //interrupt 15 // Increase threshold for base or max temps { if(last_state == 0) { // Increase base_temp if(base_temp < (max_temp -1)) { ++base_temp; if(base_temp > 99) // If over 99, print 1 for 100's place LCD4 = 0x40; else LCD4 = 0; // Else print nothing LCD3 = getLCDDigit( (base_temp /10) %10); // Print 10's digit LCD2 = getLCDDigit(base_temp %10); // Print 1's digit } else if (base_temp == max_temp -1) // If base temp is going to equal max temp { // set base temp to 50 instead of increasing base_temp = 50; LCD3 = getLCDDigit(5); // Print 50 to display LCD2 = getLCDDigit(0); } }
如果正在修改閾值,它將檢查last_state的值。當last_state為零時,base_temp被修改;否則max_temp將被修改。如果base_temp正好小于 max_temp 50,則程序將base_temp包裝為任意值 <>。
else if (last_state == 1) // Increase max_temp { if(max_temp < 149) { ++max_temp; if(max_temp > 99) LCD4 = 0x40; //show 100s digit else LCD4 = 0; LCD2 = getLCDDigit(max_temp %10); // Show 1s digit LCD3 = getLCDDigit( (max_temp / 10) %10); // Show 10s digit } else { if (max_temp == 149) // If max_temp is at limit then cycle max_temp = base_temp+1; // to be 1 degree over base temp if(max_temp > 99) LCD4 = 0x40; // Clear 100s digit else LCD4 = 0; LCD2 = getLCDDigit(max_temp %10); // Show 1s digit LCD3 = getLCDDigit( (max_temp / 10) %10); // Show 10s digit } } xplier = ( (CPUFREQ/PWMFREQ) / (max_temp - base_temp)); // Re-calculate multiplier } EIF1 = 0; // Clear External Interrupt Flag
按鈕中斷的這一部分更改max_temp閾值。它增加max_temp直到等于 149,然后它環繞起來變得比 base_temp 大 1。如果base_temp是65,那么max_temp將是66。
新增加/換行的值會短暫顯示在LCD上,以便用戶知道它已更改。下次計時器重新加載時,LCD 會變回溫度,并使用溫度更新顯示。
更改閾值后,將重新計算 xplier。xplier 是確定 T2C1 值的乘數。在按鈕中斷完成之前,它會清除 EIF1(外部中斷標志 1),以便為下一次中斷做好準備。
定時器 1 中斷
#pragma vector = 4 __interrupt void timer1Interrupt() { T2CNA1_bit.TR2 = 0; // Stop the timer. if(++count == 20) // Change the units occasionally. { count = 0; celsius = ~celsius; } readTemp(); //Get and display the current temperature. T2CNB1_bit.TF2 = 0; //Clear the overflow flag. T2CNB1_bit.TCC2 = 0; //Clear the overflow flag. // output an 'alive' blip with the first decimal point, LCD0.7 if (count & 1) LCD0 = LCD0 | 0x80; else LCD0 = LCD0 & 0x7F; T2CNA1_bit.TR2 = 1; // Start the timer. }
注意:如果還使用了定時器 2,則此函數將針對定時器 2 的中斷運行,因為定時器 2 也位于模塊 4 中。
將 TR2 設置為 0 將關閉計時器,然后檢查計數是否等于 20。計數用于定期將顯示從華氏度更改為攝氏度。readTemp() 調用負責讀取溫度的函數。readTemp 完成后,將清除指示可能的計時器溢出(TF2 和 TCC2)的標志。if 語句檢查計數基本上會在每次檢查溫度時創建一個“刻度”。此“勾號”是啟用或禁用單位字符左側的小數點。這對于了解檢查的頻率和速度以及溫度保持不變時運行非常有用。最后,計時器再次啟動。
功能
readTemp() 函數負責讀取和顯示溫度,以及驅動 PWM。
if(readiButtonTemp(&temp)==FALSE) // Check to see if the device can be found { // If there is no iButton device found adc = getADCReading(); // Read the thermistor value. temp = convertToTemp(adc); // Convert this value to degrees Celsius. } showTemp(temp); // Display temperature drivePWM(temp); // Update fan speed
它做的第一件事是嘗試從1-Wire或i按鈕器件獲取溫度。如果沒有有效的1-Wire溫度器件或通信中發生錯誤,則使用熱敏電阻讀取溫度。讀取溫度后,通過調用函數 showTemp,將 LCD 顯示屏更新為最新讀數。之后,驅動PWM相應地改變風扇的速度。
float convertToTemp(unsigned int adc) { double temp = -0.00135477 * (double)adc + 69.17; return (float)temp; }
convertToTemp將MAX1407的讀數轉換為攝氏度。請注意,該公式給出了接近室溫的近似值,并且不補償熱敏電阻隨溫度的非線性變化。
void showTemp(int temp) { LCFG_bit.DPE = 0; // disable display. // Clear the display of everything but the temperature units (F or C). LCD0 = ( (celsius == 0) ? LCD0_PATTERN_F : LCD0_PATTERN_C); // clear the digits LCD1 = LCD2 = LCD3 = LCD4 = 0; if(last_state == 0) // Display correct threshold being edited LCD1 = 0x08; else LCD1 = 0x01;
showTemp 函數做的第一件事是將單位系統寫入顯示器,然后通過將 LCD 寄存器設置為 0 來清除顯示器中的所有數字。然后,它顯示表示要通過按鈕編輯的閾值的過劃線或下劃線。
if(celsius == 0) { temp = CtoF(temp); // Convert to Fahrenheit } if (temp > 199) // If temperature is 200+ { temp = temp % 200; } if(temp > 99) // If the temperature is 100+ { show100s(); // Show a '1' on the LCD. temp -= 100; // Adjust the temperature variable. } LCD3 = getLCDDigit(temp / 10); // show the 10's place on the LCD LCD2 = getLCDDigit(temp % 10); // show the 1's place on the LCD LCFG_bit.DPE = 1; // Enable display.
如果當前單位系統為華氏度,則溫度通過函數 CtoF 轉換為華氏度。如果 temp 超過 100,則顯示 1 表示 100 的數字,并在顯示屏上顯示其他數字。
drivePWM是通過改變PWM比較值來設置風扇功率的功能。
T2CNA0_bit.TR2 = 0; // Turn timer off T2CNA0_bit.T2OE0 = 0; // Turns PWM output off T2CNA0_bit.T2POL0 = 0; // Changes polarity of PWM so that it starts out off T2CNA0_bit.T2OE0 = 1; // Turns PWM output on T2V0 = T2R0; // Manually reload timer value
此序列關閉風扇,然后手動重新加載計時器值。下面,代碼設置風扇的實際速度。如果溫度低于base_temp閾值,則風扇設置為可能的最低設置,否則它會檢查溫度是否高于最大閾值,在這種情況下,風扇設置為最高設置。最后,如果溫度在閾值之間,則根據度數計算風扇的速度。然后計時器重新打開。
圖4.頂部輸出正常,而底部跡線是可能的,如果計時器未停止并手動重新加載。
在更新比較值和重新加載值時禁用計時器以避免無意中反轉輸出非常重要。圖 4 顯示了兩種類型的輸出。頂部輸出是正常的,而底部的圖形是可能的,如果計時器沒有停止并手動重新加載。發生的情況是,新的比較值高于當前定時器值,當定時器最終達到新的比較值時,端口引腳狀態反轉,定時器繼續。但是,由于狀態變化是不需要的,因此它具有反轉定時器輸出極性的效果。
if(temp <= base_temp) // Below this temperature the fan is off { // Force port pin P6.5 low PO6 &= 0xDF; } else { if( temp >= max_temp ) // Beyond this temperature the fan is full speed { // Force port pin P6.5 high PO6 |= 0x20; } else // Set the fan speed according to temperature { T2C0 = 65535 - ((temp - base_temp) * xplier); if( (65535 - T2C0) < MIN_TICKS) T2C0 = 65535 - MIN_TICKS; T2CNA0_bit.TR2 = 1; // Restart timer } }
高于base_temp閾值的每一度都有自己的速度,該速度基于所需的PWM占空比和兩個閾值之間的范圍。MIN_TICKS是最小定時器周期數,PWM 輸出必須很高才能使風扇以最低速度運行。風扇速度隨著溫度的升高而線性增加。速度變化率僅在閾值變化時發生變化。例如,base_temp 和 max_temp 的默認閾值分別為 75 和 85。這意味著 xplier(每度風扇速度的增加)為 1350。如果閾值分別更改為 75 和 90,則 xplier 將為 900,這意味著每個度數的風扇速度增加較少。效率越低,風扇越平滑。
void main() { initTimer1(); // Initialize timer1 for thermistor/iButton polling initTimer0(); // Initialize timer0 for PWM output init1407(); // Initialize the 1407 initOW(); // Initialize the 1-Wire subsystem initLCD(); // Initialize LCD display initPushButtons(); // Initialize pushbuttons __enable_interrupt(); // Enable global interrupts while(1); }
main 函數調用初始化函數并啟用全局中斷。程序本身是中斷驅動的,因此它會處于 while 循環中,直到中斷觸發。
結論
MAXQ2000是一款高性能微控制器,具有許多有用的功能。溫度驅動的風扇控制應用是使用MAXQ2000的PWM、1-Wire和SPI功能的一個很好的例子。當這些功能與按鈕和LCD顯示屏等交互式元素相結合時,可能的應用數量幾乎是無限的。
審核編輯:郭婷
-
微控制器
+關注
關注
48文章
7559瀏覽量
151471 -
轉換器
+關注
關注
27文章
8707瀏覽量
147236 -
定時器
+關注
關注
23文章
3250瀏覽量
114865
發布評論請先 登錄
相關推薦
評論