|
............ 很久很久沒(méi)有玩8位單板機(jī)了 .... N 年以前用匯編語(yǔ)言玩了一陣朋友送的一個(gè)Z80單板機(jī),做了一個(gè)繼電器溫控的方案后,軟硬件一共500出手掉了。 一直到2009年在上海遇到Zilog 公司創(chuàng)始人,Z80設(shè)計(jì)師 Federico Faggin (F.F.) 后,又重新對(duì)單片機(jī)產(chǎn)生了一點(diǎn)興趣。和F.F 一起用餐時(shí),他告訴我,他的z80 已經(jīng)升級(jí)到 ez80, 可以用 C 語(yǔ)言玩...... 真的好玩嗎? 我在Intel 公司玩了n 年的 C 語(yǔ)言,用來(lái)測(cè)試芯片的,現(xiàn)在可以用C 來(lái)玩 ez80? ... 于是不假思索就向F.F.索要開(kāi)發(fā)套件。F.F. 很大方,當(dāng)場(chǎng)送了一套價(jià)值400 元,包裝精美的套件,看來(lái)他也是有備而來(lái)啊 ... :) .... 回家后搗鼓了一陣,搞了一個(gè)小玩具:躲在蚊帳里用紅外遙控器開(kāi)關(guān)外面的電燈 ... 后來(lái)幾次搬家,把那個(gè)小玩具弄丟了。
時(shí)鐘 tick tick 一晃一晃離開(kāi)8080/z80/8051 的時(shí)代已經(jīng)40多年了,全球 ARM core 的單片機(jī)和開(kāi)發(fā)板大行其道,卻發(fā)現(xiàn)國(guó)內(nèi)還有大量資源和時(shí)間投在了學(xué)習(xí)51 單板機(jī)上面。的確,工業(yè)級(jí)性能的805x 還有很多應(yīng)用場(chǎng)合,但對(duì)于所謂的【創(chuàng)客】,基于ARM core 的系統(tǒng)其實(shí)性?xún)r(jià)比和可玩性更高。但如果你確認(rèn)是做電子電氣自動(dòng)化工程師的料,就花點(diǎn)時(shí)間玩一下51吧。。。不過(guò)網(wǎng)上【巨量】的視頻講座和學(xué)習(xí)材料,對(duì)真正希望學(xué)好單片機(jī)的同學(xué)其實(shí)幫助并不大:一則內(nèi)容拖沓,二則組織混亂,三則浪費(fèi)時(shí)間最終把【玩興】消磨掉而圓不了工程師的夢(mèng)。 也許講者是有開(kāi)發(fā)經(jīng)驗(yàn)的,甚至是開(kāi)發(fā)的高手,但未必是【講學(xué)】的行家 ?
.....我肯定在暗示你,我即是開(kāi)發(fā)高手又是講解大師.... Follow ME ....
閑話少敘,我們先來(lái) hack 普中-51 單板機(jī)(A2 型)上 RTC 芯片 DS 1302, 把這款板子上的這個(gè)設(shè)計(jì)【缺陷】hack 掉,讓板子能夠真正做到(斷電后也能) 實(shí)時(shí)計(jì)時(shí)(包括 日月天-星期)....
¥【免責(zé)聲明:你如果按照本貼改動(dòng)你板子上的軟硬件,貼主對(duì)可能產(chǎn)生的任何損害一概不負(fù)任何法律責(zé)任 】¥
材料和工具:
* 一顆3V 可充電 鋰電池(例如 ML 2032)
* 兩根杜邦線
* 一個(gè)微型芯片管腳夾具 (我犧牲了一個(gè) 用在數(shù)字邏輯儀上的測(cè)試夾具)
* 電焊等
* 普中-51 單板機(jī)的官方電氣線路圖、說(shuō)明書(shū)
步驟:
* 斷電
* 鋰電池 (+)極和DS 1302 的VCC1 連接。板子原來(lái)設(shè)計(jì)是VCC1 懸空,(板子正反面都)沒(méi)有引出,供大家玩一下。
* 鋰電池 (- )極和板子的 GND 連接
(注:“連接” , 可以理解為焊接、夾接等等任何你認(rèn)為方便的線路導(dǎo)通連接 .... DS 1302 貼裝芯片的管腳間距較小,焊接需要很好的焊接工具和水 平,我就偷懶了,用數(shù)字邏輯儀上的測(cè)試夾具夾一下 )
* 上電
* 修改(官方)【21-DS1302時(shí)鐘實(shí)驗(yàn)】示例程序當(dāng)中的 void ds1302_init(void) 函數(shù),在其中加上 涓流充電寄存器的相關(guān)設(shè)置:
void ds1302_init(void)
{
u8 i=0;
ds1302_write_byte(0x8E,0X00);
ds1302_write_byte(0x90,0xA5); // 0xA5 = 1010- 0101, 表示內(nèi)部充電線路選用 一個(gè)二極管和一個(gè)2K 歐姆的電阻 // 由此限定最大充電電流約為 2.2mA ( ~ = ( 5V - 0.7V)/2K Ohm = 2.15 mA
for(i=0;i<7;i++)
{
ds1302_write_byte(gWRITE_RTC_ADDR[ i],gDS1302_TIME[ i]);
}
ds1302_write_byte(0x8E,0X80); //disable write...
}
* 修改(官方)【 18-串口通信實(shí)驗(yàn)】當(dāng)中的字符接受代碼,使得單片機(jī)能夠從 Windows PC 主機(jī)收取完整的時(shí)間-日期信息:
if(UART_RX_STA&0x8000)//判斷串口是否收到完整的 9 字節(jié)時(shí)間-日期數(shù)據(jù)(7 Byte RTC 寄存器數(shù)據(jù) + 2 Byte 通信結(jié)束標(biāo)志)
{
ds1302_write_byte(0x8E,0X00); // 準(zhǔn)備更新 ds1302 RTC 當(dāng)中的 時(shí)間-日期數(shù)據(jù)
for(i=0;i<7;i++)
{
ds1302_write_byte(gWRITE_RTC_ADDR[ i],UART_RX_BUF[ i]); // 逐字節(jié)更新 ....
}
ds1302_write_byte(0x8E,0X80); //禁止寫(xiě)RTC,主要是防止管腳上偶發(fā)信號(hào)【噪音,毛刺】意外破壞RTC 里面的內(nèi)容
beep_alarm(50,15); // 給一個(gè)完成任務(wù)的蜂鳴器提示。。。 可有可無(wú)。。。。
key_value = 13;// effectively jump to key-13 process,這個(gè)key_value 變量的來(lái)源是有故事的,以后再講吧。
UART_RX_STA=0;////清除標(biāo)記,以便下次接受新的 日期-時(shí)間更新數(shù)據(jù)
}
* 在 PC Windows 上寫(xiě)一個(gè)小程序 ( 偷懶的可以用 教程里推薦的串口通訊程序 把時(shí)間-日期數(shù)據(jù) 手工發(fā)給單片機(jī),但無(wú)法和PC 上的時(shí)間準(zhǔn)確同步 )
======================================================================================
// 版權(quán)說(shuō)明: 程序的框架源自 Rahul.S , 我填寫(xiě)了 main 里面 和 51單片機(jī) UART - RTC 通信的具體代碼
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <Windows.h>
#include <stdio.h>
unsigned char decToBcd(WORD dec)
{
return ( (dec/10)*16 + dec%10);
}
void main(void)
{
HANDLE hComm; // Handle to the Serial port
char ComPortName[] = "\\\\.\\COM13"; // Name of the Serial port(May Change) to be opened, COM13 是我的PC 上的設(shè)置,在你的PC 要相應(yīng)修改
BOOL Status;
SYSTEMTIME localDateTime;
int i=0,j=0;
printf("\n\n +==========================================+");
printf("\n | Serial Transmission (Win32 API) |");
printf("\n +==========================================+\n");
/*----------------------------------- Opening the Serial Port --------------------------------------------*/
hComm = CreateFile( ComPortName, // Name of the Port to be Opened
GENERIC_READ | GENERIC_WRITE, // Read/Write Access
0, // No Sharing, ports cant be shared
NULL, // No Security
OPEN_EXISTING, // Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
if (hComm == INVALID_HANDLE_VALUE)
printf("\n Error! - Port %s can't be opened", ComPortName);
else
printf("\n Port %s Opened\n ", ComPortName);
/*------------------------------- Setting the Parameters for the SerialPort ------------------------------*/
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
Status = GetCommState(hComm, &dcbSerialParams); //retreives the current settings
if (Status == FALSE)
printf("\n Error! in GetCommState()");
dcbSerialParams.BaudRate = CBR_9600; // Setting BaudRate = 9600
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = 1; // Setting StopBits = 1
dcbSerialParams.Parity = NOPARITY; // Setting Parity = None
Status = SetCommState(hComm, &dcbSerialParams); //Configuring the port according to settings in DCB
if (Status == FALSE)
{
printf("\n Error! in Setting DCB Structure");
}
else
{
printf("\n Setting DCB Structure Successfull\n");
printf("\n BaudRate = %d", dcbSerialParams.BaudRate);
printf("\n ByteSize = %d", dcbSerialParams.ByteSize);
printf("\n StopBits = %d", dcbSerialParams.StopBits);
printf("\n Parity = %d", dcbSerialParams.Parity);
}
/*------------------------------------ Setting Timeouts --------------------------------------------------*/
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts(hComm, &timeouts) == FALSE)
printf("\n Error! in Setting Time Outs\n");
else
printf("\n\n Setting Serial Port Timeouts Successfully\n\n");
/*----------------------------- Writing a Character to Serial Port----------------------------------------*/
char lpBuffer[] = { 0x11, 0x11, 0x11, 0x12, 0x12, 0x06, 0x24,0x0D, 0x0A};
DWORD dNoOFBytestoWrite; // No. of bytes to write into the port
DWORD dNoOfBytesWritten = 0; // No. of bytes written to the port
dNoOFBytestoWrite = sizeof(lpBuffer); // Calculating the no of bytes to write into the port
GetLocalTime(&localDateTime);
lpBuffer[3] = decToBcd(localDateTime.wDay);
lpBuffer[4] = decToBcd(localDateTime.wMonth);
lpBuffer[5] = decToBcd(localDateTime.wDayOfWeek);
lpBuffer[6] = decToBcd(((localDateTime.wYear) % 100));
lpBuffer[2] = decToBcd(localDateTime.wHour);
lpBuffer[1] = decToBcd(localDateTime.wMinute);
lpBuffer[0] = decToBcd(localDateTime.wSecond);
for (;;)
{
Status = WriteFile(hComm,// Handle to the Serialport
&lpBuffer[ i], // Data to be written to the port
dNoOFBytestoWrite, // No. of bytes to write into the port
&dNoOfBytesWritten, // No. of bytes written to the port
NULL);
if (Status == TRUE)
{
i++;
if( i > dNoOFBytestoWrite)
{
printf("%d bytes sent to the port...done!",dNoOFBytestoWrite);
break;
}
j = 0;
continue;
}
else
{
//printf("\n\n Error %d in Writing to Serial Port",GetLastError());
j++;
if( j > 10 ) // 10 以上發(fā)送(超時(shí))錯(cuò)誤就放棄了。。。
{
printf("%d times failed to send byte[%d]=%2x... give up and quit!\n",j,i, lpBuffer[ i]); break;
}
}
}
CloseHandle(hComm);//Closing the Serial Port
printf("\n ============port %s closed=============\n", ComPortName);
//_getch();
}
============================================================================
在 Windows 命令窗口下 用 cl 命令編譯-鏈接成 .exe 可執(zhí)行文件. 你的PC 機(jī)需要安裝MSVC 編譯系統(tǒng)( 不需要IDE),才能使用cl.exe
* 運(yùn)行單片機(jī)的 UART 數(shù)據(jù)接受等待循環(huán)代碼,同時(shí)在PC 端運(yùn)行上面提到的windows 程序,51端收到數(shù)據(jù)后跳出循環(huán),在LED 數(shù)碼管上顯示和PC 端同步的時(shí)間日期即可 。。。。
【圖片視頻】
一圖(姐)百訥, 大家看看圖片和視頻吧 。。。但這個(gè)(老古董 ... 還在用 Flash 插件... :))網(wǎng)站好像沒(méi)有辦法從我的PC 上傳mp4 視頻... 剛注冊(cè)沒(méi)有幾天,還不會(huì)玩這個(gè)網(wǎng)站,多多包涵啊。
okay ?
肯定有不少同學(xué)還沒(méi)有完全看懂、讀懂,我抽空整理一下單片機(jī)一端混亂、隨手涂鴉的原代碼, 加上注釋后給大家抄一抄 .... :)
Happy Hacking
|
評(píng)分
-
查看全部評(píng)分
|