本帖最后由 ricebucket 于 2020-7-12 22:10 編輯
作為51單片機初學者,制作一個電子鐘是不可缺少的實踐內容。周末用STC89C52RC+DS1302模塊+LCD1602顯示屏+DS18B20數字溫度傳感器實現了一個電子鐘,
顯示效果如下:
51hei圖片_20200712212615.jpg (74.99 KB, 下載次數: 69)
下載附件
2020-7-12 21:26 上傳
可以用串口命令修改RTC時鐘:
Annotation 2020-07-12 204341.png (19.12 KB, 下載次數: 74)
下載附件
2020-7-12 21:28 上傳
實踐過程中的一些經驗分享:
1、DS1302模塊的RST,CLK和IO口最好加上4.7k以上的上拉電阻,提高RTC數據讀寫可靠性,這個問題讓我花了不少時間,最后在Vcc2和IO引腳之間焊接了一個10k電阻解決問題,如圖所示:
51hei圖片_20200712213018.jpg (82.75 KB, 下載次數: 65)
下載附件
2020-7-12 21:34 上傳
2、DS1302模塊的讀寫接口類似于I2C,使用burst mode+結構體或者數值可以提高讀寫效率:
- typedef struct rtc_data
- {
- uint8 SS; //秒
- uint8 MI; //分
- uint8 HH; //小時
- uint8 DD; //日
- uint8 MM; //月
- uint8 DOW; //星期
- uint8 YY; //年
- } T_RTC_DATA;
復制代碼- void DS1302_Burst_Write(T_RTC_DATA *rtc_dat)
- {
- uint8 i, *p = (uint8 *)rtc_dat;
- DS1302_RST = 1; //使能片選信號
- _nop_();
- DS1302_Write_Byte(DS1302_BURST_WRITE_ADDR);
- for(i=0; i<sizeof(T_RTC_DATA); i++)
- {
- DS1302_Write_Byte(*p++);
- }
- DS1302_RST = 0;
- _nop_();
- }
- void DS1302_Burst_Read(T_RTC_DATA *rtc_dat)
- {
- uint8 i, *p = (uint8 *)rtc_dat;
- DS1302_RST = 1; //使能片選信號
- _nop_();
- DS1302_Write_Byte(DS1302_BURST_READ_ADDR);
- for(i=0; i<sizeof(T_RTC_DATA); i++)
- {
- *p++ = DS1302_Read_Byte();
- }
- DS1302_RST = 0;
- }
復制代碼
3、DS18B20溫度傳感器采用one wire協議,時序要求精確,讀寫問題不大,主要還是溫度讀取后的轉換和顯示,尤其是小數位的顯示,使用長度16的lookup table,可以減少重復計算:
- bit DS18B20_Get_Temperature(int *temp, int *sign)
- {
- bit ack;
- uint8 LSB, MSB;
- ack = DS18B20_Get_Ack();
- if(ack == 0)
- {
- DS18B20_Write_Byte(0xCC); //跳過ROM
- DS18B20_Write_Byte(0xBE); //跳過溫度采集
- LSB = DS18B20_Read_Byte(); //讀低字節溫度值
- MSB = DS18B20_Read_Byte(); //讀高字節溫度值
- *temp = ((int)MSB<<8) + LSB;
- if(0 > *temp)
- {
- *temp -= 1;
- *temp = ~*temp; //對負溫度數據取補碼
- *sign = -1; // 負數
- }
- else
- {
- *sign = 1; // 正數
- }
- }
- return ~ack;
- }
復制代碼- // DS18B20的小數位四舍五入顯示結果速查表,4bit=索引取值范圍 0 - 15
- uint8 code dect_lookup_tab[] = {0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9};
復制代碼- slen = 0;
- sbuf[slen++] = ds18b20_temp_sign_s < 0 ? '-' : '+';
-
- intT %= 100;
-
- if(intT > 10) sbuf[slen++] = '0' + intT / 10;
- sbuf[slen++] = '0' + intT % 10;
- sbuf[slen++] = '.';
-
- sbuf[slen++] = '0' + dect_lookup_tab[decT]; // 使用之前已經算好的四舍五入結果查表,速度更快。
- //sbuf[slen++] = '0' + (decT*10) / 16; //二進制的小數部分轉換為1位十進制位, 小數部分轉換為可顯示的數字字符
-
- sbuf[slen++] = '\0'; //添加字符串結束符
- LCD1602_Show_Str(10, 0, sbuf); //顯示到液晶屏上
復制代碼
4、uart命令部分,可以使用串口中斷接收輸入到UART_Rxd_Buf,然后選擇一個合適的定時器間隔讀取,解析并執行,詳見附件代碼。
- /* 串口動作函數,根據接收到的命令幀執行響應的動作
- buf-接收到的命令幀指針,len-命令幀長度 */
- void Uart_Cmd_Handler(uint8 *buf)
- {
- int8 slen = 0;
- printf(">cmd recv: [%s]\r\n", buf);
- if(0 == strncmp("rtc set ", buf, 8))
- {
- if(0 != UART_Cmd_Exec_RTC_Set(buf+8))
- {
- printf(">cmd exec: failed.\r\n");
- }
- }
- else
- {
- printf(">cmd unrecognized.\r\n");
- }
- }
復制代碼
5、main函數內容:
- void main()
- {
- int8 slen = 0;
- uint8 pdata uart_cmd_buf[64] = {0};
- EA = 1; //開總中斷
- DS18B20_Start();
- UART_Config(9600);
- ConfigTimer0(TIMER0_SLICE_MS); //T0定時10ms
- DS1302_Init(); //初始化RTC時鐘
- LCD1602_Init(); //初始化液晶
- LCD1602_Show_Str(0, 0, "**:**:**");
- LCD1602_Show_Str(0, 1, "20**#**#**# ???");
- LCD1602_Show_Char(15, 0, 0); //5x7字符 ℃
- LCD1602_Show_Char(4, 1, 1); //5x7字符 年
- LCD1602_Show_Char(7, 1, 2); //5x7字符 月
- LCD1602_Show_Char(10, 1, 3); //5x7字符 日
- while(1)
- {
- Uart_Cmd_Check(&uart_cmd_buf, sizeof(uart_cmd_buf)-1);
- if (timer_flag_250ms) //每250ms讀取依次時間
- {
- ReadAndShowRtc();
- timer_flag_250ms = 0;
- }
- if (timer_flag_3s) //每隔3s執行以下分支
- {
- ReadAndShowTemperature();
- timer_flag_3s = 0;
- }
- }
- }
復制代碼
附件文件列表如圖所示:
Annotation 2020-07-12 211333.png (14.91 KB, 下載次數: 55)
下載附件
2020-7-12 21:49 上傳
以上代碼使用C51開發板調試,接線簡單,具體端口可參看config.h
初學單片機,難免有錯漏之處,還請各位壇友不吝賜教。
DS1302_UART_LCD1602_STC89C52RC.7z
(2.35 MB, 下載次數: 81)
2020-7-12 22:10 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|