#include <regx51.h>
#define queue_size 6 //定义自动键的缓存队列长度。
#define cw_tone_frequency 800 //定义侧音频率。可定义成变量,在程序中进行设置,本程序忽略。
#define dot 0 //定义常数表示点
#define dash 1 //定义常数表示划
/////////////////////////////////////////////////////////////////////////////////////////////
//1、以点为基本单位,1个划的长度=3点的长度,点的长度可调整,划的长度由程序控制。
//2、在1个字母或数字内,各点、划之间的间隔为1个点的长度,该间隔由程序控制。
//3、字母(数字)与字母(数字)之间的间隔为3点的长度,该长度拍发时人工控制。
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
unsigned char dot_dash_buffer[queue_size]; //定义数组作为自动键的缓存队列
unsigned char dot_dash_buffer_head=queue_size-1; //循环数组实现队列,定义队列头
unsigned char dot_dash_buffer_tail=queue_size-1; //循环数组实现队列,定义队列尾
////////////////////////////////////////////////////////////////////////////////////////////
// 在程序中通过按键输入可对该变量进行重新设置,从而调整发码速度。
unsigned char dot_time=7; //本程序按每分钟12个word(paris)固定设置:1个点的时间约为100ms.
unsigned char speed=20; //定义显示的发码速度
////////////////////////////////////////////////////////////////////////////////////////////
// 定义电键的输入端,增加程序的可读性和电路的可移植性
sbit paddle_dot=p3^6;
sbit paddle_dash=p3^7;
sbit dot_time_key=p3^3; //定义调整发码速度键
// 定义电键的输出端,增加程序的可读性和电路的可移植性
sbit cw_out=p3^5; //cw输出,接晶体管开关电路
sbit cw_tone_out=p3^4; //侧音电路,接晶体管开关电路
////////////////////////////////////////////////////////////////////
// 定义点和划的状态寄存器,记忆上一次paddle_dot和paddle_dash的状态
bit paddle_dot_register;
bit paddle_dash_register;
/////////////////////////////////////////////////////////////////////
// 每次t0/c0中断发生时dot_dash_timer、clear_lcd自动加1,方便程序计时
unsigned char dot_dash_timer;
unsigned int clear_lcd;
// 每次t0/c0中断发生时dot_duration、dash_duration自动减1,方便程序计时
unsigned char dot_duration;
unsigned char dash_duration;
// 发划函数
void send_dash(void)
{
dot_dash_timer=0;
cw_out=1;
// 开定时器1,音频开
tr1=1;
while(dot_dash_timer!=3*dot_time);//等待dash的时间到
cw_out=0;
// 关定时器,音频关
tr1=0;
cw_tone_out=0;
while(dot_dash_timer!=4*dot_time);//等待间隔时间到
}
/////////////////////////////////////////////////////////////////////
void send_dot(void)
{
dot_dash_timer=0;
cw_out=1;
// 开定时器,音频开
tr1=1;
while(dot_dash_timer!=dot_time);//等待达的时间到
cw_out=0;
// 关定时器,音频关
tr1=0;
cw_tone_out=0;
while(dot_dash_timer!=2*dot_time);//等待间隔时间到
}
//////////////////////////////////////////////////////////////////
// 定义时钟中断,使用t/c0时钟
void timer_0(void)interrupt 1 using 1
{
// 每次中断软计时器加1
dot_dash_timer++;
clear_lcd++;
// 每次中断发生时读取paddle_dot和paddle_dash的状态,与上一次paddle_dot和paddle_dash的状态进行比较
// 根据比较结果,决定点(0)划(1)如何进入缓存队列
if(paddle_dot==0)//如果paddle_dot被按下
{
if(paddle_dash==1)//如果paddle_dot被单独按下,paddle_dash未被按下
{
// 如果上一次按键状态paddle_dot_register为0,即常按paddle_dot,以一定的时间间隔连续发点
if (paddle_dot_register==0)
{
if(dot_duration--==0) paddle_dot_register=1;
}
// 如果前次按键状态paddle_dot_register为1,即paddle_dot被按后放开,则发点
if (paddle_dot_register==1)
{
// 本程序假定队列永远不满,不做队列是否满的条件判断
// 点入队列,队尾下标要加1。
dot_dash_buffer_tail= (dot_dash_buffer_tail+1)%queue_size;
dot_dash_buffer[dot_dash_buffer_tail]=dot;
paddle_dot_register=paddle_dot; //记录本次paddle_dot按键状态
dot_duration=2*dot_time-dot_time/4; //设定连续发点的时间间隔
}
}
if(paddle_dash==0)//如果paddle_dash也被按下
{
// 如果paddle_dot_register==0,即paddle_dot前一次已被按下,
// 此时如果paddle_dash刚被按下,即paddle_dash_register==1,则插入划
if(paddle_dot_register==0&&paddle_dash_register==1)
{
// 本程序假定队列永远不满,不做队列是否满的条件判断
// 划入队列,队尾下标要加1。
dot_dash_buffer_tail= (dot_dash_buffer_tail+1)%queue_size;
dot_dash_buffer[dot_dash_buffer_tail]=dash;
paddle_dash_register=paddle_dash; //记录本次paddle_dash按键状态
dot_duration=4*dot_time-dot_time/2; //设定点划交替状态下点和划的到达时间
dash_duration=dot_duration+2*dot_time;
}
// 如果paddle_dot_register==0&&paddle_dash_register==0,则进入点划交替状态,等待点到达
if(paddle_dot_register==0&&paddle_dash_register==0)
{
if(dot_duration--==0) paddle_dot_register=1;
}
}
}
else paddle_dot_register=paddle_dot; //paddle_dot未被按下,只记录本次paddle_dash按键状态
if(paddle_dash==0)//如果paddle_dash被按下
{
if(paddle_dot==1)//如果paddle_dash被单独按下,paddle_dash未被按下
{
// 如果上一次按键状态paddle_dash_register为0,即常按paddle_dash,以一定的时间间隔连续发划
if (paddle_dash_register==0)
{
if(dash_duration--==0) paddle_dash_register=1;
}
// 如果前次按键状态paddle_dash_register为1,即paddle_dash被按后放开,则发划
if(paddle_dash_register==1)
{
// 本程序假定队列永远不满,不做队列是否满的条件判断
// 划入队列,队尾下标要加1。
dot_dash_buffer_tail= (dot_dash_buffer_tail+1)%queue_size;
dot_dash_buffer[dot_dash_buffer_tail]=dash;
paddle_dash_register=paddle_dash; //记录本次paddle_dash按键状态
dash_duration=3*dot_time+dot_time/3; //设定连续发划的时间间隔
}
}
if(paddle_dot==0)//如果paddle_dot也被按下
{
// 如果paddle_dash_register==0,即paddle_dash前一次已被按下,
// 此时如果paddle_dot刚被按下,即paddle_dot_register==1,则插入点
if(paddle_dash_register==0&&paddle_dot_register==1)
{
// 本程序假定队列永远不满,不做队列是否满的条件判断
// 点入队列,队尾下标要加1。
dot_dash_buffer_tail= (dot_dash_buffer_tail+1)%queue_size;
dot_dash_buffer[dot_dash_buffer_tail]=dot;
paddle_dot_register=paddle_dot; //记下本次paddle_dot的按键状态
dash_duration=2*dot_time-dot_time/2; //设定点划交替状态下点和划的到达时间
dot_duration=dash_duration+4*dot_time;
}
// 如果paddle_dot_register==0&&paddle_dash_register==0,则进入点划交替状态,等待划到达
if(paddle_dot_register==0&&paddle_dash_register==0)
{
if(dash_duration--==0) paddle_dash_register=1;
}
}
}
else paddle_dash_register=paddle_dash; //paddle_dash未被按下,只记录本次paddle_dash按键状态
// 本程序每10ms中断一次
th0=-(10000/256); //初值取整,赋值th0
tl0=-(10000%256); //初值取余,赋值tl0
}
// 定义时钟中断,使用t/c1时钟
void timer_1(void)interrupt 3 using 2
{
cw_tone_out=!cw_tone_out;
// 音频频率可调,本程序每0.666ms中断一次,频率750hz
th1=-(cw_tone_frequency/256); //初值取整,赋值th0
tl1=-(cw_tone_frequency%256); //初值取余,赋值tl0
}
/////////////////////////////lcd_1602显示相关函数/////////////////////////////////
extern void lcd_1602_write_command(unsigned char command);
extern void lcd_1602_write_character(unsigned char character);
extern void lcd_1602_display_string(unsigned char position,unsigned char *string);
extern void lcd_1602_display_wpm(unsigned char position,unsigned char i);
//////////////////////////////////////////////////////////////////////////////////
extern unsigned char getkey(void);
main(void)
{
// 初始化输入端口,使其工作在输入状态
paddle_dot=1;
paddle_dash=1;
dot_time_key=1;
paddle_dot_register=paddle_dot;
paddle_dash_register=paddle_dash;
// 初始化输出端口,使其工作在输出状态
cw_out=0;
cw_tone_out=0;
dot_dash_timer=0;
clear_lcd=0;
//////////////////////////////////////////////////////////////////////////////////
// 初始化lcd,显示初始值。 //
//////////////////////////////////////////////////////////////////////////////////
lcd_1602_write_command(0x38); //0x38表示:8位数据、2行显示、5×8点阵
lcd_1602_write_command(0x0c); //0x0c表示:显示开,光标关,不闪烁
lcd_1602_write_command(0x06); //ac自动加1
lcd_1602_display_wpm(0x00|0x80,speed);
lcd_1602_display_string(0x40|0x80,"750hz");
lcd_1602_write_command(0x48 | 0x80);
/////////////////////////////////////////////////////////////////////////////////////////
// t/c0&1工作在定时器方式1
tmod=0x11;
// 计数器预置初值,计时器的计数率为fosc/12,当单片机的fosc=12mhz时,机器周期为1us.当计数器
//工作在方式1时,设置初值的计算公式为:(2的16次方-计数初值)*1us=定时间隔数,
// 本程序t/c0每10ms中断一次
th0=-(10000/256); //初值取整,赋值th0
tl0=-(10000%256); //初值取余,赋值tl0
// 音频频率可调,本程序每0.666ms中断一次,频率750hz
th1=-(cw_tone_frequency/256); //初值取整,赋值th1
tl1=-(cw_tone_frequency%256); //初值取余,赋值tl1
// cpu开中断
ea=1;
// t/c0&1开中断
et0=1;
et1=1;
// 开定时器0
tr0=1;
///////////////////////////////////////////////////////////////////////////////////////////
while(1)
{
// 自动键输入缓冲区如果不空,则发送缓冲区中的点或划。
// 缓冲区的头指针和尾指针相等时,则判定缓冲区为空
if (dot_dash_buffer_head!=dot_dash_buffer_tail)
{
// 如果上一个码发完则清屏,为下一次显示做准备,判断条件为大于3个点的时间内没有发码,
if (clear_lcd>3*dot_time)
{
lcd_1602_display_string(0x48 | 0x80," ");
lcd_1602_write_command(0x48 | 0x80);
}
switch (dot_dash_buffer[(dot_dash_buffer_head+1)%queue_size])
{
case dot:
{
send_dot();
lcd_1602_write_character('.');
break;
}
case dash:
{
send_dash();
lcd_1602_write_character('_');
break;
}
}
// 缓冲区队首指针后移,已发完的点或划出队列
dot_dash_buffer_head = (dot_dash_buffer_head+1)%queue_size;
// 对发完后的时间间隔开始计时
clear_lcd=0;
}
/////////////////////////////////////////////////////////////////////////////////////////////
//以下程序扫描键盘,调整发码速度和侧音频率
else
{
switch (getkey())
{
case 0: //键盘返回0调整发码速度,调整方式为循环方式
{
if (speed==12)
{
speed=15;
dot_time=8;
lcd_1602_display_wpm(0x00|0x80,speed);
break;
}
if (speed==15)
{
speed=20;
dot_time=7;
lcd_1602_display_wpm(0x00|0x80,speed);
break;
}
if (speed==20)
{
speed=12;
dot_time=10;
lcd_1602_display_wpm(0x00|0x80,speed);
break;
}
}
case 3:{}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
}