产品选型

51单片机发字符串 单片机实例分享,自定义提醒闹钟

小编 2024-11-26 产品选型 23 0

单片机实例分享,自定义提醒闹钟

图14.1 电子钟

想起那时刚学习单片机,仅仅会写几段汇编代码。后来的几个月,自己也学写了一个电子钟程序,不过做得非常简陋,也没使用什么时钟芯片,仅仅通过单片机的定时器来累计计时,功能上实现了时、分、秒的显示以及简单的闹钟功能。不过,我还真是怀念以前学习单片机的美好时光,也很高兴那时自己能专心学习单片机。后面我也做了一款像样的电子钟(见图14.1),在下面的内容里,我会和大家分享制作它的过程。

零件清单

零件清单是制作这款电子钟的基本元件,还有一些边角料,不再列出。从零件列表(见表14.1)中大家可以看出,整体的成本不超过80元。不过,最近这段时期,AVR单片机涨价厉害,在网上也要18元左右才能买到。如果液晶屏在淘宝网上购买的话,建议以关键字ST7565搜索,这样才能搜索到ST7565控制器的128×64液晶屏。至于其他零件比较常用,一般电子市场都能购买得到。

图14.2 零件全家福

表14.1 材料表

电路原理

如图14.3所示,微控制器(MCU)我选择了AVR单片机的Atmega8,因为比较熟悉。它的程序存储器大小为8KB,数据RAM大小为1KB,工作电源电压范围为2.7~5.5V,最大工作温度为+85℃,最小工作温度为-40℃。时钟芯片选择DS1302,DS1302是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5~5.5V。采用三线接口与MCU进行同步通信。最后,通过使用SD卡的SPI模式和单片机的SPI接口连接,进行数据交换。SD卡相当于容量很大的SPI接口的FLASH,在制作过程中,也可以替换成大容量的FLASH芯片。128×64液晶仅仅需要4根线和单片机连接。由于使用的控制器是ST7565,它不带中文、英文字库,因此需要自己建立字库。但这种液晶屏价格便宜,外观也很小巧。我购买的这款液晶屏,背光是橘黄色的,到了晚上会发出迷人的光泽。

图14.3 电路原理图

工作原理

在制作之前,我先介绍一下它的工作原理。控制芯片使用的是AVR的atmega8单片机,简称M8。大家也可以使用熟悉的51单片机(程序可以到本书配套光盘中下载,大家根据需要自己修改或移植)。程序通过读取SD卡内的TXT文件,显示每天需要提醒的内容。因此,可以通过电脑,方便地修改提醒的内容,如节日、生日、纪念日等。不必再为了修改液晶屏上的提醒内容而特意修改程序代码,仅仅通过编辑TXT文件即可。SD卡是通过它的SPI接口和单片机进行数据交换。液晶屏使用的是串口128×64的黑白液晶屏,控制器是ST7565,它和单片机连接也仅仅需要4个I/O口。时钟芯片使用的是DS1302,大家对它应该不陌生吧。时钟芯片通过3个I/O口和单片机连接,电源使用USB接口的5V电源,经过1117-3.3电源稳压芯片转换成3.3V电压,供给单片机、液晶和SD卡使用,大家也可以使用3.3V的电源直接供电。这款电子钟通过两个按钮实现时间和闹钟的设置。当时钟正常运行时,第2个按钮可以单独开启闹钟或关闭闹钟。

使用方法

(1)在计算机的WinXP系统下把SD卡格式化成FAT文件系统。

(2)先复制字体到SD卡内,这样才能在液晶上显示中文。

(3)在根目录下新建“提醒.txt”文本文件。

(4)在文件内写入一行内容,如下:****-02-13"明天是情人节",这样每年的2月13号,电子钟就会提醒你明天是情人节了。

