本课程的主要目的是学习MSPM0L1306的ADC应用和DS18B20温度传感器单线协议。
本次学习实现按键报警,ADC-NTC温度采集,DS18B20温度传感器单线通讯以及温度显示及环境温差异常报警。
一、硬件学习
1、按键。
S1按键:PA18默认外部下拉,检测高电平有效,配置IO时需要内部下拉或悬空。课程1中有介绍,参考:
https://forum.eepw.com.cn/thread/381478/1
2、蜂鸣器。
本次使用蜂鸣器为低电平触发方式,IO使用PA6口,配置IO时需要内部上拉或悬空,硬件原理如下:
3、ADC热敏电阻。
由开发板原理图可以看出,NTC温度传感器接至了ADC通道9(PA15),如下:
NTC使用型号为TI的TMP6161DECT,其为正温度系数热敏电阻。详细规格可在TI官网中找到,链接如下:
使用热敏电阻,自然离不开其温度-电阻值数据表,通过规格书可直接找到其设计工具表。
点击Thermistor Design Tool (zip)下载,得到两个Excel表:
开发板热敏电阻接法为电压基准方式,固查看电压基准的Excel文档即可。(电流基准方式可自行学习)
文档中直接给出了温度/电阻值对应关系表。本次学习使用1℃档位的电阻值分压关系,通过ADC采样的AD值查表,得到对应NTC温度值。
原理图中,NTC接法:3.3V供电,电阻10K/NTC(10K)。
得出ADC电压关系为:Uadc = 3.3*RT/(10000+RT);
转换为ADC(12bit)采样AD值为:AD = Uadc*4096/3.3 = 4096*RT/(10000+RT)。
由此ADC采样获得AD值,通过AD/RT和RT/℃的对应关系,可通过查表方式得到AD值对应温度。温度/AD表和查询函数如下:
4、数码管显示器。
在课程2中,已对数码管显示器做了详细介绍,参考:
https://forum.eepw.com.cn/thread/381492/1
由于本次课程中使用了S1按键(PA18)和ADC通道9(PA15),固在课程2的基础上调整一下显示器接口:PA21-->DIO,PA16-->SCLK,PA17-->RCLK。
5、DS18B20温度传感器。
本次使用通信IO为PA11,由于开发板缺少了3V3,固将SW2接口飞线至了3V3,SW1跳线帽接至PA11。
DS18B20为单线通信方式,原理很简单,具体可参考大神作品:
https://forum.eepw.com.cn/thread/381543/1
6、总体接线图。
二、软件配置
1、按键。
2、蜂鸣器。
3、ADC通道9。
4、数码管显示器。
依次修改:DAT-->PA21,CLK-->PA16,RCK-->PA17。
5、DS18B20温度传感器。
6、配置好sysconfig后,编译更新代码。
三、代码实现
1、基础代码:按键、蜂鸣器、HC595配置和相关参数。
#define Key1Tmp DL_GPIO_readPins(USER_KEYS_PORT, USER_KEYS_KEY1_PIN) //KEY1 #define BEEP_ON DL_GPIO_clearPins(BEEP_PORT, BEEP_PIN6_PIN); //Beep ON #define BEEP_OFF DL_GPIO_setPins(BEEP_PORT, BEEP_PIN6_PIN); //Beep OFF #define HC595_DAT_H DL_GPIO_setPins(HC595_PORT, HC595_DAT_PIN) //HC595 data #define HC595_DAT_L DL_GPIO_clearPins(HC595_PORT, HC595_DAT_PIN) #define HC595_CLK_H DL_GPIO_setPins(HC595_PORT, HC595_CLK_PIN) //HC595 sclk #define HC595_CLK_L DL_GPIO_clearPins(HC595_PORT, HC595_CLK_PIN) #define HC595_RCK_H DL_GPIO_setPins(HC595_PORT, HC595_RCK_PIN) //HC595 rclk #define HC595_RCK_L DL_GPIO_clearPins(HC595_PORT, HC595_RCK_PIN) volatile uint8_t Disp_DX[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, //0 1 2 3 4 5 6 7 8 9 0x88, 0xA0, 0x83, 0xC6, 0xA7, 0xA1, 0x86, 0x8E, 0x8C, 0xFF, //A a b C c d E F P NC 0xBF, 0x7F, 0x9C, 0xA3, 0xC7, 0x89, 0xAB, 0xC1 }; //- . ^o^ o L H n U volatile uint16_t KeyBounce=0; //volatile uint16_t TmpVal3=0; volatile bool gCheckADC; volatile uint16_t gAdcResultVolts=0, gAdcNtcTempC=0, gDs18b20TempC=0, cycle=0;
2、NTC热敏电阻查表函数。
//NTC Temperature: Divider resistance:10k/RT(TMP6131DECT), Vref:3.3V, -20~+100℃ volatile uint16_t NTC_AD[121] = { 1750, 1757, 1763, 1770, 1776, 1783, 1789, 1796, 1803, 1809, 1816, 1822, 1829, 1835, 1842, 1849, 1855, 1862, 1868, 1875, 1881, 1888, 1894, 1901, 1908, 1914, 1921, 1927, 1934, 1940, 1947, 1953, 1960, 1966, 1973, 1979, 1986, 1992, 1999, 2005, 2012, 2018, 2024, 2031, 2037, 2044, 2050, 2057, 2063, 2069, 2076, 2082, 2088, 2095, 2101, 2108, 2114, 2120, 2126, 2133, 2139, 2145, 2152, 2158, 2164, 2170, 2177, 2183, 2189, 2195, 2201, 2207, 2214, 2220, 2226, 2232, 2238, 2244, 2250, 2256, 2262, 2268, 2275, 2281, 2287, 2293, 2299, 2304, 2310, 2316, 2322, 2328, 2334, 2340, 2346, 2352, 2358, 2363, 2369, 2375, 2381, 2387, 2392, 2398, 2404, 2410, 2415, 2421, 2427, 2432, 2438, 2444, 2449, 2455, 2460, 2466, 2472, 2477, 2483, 2488, 2494 }; //NTC temperature AD value Search int16_t NTC_AD_Search(uint16_t TempAD) { uint8_t start = 0; uint8_t end = 120; uint8_t mid = 0; int16_t temp = 0; uint16_t tmp; while(start <= end) { mid = (start+end)/2; if((TempAD >= NTC_AD[mid]) && (TempAD <= NTC_AD[mid+1])) { tmp = (TempAD - NTC_AD[mid]) << 1; if(tmp >= (NTC_AD[mid+1] - NTC_AD[mid])) mid = mid+1; break; } else if((TempAD < NTC_AD[mid]) && (TempAD >= NTC_AD[mid-1])) { tmp = (NTC_AD[mid] - TempAD) << 1; if(tmp > (NTC_AD[mid] - NTC_AD[mid-1])) mid = mid-1; break; } if(TempAD < NTC_AD[mid]) end = mid-1; else if(TempAD > NTC_AD[mid]) start = mid+1; } temp = mid-20; return temp; }
3、ADC中断回调和温度读取转换。
void ADC12_0_INST_IRQHandler(void) { switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) { case DL_ADC12_IIDX_MEM0_RESULT_LOADED: gCheckADC = true; break; default: break; } } void ReadNtcTemprature(void) { while (false == gCheckADC) { __WFE(); } gAdcResultVolts = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0); gAdcNtcTempC = NTC_AD_Search(gAdcResultVolts); gCheckADC = false; }
4、HC595显示函数。
//HC595 write 8bit void HC595_WriteData(uint8_t data) { uint8_t i; for(i=0; i<8; i++) { if((data & 0x80) > 0) { HC595_DAT_H; }else { HC595_DAT_L; } data <<= 1; HC595_CLK_L; delay_cycles(8); HC595_CLK_H; delay_cycles(8); } } //HC595 display enable void Display_Out(void) { HC595_RCK_L; delay_cycles(8); HC595_RCK_H; delay_cycles(8); } //HC595 write break and bit void HC595_SendData(uint8_t disp_num, uint8_t disp_bit) { HC595_WriteData(disp_num); HC595_WriteData(1 << disp_bit); Display_Out(); } //HC595 display 4 LEDs void Display_4bData(uint16_t data) { uint16_t temp; uint8_t num_q,num_b,num_s,num_g; temp = data; num_q = temp / 1000; num_b = temp / 100 % 10; num_s = temp / 10 % 10; num_g = temp % 10; HC595_SendData(Disp_DX[num_q],3); HC595_SendData(Disp_DX[num_b],2); HC595_SendData(Disp_DX[num_s],1); HC595_SendData(Disp_DX[num_g],0); } //HC595 display 8 LEDs void Display_8bData(uint16_t dataH, uint16_t dataL) { uint16_t tempH,tempL; uint8_t num_q,num_b,num_s,num_g; tempH = dataH; num_q = tempH / 1000; num_b = tempH / 100 % 10; num_s = tempH / 10 % 10; num_g = tempH % 10; HC595_SendData(Disp_DX[num_q],7); HC595_SendData(Disp_DX[num_b],6); HC595_SendData(Disp_DX[num_s],5); HC595_SendData(Disp_DX[num_g],4); tempL = dataL; num_q = tempL / 1000; num_b = tempL / 100 % 10; num_s = tempL / 10 % 10; num_g = tempL % 10; HC595_SendData(Disp_DX[num_q],3); HC595_SendData(Disp_DX[num_b],2); HC595_SendData(Disp_DX[num_s],1); HC595_SendData(Disp_DX[num_g],0); } //HC595 display temperature: 3 integers, 1 decimal point: xxx.x void Display_D3P1TempC(uint16_t data) { uint16_t temp; uint8_t num_q,num_b,num_s,num_g; temp = data; num_q = temp / 1000; num_b = temp / 100 % 10; num_s = temp / 10 % 10; num_g = temp % 10; HC595_SendData(Disp_DX[num_q],3); HC595_SendData(Disp_DX[num_b],2); HC595_SendData(Disp_DX[num_s]&0x7F,1); HC595_SendData(Disp_DX[num_g],0); } //HC595 display temperature: 3 integers + 2 integers, 1 decimal point: xxxC + xx.xC void Display_AD3P1TempC(uint16_t dataH, int16_t dataL) { uint16_t tempH,tempL; uint8_t num_q,num_b,num_s,num_g; tempH = dataH; num_b = tempH / 100 % 10; num_s = tempH / 10 % 10; num_g = tempH % 10; if(num_b > 0) HC595_SendData(Disp_DX[num_b],7); HC595_SendData(Disp_DX[num_s],6); HC595_SendData(Disp_DX[num_g],5); HC595_SendData(Disp_DX[13],4); tempL = dataL; num_b = tempL / 100 % 10; num_s = tempL / 10 % 10; num_g = tempL % 10; HC595_SendData(Disp_DX[num_b],3); HC595_SendData(Disp_DX[num_s]&0x7F,2); HC595_SendData(Disp_DX[num_g],1); HC595_SendData(Disp_DX[13],0); }
5、DS18B20读写函数。
//delay n us void delay_us(uint32_t nus) { delay_cycles(nus*32); } //DS18B20 Reset void DS18B20_Reset(void) { DS18B20_OutPut_Mode; DS18B20_DIO_OUT_L; delay_us(750); //750us DS18B20_DIO_OUT_H; delay_us(15); //15us } //DS18B20 ACK: 0 - true, 1 - false uint8_t DS18B20_Check(void) { uint8_t retry = 0; uint8_t rval = 0; while ((DS18B20_DIO_IN > 0) && (retry < 200)) //wait 200us { retry++; delay_us(1); } if (retry >= 200) { rval = 1; } else { retry = 0; while (!(DS18B20_DIO_IN > 0) && (retry < 240)) //wait 240us { retry++; delay_us(1); } if (retry >= 240) rval = 1; } return rval; } //DS18B20 Initialization uint8_t DS18B20_Init(void) { DS18B20_Reset(); return DS18B20_Check(); } //DS18B20 read 1bit uint8_t DS18B20_Read_bit(void) { uint8_t data = 0; DS18B20_DIO_OUT_L; delay_us(2); DS18B20_DIO_OUT_H; delay_us(12); DS18B20_InPut_Mode; if (DS18B20_DIO_IN > 0) { data = 1; } delay_us(50); return data; } //DS18B20 read 1Byte uint8_t DS18B20_Read_Byte(void) { uint8_t i, b, data = 0; for (i = 0; i < 8; i++) { b = DS18B20_Read_bit(); //LSB -> MSB data |= b << i; } return data; } //DS18B20 write 1Byte void DS18B20_Write_Byte(uint8_t data) { uint8_t j; for (j = 1; j <= 8; j++) { if (data & 0x01) { DS18B20_DIO_OUT_L; //write 1 delay_us(2); DS18B20_DIO_OUT_H; delay_us(50); } else { DS18B20_DIO_OUT_L; //write 0 delay_us(50); DS18B20_DIO_OUT_H; delay_us(2); } data >>= 1; } } //DS18B20 start convert temperature void DS18B20_Start(void) { DS18B20_Reset(); DS18B20_Check(); DS18B20_Write_Byte(0xcc); //skip rom DS18B20_Write_Byte(0x44); //convert } //DS18B20 get temperature value, 0.1C ,return C = temp/10 uint16_t DS18B20_Get_Temperature(void) { uint8_t flag = 1; //Positive and Negative flag: Positive uint8_t TempL, TempH; uint16_t temp; DS18B20_Start(); //DS18B20 start convert DS18B20_Reset(); DS18B20_Check(); DS18B20_Write_Byte(0xcc); //skip rom DS18B20_Write_Byte(0xbe); //convert TempL = DS18B20_Read_Byte(); //LSB TempH = DS18B20_Read_Byte(); //MSB if (TempH > 7) { TempH = ~TempH; TempL = ~TempL; flag = 0; //Negative } temp = TempH; temp <<= 8; temp += TempL; //convert temperature if (flag == 0) { temp = (double)(temp+1) * 0.625; temp = -temp; } else { temp = (double)temp * 0.625; } return temp; }
6、main函数。
int main(void) { SYSCFG_DL_init(); NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN); while (1) { if(Key1Tmp) { KeyBounce ++; if(KeyBounce >= 2000) { //TmpVal3++; KeyBounce = 0; BEEP_ON; } //if(TmpVal3 >= 10000) TmpVal3 = 0; } else if(((float)gDs18b20TempC/10 - (float)gAdcNtcTempC) > 3.0) //DS18B20 temperature - NTC temperature > 3.0C { BEEP_ON; } else BEEP_OFF; //Display_4bData(TmpVal3); cycle++; if(cycle >= 8000) { cycle = 0; DL_ADC12_startConversion(ADC12_0_INST); ReadNtcTemprature(); //get NTC temperature DL_ADC12_enableConversions(ADC12_0_INST); gDs18b20TempC = DS18B20_Get_Temperature(); //get DS18B20 temperature } Display_AD3P1TempC(gAdcNtcTempC, gDs18b20TempC); //display NTC & DS18B20 temperature } }
四、显示效果
成果视频:
视频1:http://v.eepw.com.cn/video/play/id/15983
视频2:http://v.eepw.com.cn/video/play/id/15984
视频3:http://v.eepw.com.cn/video/play/id/15985
完结,共进!