找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 85|回复: 0
打印 上一主题 下一主题
收起左侧

为PZ-51单板机上的实时时钟加装后备纽扣电池,和Windows时间同步

[复制链接]
跳转到指定楼层
楼主
............ 很久很久没有玩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

51hei图片_20240903211531.jpg (312.42 KB, 下载次数: 0)

和 Windows PC 时间同步

和 Windows PC 时间同步

51hei图片_20240903211630.jpg (389.24 KB, 下载次数: 0)

夹住Vcc1 管脚

夹住Vcc1 管脚

51hei图片_20240903213021.jpg (427.17 KB, 下载次数: 1)

背面的锂电池

背面的锂电池

WeChat_20240903211753.gif (1.07 MB, 下载次数: 0)

动画-同步

动画-同步

1.gif (918.78 KB, 下载次数: 0)

动画-断电后再上电

动画-断电后再上电

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表