因为上次太仓促,这次来真的,原创:基于89C51单片机摩尔斯电码收发系统仿真,附完整代码和仿真,还有视频演示哟。
本人编制此程序仅为兴趣和爱好,请大家多多指教和鼓励。
/*----------------------------------------------------------
* 【实验平台】: 89C52单片机开发板
* 【外部晶振】: 11.0592MHz
* 【主控芯片】: STC89C52
* 【编译环境】: Keil μVisio4
* 【程序编写】: wzqwxx 水上人家
名称:摩尔斯电码自动(手动)发送
内容:仅供交流学习,其他用途请注明编者,若有BUG请原谅编者。
本程序仅实现了短电报自动发送功能,能用两个键输入报文,
能用单手键发送报文,或用PC机等终端输入报文。
编制程序过程本身就很有趣和充满挑战,要不断思考修改调试
完善,从中学到了不少知识。如果能制作成品,练习发报也十分有
趣,这也是对古老的电讯一种纪念,致敬充满智慧的先辈们。
播发摩尔斯电码的一般要求是:以一个“点”的长度为一个时间
单位,“划”是三个点的时间长度;点划之间的间隔是一个点的长度;
字符之间的间隔是三个点的长度;单词之间的间隔是七个点的长度。
这样才能被收报人识别。
------------------------------------------------------------*/
仿真说明:
1、开机后,出现问候语。即进入信息录入界面。这时可按点划键进行输入,这时每输入一点一划,都会出现候选字,如是你想要的,可稍停,即可自动上屏。
若不是要连续输入,直到找到。字母和数字可按摩斯码输入,空格输入点点划划四笔。 注意,点划输入间隔时间不要过长,这个要学习适应。
2、若输入出错,可按菜单键一下,出现DEL,按点键可删除一字,按划键全部删除。
3、在录入界面下,连按两下菜单键,出现发送SEND菜单,再按点键可发送。
4、在录入界面下,连按三下菜单键,出现短语phrase菜单,按点划键上下查找,找到短语后,按菜单键进入发送界面,按点键发送。
5、在录入界面下,连按四下菜单键,出现PCINPUT,再按点键可进入虚拟终端,点击终端获得焦点后,即可输入字符。按退格键删除一字,按回车发送,
按ESC返回点划双键输入。在终端里,在没有输入任何字符下,输入问号,再输入数字,即短语的序号,可调出短语,按回车发送。
6、在录入界面下,按单手键可直接纯手动发报。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
- // main.文件
- #include <REGX52.H>
- #include <stdio.H>
- #include "1602.h"
- #include "MRScode.h"
- unsigned char code *HI = "HELLO!";//开机问候语
- void Timer0Init(void) //4毫秒@11.0592MHz
- {
- TMOD &= 0xF0; //设置定时器模式
- TMOD |= 0x01; //设置定时器模式
- TL0 = 0x9A; //设置定时初值
- TH0 = 0xF1; //设置定时初值
- TF0 = 0; //清除TF0标志
- ET0 = 1;
- EA = 1;
- TR0 = 0; //定时器0由点键和划键启动计时
- }
- void UART_init()
- {
- TMOD |= 0x20; //T1工作模式2 8位自动重装
- TH1 = 0xfd;
- TL1 = 0xfd; //比特率9600 时钟频率11.0592MHZ
- TR1 = 1; //启动T1定时器
- SM0 = 0;
- SM1 = 1; //串口工作方式1 10位异步
- REN = 1; //串口允许接收
- TI = 1;
- }
- /*********按键输入键值获取*******/
- unsigned char keyget()
- {
- unsigned char keyvalue; //键值
- beep = Hmove;
- if(Di == 0 ) //点键按下
- {
- delay_ms(25);
- if(Di == 0 )
- {
- while(Di == 0 ); //松手检测
- keyvalue = 1;
- }
- }
- else if(Hu == 0 ) //划键按下
- {
- delay_ms(25);
- if(Hu == 0 )
- {
- while(Hu == 0 );
- keyvalue = 2;
- }
- }
- else if(GL == 0 ) //功能键按下
- {
- delay_ms(25);
- if(GL == 0 )
- {
- while(GL == 0 );
- keyvalue = 3;
- }
- }
- else
- {
- keyvalue = 255;
- }
- return(keyvalue);
- }
- void main()
- {
- char i;
- Timer0Init();//初始化定时器0
- UART_init();
- lcd_init();
- Lcdwritestring(0,5,HI);
- for(i = 0; i < 100; i++)
- delay_ms(50);
- send_mrs_code_TEXT(HI); //发送字符串
- write_com(0x01);// 清屏
- while(1)
- {
- if(~F0) //PSW^5
- keyin(keyget()); //实体键输入
- else
- PCin(); //电脑终端输入
- };
- }
复制代码
- // MRScode.c文件
- #include <stdio.H>
- #include "1602.h"
- #include "MRScode.h"
- unsigned char count; //计算摩尔码输入显示点划的位置
- unsigned char showbuffer[L]; //显示缓冲
- unsigned char i = 0; //显示缓冲数组下标
- static unsigned char time; //按键停顿延时计数 供字符自动上屏
- static unsigned char MODE; //功能切换 MODE=0 KEY输入,=1按点键删1字,按划全删
- //=2按点键发送,=3调用短语,=4进入PC输入
- //摩尔斯码字库 字母A~Z(下标0~25)计26个, 数字0~9(下标26~35)计10个
- // ? , . ! @ : -(下标36~42)计7个,总共43个字节
- unsigned char code MRSZK[] = { 0x61,0X28,0X2a,0x44,0x80,0x22,
- 0x46,0x20,0x60,0x27,0x45,0x24,0x63,0x62,0x47,0x26,0x2d,0x42,
- 0x40,0x81,0x41,0x21,0x43,0x29,0x2b,0x2c,/*A~Z*/0xbf,0xaf,0xa7,
- 0xa3,0xa1,0xa0,0xb0,0xb8,0xbc,0xbe,/*0~9*/0xcc,0xf3,0xd5,0xeb,
- 0xda,0xf8,0xe1/* ?,.!@:- */ };
- unsigned char code ASCIIZK[] = "?,.!@:-"; //符号库,可扩展
- /*** !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ
- [\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ASCII可见字符 ***/
- //常用短语库
- unsigned char code *phrase[] = {"ok","NO", "SOS","hello","thank you","receive",
- /*好,不, 求救, 你好, 谢谢你, 收到,*/
- "retransmission","Goodbye", "Help me!", "my location:" , "MAX 10" } ;
- /*重复, 再见, 帮帮我, 我的位置, 最多10条短语*/
- /******不限于这些短语,抛转引玉,其它可视EEPROM大小再添加*****/
- /*********此函数据键值实现摩斯码转ASCII码、并切换菜单实现相关功能*******/
- void keyin(unsigned char keyvalue)
- {
- static unsigned char MRScode,num;
- unsigned char x,y,z,j;
- bit flag;
- if(keyvalue != 255) //有键按下
- {
- Lcdwritestring(1,0," "); // 16空格清第二行
- if(keyvalue == 1) //点键
- {
- switch (MODE) //功能模式切换
- {
- case 0: //本机正常输入点
- TR0 = 1;
- MRScode <<= 1; //发送端从高位开始的
- Lcdwritechar(1,6+num,'.'); //num为点划输入的次数
- num++;
- break;
- case 1: //按点键删除一字
- MODE = 0;
- if(i > 0) //有字符就删除
- i--;
- goto CCC;
- case 2: //输完按点键发送
- MODE = 0;
- send_mrs_code_TEXT(showbuffer);
- goto DDD;
- case 3: //上翻查找调用短语
- write_com(0x01); //清屏
- for(j= 0;j<L;j++)
- showbuffer[j] = phrase[x][j];
- if(++x > 10)
- x = 10;
- flag = 1;
- goto DDD;
- case 4: //切换到Pc终端输入
- MODE = 0;
- i = 0;
- F0 = 1;
- Lcdwritestring(1,0,"PC:");
- return;
-
- default:
- MODE = 0;
- return;
- }
- }
- if(keyvalue == 2) //划键
- {
- if(MODE == 1) //按划键全部删除
- {
- MODE = 0;
- i = 0;
- goto CCC;
- }
- else if(MODE == 3) //下翻查找短语
- {
- write_com(0x01);
- for(j= 0;j<L;j++)
- showbuffer[j] = phrase[x][j];
- if(x>0)
- x--;
- flag = 1;
- goto DDD;
- }
- TR0 = 1; //本机正常输入划
- MRScode <<= 1; //发送端从高位开始的
- MRScode |= 0x01;
- Lcdwritechar(1,6+num,'_');
- num++;
- }
- if(keyvalue == 3 )//功能键
- {
- if( ++MODE > 4 ) //循环显示菜单
- MODE = 0;
- if(flag == 1) //查找到短语后直接进入发送菜单
- MODE = 2;
- flag = 0;
-
- switch (MODE) //屏上显示功能菜单
- {
- case 1:
- write_com(0x01);
- Lcdwritestring(1,13,"DEL"); //删除
- goto DDD;
-
- case 2:
- write_com(0x01);
- Lcdwritestring(1,8,"SEND->"); //发送
- goto DDD;
-
- case 3:
- write_com(0x01);
- Lcdwritestring(1,6,"phrase"); // 调用短语
- goto DDD;
-
- case 4:
- write_com(0x01);
- Lcdwritestring(1,3,"PC IN"); // 进入PC输入
- goto DDD;
-
- default:
- write_com(0x01);
- Lcdwritestring(1,0,"KEY IN"); // 本机输入
- goto DDD;
- }
- }
- if(num<5 && num > 0) //处理字母
- {
- if(num == 4 && MRScode == 3) // 输入 ..-- 是空格
- {
- z = '_'; //显示用_代替
- goto BBB;
- }
-
- y = (8-3-num) << 5; //得到字母前三位识别码值
- for(j = 0; j <= 25;j++)//字母字库地址0~25;
- {
- if( MRSZK[j] == ( MRScode | y) )
- {
- z = j+0x41;//转化成ASCII码送显示
- break;
- }
- }
- }
- else if(num>5) //处理符号
- {
- for(j = 36; j <= 42;j++)//符号字库地址36~42;
- {
- if((MRSZK[j]&0x3F) == MRScode ) //清零高两位
- {
- z = ASCIIZK[j-36];
- break;
- }
- }
- }
- else //处理数字
- {
- for(j = 26; j <= 35;j++)//数字地址26~35;
- {
- if((MRSZK[j]&0x1F) == MRScode ) //清零高三位
- {
- z = j+0x16;
- break;
- }
- }
- }
- Lcdwritechar(1,4,z);//显示候选字符
- if(num > 5) //满6个点划自动上屏
- {
- BBB: time = 0;
- TR0 = 0;
- MRScode = 0;
- num = 0;
- showbuffer[i] = z;//转化成的ASCII码送显示
- i++;
- CCC: showbuffer[i] = 0; //字符串结束加0
- write_com(0x01);
- DDD: Lcdwritestring(0,0,showbuffer);
-
- }
- if(i > L-2)
- {
- Lcdwritestring(1,0,"MEMORY FULL!"); //不能超过显存最大长度
- showbuffer[i] = 0;//字符串结束加0
- }
- }
- else
- {
- if( time > 250) //时间到,自动上屏
- {
- goto BBB;
- }
- }
- }
- /*********电脑终端输入ASCII码**********/
- void PCin()
- {
- unsigned char j,k,n,Char;
- for(k = 0;k < L;k++)
- showbuffer[k] = 0;//清缓冲
- k = 0;
- do{
- while(!RI);
- RI = 0;
- Char = SBUF; //运行后在仿真终端输入半角字符
- if(Char == 0x1B) //按ESC键退出PC输入
- {
- write_com(0x01);
- showbuffer[0] = 0;//清缓冲
- Lcdwritestring(0,0,"KEY:");
- F0 = 0; //PSW 用户位
- MODE = 0;
- return;
- }
- if(Char == 0x08) //按Backspace键
- {
- k--; //退格删除一个字
- if(k == 0xff)
- k = 0 ;
- showbuffer[k] = 0;
- write_com(0x01);// 清屏
- Lcdwritestring(0,0,showbuffer);
- continue;
- }
- showbuffer[k] = Char;
- write_com(0x01);// 清屏
- Lcdwritestring(0,0,showbuffer);
-
- /***此段为输入短语数字代码调用短语**/
- if(showbuffer[0] == '?') //调用短语用问号开头
- {
- if(Char >= '0' && Char <= '9')
- { //如果是数字
-
- n = n * 10 + (Char-'0'); //转成十进制数
- if(n>10) //暂时短语只有9个
- {
- n = 10;
- write_com(0x01);
- Lcdwritestring(1,0,phrase[n]);//调用短语
- for(n = 1;n <= k+1; n++)
- showbuffer[n] = 0;
- Lcdwritestring(0,0,showbuffer);
- n = 0;
- k = 0;
- }
- else
- Lcdwritestring(1,0,phrase[n]);//调用短语
- }
- }
- k++;
- }while(Char != 0x0D);//回车发送
- if(showbuffer[0] == '?')
- {
- for(j= 0;j<L;j++)
- showbuffer[j] = phrase[n][j];
- }
- Lcdwritestring(0,0,showbuffer);
- send_mrs_code_TEXT(showbuffer);
- }
- /******此函数可调节整体发送速度*****/
- void delay_ms(unsigned char date)
- {
- unsigned char i,j;
- for(i = 0;i < date;i++)
- for(j = 0; j < 138; j++);
- } //一点时长138约92ms 188约124.6ms 90约61.5ms
- void S_beep() //1个短滴声
- { char i;
- for(i=0;i<1;i++)
- {
- beep = 0;
- delay_ms(100);
- beep = 0;
- delay_ms(100);
- }
- beep = 1;
- count++;
- Lcdwritechar(1,9+count,'.');
- }
- void L_beep() //一个长滴音
- { char i;
- for(i=0;i<3;i++)
- {
- beep = 0;
- delay_ms(100);
- beep = 0;
- delay_ms(100);
- }
- beep = 1;
- count++;
- Lcdwritechar(1,9+count,'_');
- }
- void J2_beep() // 间隔2个点静音
- { char i;
- for(i=0;i<2;i++)
- {
- beep = 1;
- delay_ms(100);
- beep = 1;
- delay_ms(100);
- }
- }
- void J1_beep() // 间隔1个点静音
- { char i;
- for(i=0;i<1;i++)
- {
- beep = 1;
- delay_ms(100);
- beep = 1;
- delay_ms(100);
- }
- }
- void J4_beep() // 间隔4个点静音
- { char i;
- for(i=0;i<4;i++)
- {
- beep = 1;
- delay_ms(100);
- beep = 1;
- delay_ms(100);
- }
- }
- /***********发送一个摩尔斯码************/
- void MRS_code_send(unsigned char MRS_code )
- {
- unsigned char i; //数组下标
- bit bt; //识别长短声音
- if(MRS_code < 0x90) //小于是字母
- {
- i = 3 + MRS_code /0x20; //取前三位
- MRS_code <<= i-1;
- for(; i< 8; i++)
- {
- bt = (MRS_code <<= 1)&0x80;
- if(bt)
- L_beep(); //一个长滴音
- else
- S_beep(); //1个短滴声
- J1_beep(); // 间隔1个点静音;
- }
- }
- else if(MRS_code > 0x90 && MRS_code < 0xc0) //否则是发送数字
- {
- MRS_code <<= 2;
- for(i = 3; i< 8; i++)
- {
- bt = (MRS_code <<= 1)&0x80;
- if(bt)
- L_beep(); //一个长滴音
- else
- S_beep(); //1个短滴声
- J1_beep(); // 间隔1个点静音;
- }
- }
- else //否则是发送符号
- {
- MRS_code <<= 1;
- for(i = 2; i< 8; i++)
- {
- bt = (MRS_code <<= 1)&0x80;
- if(bt)
- L_beep(); //一个长滴音
- else
- S_beep(); //1个短滴声
- J1_beep(); // 间隔1个点静音;
- }
- }
- J2_beep(); //字元间隔1+2=3个点静音
- }
- /******本函数ASCII码转摩尔斯码字符串发送*****/
- void send_mrs_code_TEXT(unsigned char *p)
- {
- unsigned char i; //i即摩尔斯码字库数组下标
- while(*p)
- {
- if(*p != 0x20)//是否是空格
- {
- if(*p >= 0x41 && *p <= 0x5A )//大写字母A~Z
- {
- i = *p-0x41; //i取字母字库 ,0x00地址开始
- }
- else if(*p >= 0x61 && *p <= 0x7A) //小写字母a~z
- {
- i = *p-0x61; //i取字母字库 ,0x00地址开始 ,
- } //摩斯码字母不区分大小写
- else if(*p >= 0x30 && *p <= 0x39) //数字0~9
- {
- i = *p-0x16; //i取数字字库 ,0x1A地址开始
- }
- else
- {
- switch (*p)
- {
- case '?': i =36; //即字库数组下标
- break;
- case ',': i =37;
- break;
- case '.': i =38;
- break;
- case '!': i =39;
- break;
- case '@': i =40;
- break;
- case ':': i =41;
- break;
- case '-': i =42;
- break;
- default:goto AAA;//其它字符当空格处理
- break;
- }
- }
- count = 0;//清摩尔码显示位置
- write_com(0x01);// 清屏
- Lcdwritestring(0,0,p); //第一行显示待发字符串
- Lcdwritestring(1,0,"SEND->"); //第二行显示正在发送:
- Lcdwritechar(1,8,*p);//第二行显示待发字符
- p++;
- MRS_code_send(MRSZK[i]); //发送一个摩尔码
- }
- else
- {
- AAA: p++;
- J4_beep(); // 是空格,单词间隔3+4=7个点静音
- }
- }
- }
- void time0_MRS (void) interrupt 1 //STC89C52 4毫秒@11.0592MHz
- {
- TL0 = 0x9A; //设置定时初值 我这是按点平均时长90ms,划时长270ms设置的
- TH0 = 0xF1; //设置定时初值 在接收人工发送的要设计智能调整适应
- ++time; //按键停顿延时计数 供字符自动上屏
- }
- ..................................
- 其他,限于篇幅,请在附件里查看。
复制代码
Proteus7.5版本的仿真代码下载:
摩斯码104.zip
(371.54 KB, 下载次数: 100)
仿真演示视频
https://www.bilibili.com/video/BV15h41187pu?share_source=copy_web
|