大家会发现,这款电子钟没有农历的显示,如果要显示农历怎么办呢?如果朋友的生日是按农历来算的怎么办呢?其实也挺简单的,通过在文件中写入公历和农历的对应时间关系即可。如:2010-02-13"农历2009-12-30"。注意:*号是通配符,表示任意的意思。例如:2010-**-**"虎年",表示2010年的任意日期,都会显示虎年。

字库的制作及使用

这是本次制作的知识要点之一。GB2312是中国国家标准简体中文字符集,全称《信息交换用汉字编码字符集·基本集》,又称GB0,由中国国家标准总局发布,1981年5月1日实施。GB2312编码通行于中国大陆,新加坡等也采用此编码。中国大陆绝大多数中文系统和国际化的软件都支持GB2312。

GB2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。GB2312的出现,基本满足了汉字的计算机处理需要。GB2312中对所收汉字进行了“分区”处理,每区含有94个汉字/符号。这种表示方式也称为区位码。

01~09区为特殊符号。

16~55区为一级汉字,按拼音排序。

56~87区为二级汉字,按部首/笔画排序。

10~15区及88-94区则未有编码。

举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码就是1601。

在电脑上的TXT文本文件中,每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。为了和原有的ASCII码兼容,“高位字节”使用了0xA1~0xF7(把01~87区的区号加上0xA0),“低位字节”使用了0xA1~0xFE(把01-94加上0xA0)。由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0~0xF7,“低位字节”的范围是0xA1~0xFE,占用的码位是72×94=6768。其中,有5个空位是D7FA~D7FE。例如,“啊”字在文件中会以两个字节“0xB0(第一个字节)0xA1(第二个字节)”储存。

制作步骤

1 按图在洞洞板上安插好各个元件,并插上已经烧录好程序的芯片。

2 根据电路原理图,依次连接导线

3 将各个组件准备好了后,就可以组装起来。

4 最后插上USB电源,就可正常工作了。

DIY的过程不仅仅是制作过程,还是一个让作品更美好的过程,为此,我又开始了这个自定义提醒闹钟的美化过程。

将钢丝弯曲成如图的形状,在钢丝上拧上螺丝,在万用板上也拧上螺丝。

用内六角扳手、普通扳手拧紧螺丝。

那么如何定位字库中的点阵数据呢?文件编码的区码范围是从0xA1(十六进制)开始,对应区位码中区码的第一区,第二个字节为汉字的位码,范围也是从0xA1(十六进制)开始,对应某区中的第一个位码。就是说,将汉字编码减去0xA0A0就得到该汉字的区位码。例如,汉字“啊”的机内码是十六进制的“0xB0A1”,其中前两位“0xB0”表示编码的区码,后两位“0xA1”表示编码的位码。所以“啊”的区位码为0xB0A1-0xA0A0=0x1001,将区码和位码分别转换为十进制16和01,得到汉字“啊”位于第16区的第1个字的位置,那么点阵数据在文件中的位置为第“32×[(16-1)×94+(1-1)]=45120”以后的32个字节。这就是“啊”的显示点阵需要的字节数据了。其中,32为16×16点阵的取模字节数,表示32字节大小。单片机通过取这连续的32个字节,送到LCD的相应位置,就能正确显示汉字、图形符号了。

最后,使用字库生成工具,就能生成自己需要的字库了。这样的工具软件在网上有许多,请自行选择。我使用未注册的“汉字取模字库生成”小工具,使用次数有一定的限制,但偶尔用于生成字库还是够用的。由于这款液晶显示数据是1列(8个点)为一个地址单位的,而不是1行(8个点)或点地址为单位。因此,取模时需使用纵向(列)取模方式取模,这样方便后期程序的编写。当然也可以直接选择“@宋体”这类字体。通过工具预览后,你会发现,这种字体旋转了90°。点击生成字库,在弹出的菜单中输入的路径和文件名。按“确认”后就会生成需要的字库了,注意后缀名为.dot。程序读取经过旋转后的32字节字体数据,即点阵列数据,就能显示一个汉字了。

