............ 很久很久没有玩8位单板机了 .... N 年以前用汇编语言玩了一阵朋友送的一个Z80单板机,做了一个继电器温控的方案后,软硬件一共500出手掉了。 一直到2009年在上海遇到Zilog 公司创始人,Z80设计师 Federico Faggin (F.F.) 后,又重新对单片机产生了一点兴趣。和F.F 一起用餐时,他告诉我,他的z80 已经升级到 ez80, 可以用 C 语言玩...... 真的好玩吗? 我在Intel 公司玩了n 年的 C 语言,用来测试芯片的,现在可以用C 来玩 ez80? ... 于是不假思索就向F.F.索要开发套件。F.F. 很大方,当场送了一套价值400 元,包装精美的套件,看来他也是有备而来啊 ... :) .... 回家后捣鼓了一阵,搞了一个小玩具:躲在蚊帐里用红外遥控器开关外面的电灯 ... 后来几次搬家,把那个小玩具弄丢了。
时钟 tick tick 一晃一晃离开8080/z80/8051 的时代已经40多年了,全球 ARM core 的单片机和开发板大行其道,却发现国内还有大量资源和时间投在了学习51 单板机上面。的确,工业级性能的805x 还有很多应用场合,但对于所谓的【创客】,基于ARM core 的系统其实性价比和可玩性更高。但如果你确认是做电子电气自动化工程师的料,就花点时间玩一下51吧。。。不过网上【巨量】的视频讲座和学习材料,对真正希望学好单片机的同学其实帮助并不大:一则内容拖沓,二则组织混乱,三则浪费时间最终把【玩兴】消磨掉而圆不了工程师的梦。 也许讲者是有开发经验的,甚至是开发的高手,但未必是【讲学】的行家 ?
.....我肯定在暗示你,我即是开发高手又是讲解大师.... Follow ME ....
闲话少叙,我们先来 hack 普中-51 单板机(A2 型)上 RTC 芯片 DS 1302, 把这款板子上的这个设计【缺陷】hack 掉,让板子能够真正做到(断电后也能) 实时计时(包括 日月天-星期)....
¥【免责声明:你如果按照本贴改动你板子上的软硬件,贴主对可能产生的任何损害一概不负任何法律责任 】¥
材料和工具:
* 一颗3V 可充电 锂电池(例如 ML 2032)
* 两根杜邦线
* 一个微型芯片管脚夹具 (我牺牲了一个 用在数字逻辑仪上的测试夹具)
* 电焊等
* 普中-51 单板机的官方电气线路图、说明书
步骤:
* 断电
* 锂电池 (+)极和DS 1302 的VCC1 连接。板子原来设计是VCC1 悬空,(板子正反面都)没有引出,供大家玩一下。
* 锂电池 (- )极和板子的 GND 连接
(注:“连接” , 可以理解为焊接、夹接等等任何你认为方便的线路导通连接 .... DS 1302 贴装芯片的管脚间距较小,焊接需要很好的焊接工具和水 平,我就偷懒了,用数字逻辑仪上的测试夹具夹一下 )
* 上电
* 修改(官方)【21-DS1302时钟实验】示例程序当中的 void ds1302_init(void) 函数,在其中加上 涓流充电寄存器的相关设置:
void ds1302_init(void)
{
u8 i=0;
ds1302_write_byte(0x8E,0X00);
ds1302_write_byte(0x90,0xA5); // 0xA5 = 1010- 0101, 表示内部充电线路选用 一个二极管和一个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-串口通信实验】当中的字符接受代码,使得单片机能够从 Windows PC 主机收取完整的时间-日期信息:
if(UART_RX_STA&0x8000)//判断串口是否收到完整的 9 字节时间-日期数据(7 Byte RTC 寄存器数据 + 2 Byte 通信结束标志)
{
ds1302_write_byte(0x8E,0X00); // 准备更新 ds1302 RTC 当中的 时间-日期数据
for(i=0;i<7;i++)
{
ds1302_write_byte(gWRITE_RTC_ADDR[ i],UART_RX_BUF[ i]); // 逐字节更新 ....
}
ds1302_write_byte(0x8E,0X80); //禁止写RTC,主要是防止管脚上偶发信号【噪音,毛刺】意外破坏RTC 里面的内容
beep_alarm(50,15); // 给一个完成任务的蜂鸣器提示。。。 可有可无。。。。
key_value = 13;// effectively jump to key-13 process,这个key_value 变量的来源是有故事的,以后再讲吧。
UART_RX_STA=0;////清除标记,以便下次接受新的 日期-时间更新数据
}
* 在 PC Windows 上写一个小程序 ( 偷懒的可以用 教程里推荐的串口通讯程序 把时间-日期数据 手工发给单片机,但无法和PC 上的时间准确同步 )
======================================================================================
// 版权说明: 程序的框架源自 Rahul.S , 我填写了 main 里面 和 51单片机 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 上的设置,在你的PC 要相应修改
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 以上发送(超时)错误就放弃了。。。
{
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 可执行文件. 你的PC 机需要安装MSVC 编译系统( 不需要IDE),才能使用cl.exe
* 运行单片机的 UART 数据接受等待循环代码,同时在PC 端运行上面提到的windows 程序,51端收到数据后跳出循环,在LED 数码管上显示和PC 端同步的时间日期即可 。。。。
【图片视频】
一图(姐)百讷, 大家看看图片和视频吧 。。。但这个(老古董 ... 还在用 Flash 插件... :))网站好像没有办法从我的PC 上传mp4 视频... 刚注册没有几天,还不会玩这个网站,多多包涵啊。
okay ?
肯定有不少同学还没有完全看懂、读懂,我抽空整理一下单片机一端混乱、随手涂鸦的原代码, 加上注释后给大家抄一抄 .... :)
Happy Hacking
|