存储整个字库数据是个难点,GB2312汉字库有200多字节的大小,单片机的FLASH可是没有足够的空间用来保存它。那么怎么办呢?其实,方法也挺多的。有一种实用、简单、方便的方法就是外接FLASH存储芯片。如SST25VF020、AT45DB161等,它们都是串行接口,可以节省许多I/O,读取速度也够快,但增加了制作成本。还有一种方法,可以直接放在SD卡内,但程序会复杂很多。同时,显示字体的速度也没外接FLASH快。不过最后我还是选择了第2种方法,以后的小制作中再试试第1种方法。

为了让电脑和单片机互相交换数据容易些,需要把SD卡格式化成FAT文件系统,然后单片机解读SD卡格式化后FAT文件系统,在此基础上再读取txt文件,最后调用相应的字库数据在液晶屏上显示。具体如何实现,请读者朋友自行分析源代码。源代码可到本书配套光盘上下载。

编程说明

当我烧录完程序后,迫不及待地要运行这个电路了。把电子钟插上电源后,程序会先初始化硬件(液晶、SD卡和时钟芯片)。之后会读取单片机EEPROM里的闹钟信息,没有的话会新建初始化内容,并写入EEPROM。最后,液晶就会分4行显示时间、日期、星期和闹钟。当有提醒信息时,闹钟时间和提醒的内容会交替闪烁。在程序的循环体内,程序会定时读取SD卡内的TXT文件,如果TXT文件内定义的日期和时钟芯片的日期一致,那么单片机会读取文本文件内对应的显示内容,并在液晶的第4行显示。如果没有相等的日期,单片机会显示默认的字符串“MADEBYZBJ”,大家可以改成自己定义的字符串。

电子钟的右侧有4个按钮,但是本次制作只使用了上面2个按钮,另外2个按钮功能未用。这4个按钮的一端都连到了单片机的中断引脚,并把这个中断引脚设置为上拉,在程序中等待下降沿中断。按钮的另一端和单片机的4个普通I/O连接,这4个I/O设置为低电平。当按钮按下时,就会引发下降沿中断,此时程序修改中断,引为低电平,并把4个普通I/O口上拉,再分别读取4个引脚的电平状态。如果,某个引脚读到低电平,就可以判断对应的这个按钮按下了。最后,等待按钮的释放,不断循环此过程。

当时钟在运行状态时,按第1个按钮,将会进入时钟设置状态,再次按下第1个按钮,就会进入下一个设置选项,以此类推,直到退出最后一个选项(注意闹钟关闭状态,不会进入闹钟设置选项),这样电子钟就会退出设置状态,再次进入运行状态了。电子钟在设置状态下,设置的项目会反显,可以通过按第2个按钮,改变设置的数值。

当时钟在运行状态时,按下第2个按钮,闹钟将会开启或关闭,这取决于原来的状态。在液晶屏上会显示相应的闹钟状态信息。闹钟数据虽然保存在M8单片机的EEPROM中,但不会直接使用。当单片机上电运行时,会自动载入RAM中使用,这样是为了延长EEPROM的使用寿命。但当RAM中的闹钟数据改变时,修改的数据才会同步更新,写入EEPROM。程序会比较RAM中的闹钟时间和时钟芯片的时钟是否一致,当两者一致时,闹钟就会“嘀嘀”地叫了。至于鸣叫多久,大家可以根据自己的需要修改程序中的设置。

更多源程序可以到qq群657864614进行下载!

有没有因为想买一本书,因为价格偏高而犹豫?现在我来帮你解决这个问题,把书名作者直接告诉我,我来帮你找,通常价格均为1元,难一点的也就几块钱,先查询,后付款,诚信经营! 代找各种电子书电子图书教材文献查找代查代找中文pdf格式-淘宝网

单片机C语言之函数

函数定义

函数是一个自我包含的完成一定相关功能的执行代码段。通常C语言的编译器会自带标准的函数库,这些都是一些常用的函数。标准函数已由编译器软件商编写定义,使用者直接调用就能了,而无需定义。但是标准的函数不足以满足使用者的特殊要求,因此C语言允许使用者根据需要编写特定功能的函数,要调用它必须要先对其进行定义。

定义的模式如下:

函数类型 函数名称(形式参数表)

函数类型是说明所定义函数返回值的类型。返回值其实就是一个变量,只要按变量类型来定义函数类型就行了。如函数不需要返回值函数类型能写作“void”表示该函数没有返回值。注意的是函数体返回值的类型一定要和函数类型一致,不然会造成错误。

函数名称的定义在遵循C语言变量命名规则的同时,不能在同一程序中定义同名的函数,这将会造成编译错误(同一程序中是允许有同名变量的,因为变量有全局和局部变量之分)。

形式参数是指调用函数时要传入到函数体内参与运算的变量,它可以是一个、几个或没有。当函数不需要形式参数时(即无参函数),括号内为空或写入“void”表示,但括号不能少。

函数体中能包含有局部变量的定义和程序语句,如函数要返回运算值则要使用return语句进行返回。若在函数的{}号中也能什么也不写,这就成了空函数。在一个程序项目中可以写一些空函数,在以后的修改和升级中能方便的在这些空函数中进行功能扩充。

函数的调用

(一)函数调用的一般说明

函数定义好以后,要被其它函数调用了才能被执行。C语言的函数是能相互调用的,但在调用函数前,必须对函数的类型进行说明,就算是标准库函数也不例外。

标准库函数的说明会被按功能分别写在不一样的头文件中,使用时只要在文件最前面用#include预处理语句引入相应的头文件。如前面使用的printf函数的说明是放在文件名为stdio.h的头文件中。

调用就是指一个函数体中引用另一个已定义的函数来实现所需要的功能,这个时候函数体称为主调用函数,函数体中所引用的函数称为被调用函数。主函数只是相对于被调用函数而言。

一个函数体中能调用数个其它的函数,这些被调用的函数同样也能调用其它函数,也能嵌套调用。但是在c51语言中有一个函数是不能被其它函数所调用的,它就是main主函数。

标准库函数只要用#include引入已写好说明的头文件,在程序就能直接调用函数了。如调用的是自定义的函数则要用如下形式编写函数类型说明:

类型标识符 函数的名称(形式参数表);

这样的说明方式是用在被调函数定义和主调函数是在同一文件中。也能把这些写到文件名.h的文件中用#include“文件名.h”引入。

如果被调函数的定义和主调函数不是在同一文件中的,则要用如下的方式进行说明,说明被调函数的定义在同一项目的不一样文件之上,这样说明的函数也能称为外部函数,定义如下:

extern类型标识符 函数的名称(形式参数表);

函数的定义和说明是完全不一样的,在编译的角度上看函数的定义是把函数编译存放在ROM的某一段地址上,而函数说明是告诉编译器要在程序中使用那些函数并确定函数的地址。如果在同一文件中被调函数的定义在主调函数之前,这个时候能不用说明函数类型。也就是说在main函数之前定义的函数,在程序中就能不用写函数类型说明了。能在一个函数体调用另一个函数(嵌套调用),但不允许在一个函数定义中定义另一个函数。还要注意的是函数定义和说明中的“类型、形参表、名称”等都要相一致。

(二)函数调用的一般形式

调用函数的一般形式如下:

函数名 (实际参数表)

“函数名”就是指被调用的函数。

实际参数表能为零或多个参数,多个参数时要用逗号隔开,每个参数的类型、位置应与函数定义时所的形式参数一一对应,它的作用就是把参数传到被调用函数中的形式参数,如果类型不对应就会产生一些错误。调用的函数是无参函数时不写参数,但不能省后面的括号。

下面我们看一下在实际应用中函数不同的调用方式:

1. 函数语句

例如printf(“Hello World!\n”);

它以“Hello World!\n”为参数调用printf这个库函数,在这里函数调用被看作了一条语句。

2. 函数参数

“函数参数”这种方式是指被调用函数的返回值当作另一个被调用函数的实际参数,如temp=StrToInt(CharB(16));CharB的返回值作为StrToInt函数的实际参数传递。

3. 函数表达式

例如temp=Count();

这个函数的调用作为一个运算对象出现在表达式中,称为函数表达式。例子中Count()返回一个int类型的返回值直接赋值给temp。注意的是这种调用方式要求被调用的函数能返回一个同类型的值,不然会出现不可预料的错误。

C51常用头文件

下面介绍一些常用的C51头文件:

absacc.h——包含允许直接访问8051不同存储区的宏定义;

assert.h——文件定义assert 宏,可以用来建立程序的测试条件;

ctype——字符转换和分类程序;

intrins.h——文件包含指示编译器产生嵌入式固有代码的程序的原型;

math.h——数学程序;

reg51.h——51的特殊寄存器;

reg52.h——52的特殊寄存器;

setjmp.h——定义jmp_buf类型和setjmp和longjmp程序的原型;

stdarg.h——可变长度参数列表程序;

stdlib.h——存储区分配程序;

stdio.h——标准输入和输出程序;

string.h——字符串操作程序、缓冲区操作程序。

对于常用的MCS-51单片机,必须包含reg51.h的头文件,因为该文件对51单片机的相关寄存器及位进行了定义,这样在程序中才可以使用这些资源。

reg51.h文件的具体内容如下:

#ifndef __REG51_H__

#define __REG51_H__

/* BYTE Register */ //单元定义

sfr P0 = 0x80;

sfr P1 = 0x90;

sfr P2 = 0xA 0 ;

sfr P3 = 0xB0;

sfr PSW = 0xD0;

sfr ACC = 0xE0;

sfr B = 0xF0;

sfr SP = 0x81;

sfr DPL = 0x82;

sfr DPH = 0x83;

sfr PCON = 0x87;

sfr TCON = 0x88;

sfr TMOD = 0x89;

sfr TL0 = 0x8A;

sfr TL1 = 0x8B;

sfr TH0 = 0x8C;

sfr TH1 = 0x8D;

sfr IE = 0xA8;

sfr IP = 0xB8;

sfr SCON = 0x98;

sfr SBUF = 0x99;

/* BIT Register */

/* PSW */ //位定义

sbit CY = 0xD7;

sbit AC = 0xD6;

sbit F0 = 0xD5;

sbit RS1 = 0xD4;

sbit RS0 = 0xD3;

sbit OV = 0xD2;

sbit P = 0xD0;

/* TCON */

sbit TF1 = 0x8F;

sbit TR1 = 0x8E;

sbit TF0 = 0x8D;

sbit TR0 = 0x8C;

sbit IE1 = 0x8B;

sbit IT1 = 0x8A;

sbit IE0 = 0x89;

sbit IT0 = 0x88;

/* IE */

sbit EA = 0xAF;

sbit ES = 0xAC;

sbit ET1 = 0xAB;

sbit EX1 = 0xAA;

sbit ET0 = 0xA9;

sbit EX0 = 0xA8;

/* IP */

sbit PS = 0xBC;

sbit PT1 = 0xBB;

sbit PX1 = 0xBA;

sbit PT0 = 0xB9;

sbit PX0 = 0xB8;

/* P3 */

sbit RD = 0xB7;

sbit WR = 0xB6;

sbit T1 = 0xB5;

sbit T0 = 0xB4;

sbit INT1 = 0xB3;

sbit INT0 = 0xB2;

sbit TXD = 0xB1;

sbit RXD = 0xB0;

/* SCON */

sbit SM0 = 0x9F;

sbit SM1 = 0x9E;

sbit SM2 = 0x9D;

sbit REN = 0x9C;

sbit TB8 = 0x9B;

sbit RB8 = 0x9A;

sbit TI = 0x99;

sbit RI = 0x98;

#endif

而absacc.h为对8051单片机的不同存储区的宏定义,具体如下:

#define CBYTE ((unsigned char volatile code *) 0) //定义程序存储器;

#define DBYTE ((unsigned char volatile data *) 0) //定义片内数据存储区;

#define PBYTE ((unsigned char volatile pdata *) 0) //定义页寻址空间;

#define XBYTE ((unsigned char volatile xdata *) 0) //定义片外数据存储区。

而intrins.h文件对指示编译器产生嵌入式代码,如空操作执行、位指令、栈操作指令等,该文件的具体内容如下:

extern void _nop_ (void); //空操作8051 NOP指令

extern bit _testbit_ (bit); //测试并清零位8051 JBC指令

extern unsigned char _cror_ (unsigned char,unsigned char); //字符循环左移

extern unsigned int _iror_ (unsigned int,unsigned char); //字符循环右移

extern unsigned long _lror_ (unsigned long,unsigned char); //整数循环右移

extern unsigned char _crol_ (unsigned char,unsigned char); //整数循环右移

extern unsigned int _irol_ (unsigned int,unsigned char); //整数循环左移

extern unsigned long _lrol_ (unsigned long,unsigned char); //长整数循环左移

extern unsigned char _chkfloat_(float); //测试并返回源点数状态

extern void _push_ (unsigned char _sfr); //压入堆栈

extern void _pop_ (unsigned char _sfr); //弹出堆栈

相关问答

51单片机 如何将一个 字符串 (包括字母数字和标点)发给电脑,在...

下边有很多字符串常量可以使用,直接使用对应的in表达式就可以了。stringstr1="a=1.2,3.5,7.88";vararr=(fromsinstr1....

怎么把写好的程序烧写进到89 c51单片机 里头去?-ZOL问答

看你的单片机的型号不过都要生成HEX文件如果是AT的要用下载器。STC的用串口线就能下载,电脑跟单片机之间要用MAX232你要用编程器,但有的编程器,不支持89C51...

vbutf8怎么转换中文?

无须先转换成UTF-8再转成GB2312更简单的:'源串dimstrTextasstring="要发往单片机的字符串"'要输出的字节数组dim.....

db指令?

如:MES...DB作为汇编语言中的伪操作命令,它用来定义操作数占用的字节数。在这里B是byte的缩写,即字节,所以,该伪操作所定义的每个操作数占有1个字节(8位)。...

单片机 编写音乐程序中DB表示什么意思?程序中的一段如下?

DB是单片机汇编语言中的指令DB指令以表达式的值的字节形式初始化代码空间。格式:[标号:]DB表达式表表达式中可包含符号、字符串、或表达式等项,各个项之间用逗...

如何让 单片机 用串行通信获取系统时间?

单片机与PC机间串行口通信的基本原理,介绍了89C51单片机及其外围信号采集电路构成的下位机的原理及程序框图,并详述了在Delphi环境下上位机利用Spco...

单片机 定时器程序如何实现?- 一起装修网

一起装修网问答平台为您提供单片机定时器程序如何实现?的相关答案,并为您推荐了关于单片机定时器程序如何实现?的相关问题,一起装修网问答平台:装修问题,因我而...

c51单片机 中的前单引号怎么? - 9C1uKf71F42 的回答 - 懂得

搜狗拼音输入法,先选择半角,然后就可以输入单引号了‘’如果是要输入英文半角那就按一下“shift”,然后再输入就可以了。''或者双引号里面的字符串,...

c语言!0是什么意思?0又是什么意思?

'\0'是字符串的结束标志,s[i]!='\0'这个意思是s【i】是一个字符串,但是这个字符串里面是没有任何字符的,这个主要是为了区别字符和字符串之间的。C语言是一...

51单片机 写入芯片超时是为什么-ZOL问答

你这是普中的板子吧,而单片机是STC的吧?要是STC的单片机,就必须用STC的下载软件,STC-ISP,这是STC单片机的专用的烧录软件,什么普中,都是神马,它怎么可能写出烧...

猜你喜欢