现在在main函数中编写如下代码:在OLED_Fill(0x00); 语句的后面加入timer_portInit();语句,完成定时串口中断初始化。然后在while(1)循环中加入如下的代码
while(flagReceive == 1 && beepTime > 0) // 单片机接收信息后, 蜂鸣器响铃 { beep = ~beep; //字符串接收完成,蜂鸣器响 delay(1); beepTime--; } beepTime = 300; flagReceive = 0;
现在编译程序,将生成的hex文件烧入单片机中,电脑串口助手发送信息后的效果如下图所示
现在已经实现了通过串口通断实现发送信息于OLED屏幕显示,并且蜂鸣器响铃的功能了。
4、 OLED显示光标与按键控制光标移动
上一步我们通过实现了串口通断发送信息于OLED屏幕显示的功能,现在我们要实现系统处于发送模式时:OLED 屏幕显示光标;用按键进行接收/发送模式的切换;用按键控制光标的移动来选择我们要发送给电脑串口助手的字符数据。
首先是要实现的是发送/接收信息模式的功能,这个功能的实现是要用按键来处理模式转换的功能。在Main.h中我们定义按键的处理变量如下
sbit left = P2^1; //向左按键 sbit right = P2^2; //向右按键 sbit up = P2^0; //向上按键 sbit down = P3^3; //向下按键
这里我们定义了开发板上的按键A4、A5、A6、A7分别是变量left、right、up、down,完成了左右、上下键的的定义。进行模式切换时,是每次通过同时按下左右键可以进行接收/发送模式的转换。
现在我们要实现的是OLED屏幕显示光标。显示光标的功能是通过调用Oled.c文件中的OLED_WrDat(0xFF)函数来实现的。0xFF表示写入OLED的数据是在OLED屏幕上显示的是白底。它与OLED_Fill(0xFF);写入OLED命令的功能是一样的,在OLED屏幕上全屏显示白底的功能。用这个OLED_WrDat来显示光标的功能,提前就要设置光标显示在OLED 屏幕的何处。比如通过调用Oled.c文件中的OLED_Set_Pos(0,0)函数就设置了OLED的坐标于屏幕的左上角的开始位置处。然后通过调用OLED_WrDat(0xFF)就可以在屏幕的左上角的位置处显示一条细细的竖直线。我们显示的字符的宽度是8*16的字体,所以我们通过for循环来调用OLED_WrDat()函数就可以在OLED屏幕的显示字符处显示与字符同样大小的光标了。现在我们在Main.h文件中定义如下的数组变量并初始化
UCHAR code closeCursor[8]= // 清除光标 { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; UCHAR code openCursor[8]= // 显示光标 { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
我们通过调用OLED_WrDat(openCursor[i])函数就可以将数组中的数据写入OLED中了。
现在要实现的是按键处理的功能,在Main.h中定义如下的变量和声明函数
char funcSelect = 0; // 非 0 时发送信息模式 char flagSend = 0; // 非 0 时单片机发送信息 char keyX = 0, keyY = 0; // 光标显示的坐标 void keyLeftRight(); // 按键处理 void keyUpDown(); void sendData(); // 单片机发送数据 void sendMode(); // 发送信息模式
keyX与keyY变量都初始化为0 ,第一次模式切换到发送模式时,通过调用OLED_Set_Pos(keyX, keyY); 语句就可以在OLED 屏幕左上角的位置处显示光标了,keyX与keyY变量的值是在按键处理中,每次通过按键进行改变上下左右移动改变时改变的。
funcSelect变量初始化为0,当我们每次同时按下左右键时,通过执行funcSelect = ~funcSelect;语句后,可以在0与非0之间变换。
现在进行左右键功能的编写,在Main.c中实现keyLeftRight的功能,其代码如下所示
void keyLeftRight() { if(left == 0) //按往左键时 { delay(10); if(left == 0 && right == 0) //左右键同时按下时, 进行接收/发送模式切换 { // funcSelect: 为0 是接收信息模式, 非0是发送信息模式 funcSelect = ~funcSelect; if(funcSelect == 0) { OLED_CLS(); } } else if(left == 0 && right != 0 && funcSelect != 0) { OLED_Set_Pos(keyX, keyY); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); // 关闭当前的光标 OLED_Set_Pos(keyX, keyY + 1); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); keyX -= 8; // 改变光标X轴的位置 if(keyX < 0) { keyX = 120; } } while(!left); //消抖 } if(right == 0) //按往右键时 { if(funcSelect != 0) { OLED_Set_Pos(keyX, keyY); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); OLED_Set_Pos(keyX, keyY + 1); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); keyX += 8; if(keyX > 120) { keyX = 0; } } while(!right); //消抖 } }
keyLeftRight()函数的功能就是:通过每次按下左右的时,可以通过改变funcSelect的值来控制sendMode()函数功能的实现;分别按下左右键的时候就可以实现光标的左右移动和关闭移动之前显示的光标位置处的光标;执行keyX += 8; 语句之后,就可以改变keyX的值了,在主函数里面通过死循环不断的调用sendMode()函数就可显示keyX改变之后的坐标处的光标了。
现在进行上下键功能的编写,在Main.c中实现keyUpDown的功能,其代码如下所示
void keyUpDown() { if(up == 0) //按往上键时 { if(up == 0 && down == 0) //上下键同时按下时控制单片机发送字符 { flagSend = 1; } else if(up == 0 && down != 0 && funcSelect != 0) { OLED_Set_Pos(keyX, keyY); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); OLED_Set_Pos(keyX, keyY + 1); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); keyY -= 2; if(keyY < 0) { keyY = 6; } } } while(!up); //消抖 if(down == 0) //按往下键时 { if(funcSelect != 0) { OLED_Set_Pos(keyX, keyY); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); OLED_Set_Pos(keyX, keyY + 1); for(i = 0; i < 8; i++) OLED_WrDat(closeCursor[i]); keyY += 2; if(keyY > 6) { keyY = 0; } } } while(!down); //消抖 }
上下键改变光标的位置的原理和左右键改变光标的位置的原理是一样的。上下键还要实现的功能就是每次同时按下上下键时,可以将光标处的字符发送给电脑串口调式助手。发送字符是通过flagSend变量的值进行控制的。显示光标的功能是通过sendMode函数实现的,具体的代码与功能的实现是在下一步的讲解中。
主函数中,在OLED_P8x16Str(0, 0, dataString); 后面编写如下代码
keyLeftRight(); // 按键处理
keyUpDown();
sendMode(); // 模式转换检测
在主函数中通过函数调用实现按键处理的功能。
编译程序,将生成的hex文件烧入单片机中。OLED 屏幕屏幕显示光标如下图所示
现在我们已经实现了通过按键进行模式切换、OLED 屏幕显示光标、按键控制光标移动的功能了.
5、光标处的字符发送给电脑串口助手显示与流水灯
上一步我们实现了OLED显示光标与按键控制光标移动的功能,现在要实现的功能是通过同时按下上下键将光标处的字符发送给电脑串口调式助手于接收区内显示,在发送完字符后8个led灯实现流水灯流动两次的效果。
首先是实现发送光标处的字符功能。在上一步我们已经在Main.h中声明了sendData()与sendMode()函数。sendData()函数是实现单片机发送数据的功能,sendMode()函数是实现单片机处于发送模式与调用sendData()函数发送数据的功能。
现在实现sendMode函数的功能,在Main.c中编写代码如下
void sendMode() { if(funcSelect) //发送模式时 { while(flagSend == 1) // 发送字符标志 { sendData(); // 发送数据给电脑串口助手 flagSend = 0; } OLED_Set_Pos(keyX, keyY); // 打开光标 for(i = 0; i < 8; i++) OLED_WrDat(openCursor[i]); OLED_Set_Pos(keyX, keyY + 1); for(i = 0; i < 8; i++) OLED_WrDat(openCursor[i]); } else flagSend = 0; }
当处于发送模式时,funcSelect变量是非零执行if语句之后就可以显示光标了。每次按上下左右键之后,也是在主函数中通过死循环不断调用sendMode()函数实现的。当处于发送模式时,同时按下上下键时,flagSend变量的值就变为1了。执行if条件语句中的while语句,调用sendData()函数将光标处的字符发送给单片机。
现在实现sendData函数的功能,在Main.c中编写代码如下
void sendData() { dataChar = keyX / 8 + keyY * 8; // 获取光标处的字符 SBUF = dataString[dataChar]; //发送光标处的字符 while(!TI); flagLed = 1; TI = 0; }
dataChar = keyX / 8 + keyY * 8; 语句是用dataChar变量来存放光标处的字符在dataString数组中的下标位置,然后通过dataString[dataChar]就可以访问dataString数组中的字符数据了,也就是处于光标处的字符数据。
SBUF = dataString[dataChar]; //发送光标处的字符
while(!TI);
这两行代码的功能是将光标处的字符存放于寄存器SBUF中,然后发送给电脑串口调式助手与手机的SSP蓝牙串口APP上显示。每次发送完数据后,要将TI中断标志置零,为下一次的串口中断成功发送数据做好准备。光标处的字符发送给电脑串口调试助手与手机的SSP蓝牙串口APP上的效果如下图所示
现在要实现的功能是单片机发送完数据后,LED灯实现流水灯的功能。在sendData()函数中,当每次发送完数据之后,flagLed变量的值就为1。
现在在Main.h中定义流水灯初始化的数组和变量如下
UINT ledData[8] = // led流水灯初始化 { 0xFE, 0XFD, 0XFB, 0XF7, 0xEF, 0XDF, 0XBF, 0X7F };
UINT ledTime = 2; // ledTime: 控制流水灯的次数
0xFE是表示8个LED灯中的D1灯为低电平,其他的七个LED灯处于高电平,所以访问ledData数组中的第一个数据时,只有第一个led灯点亮,其他的七个LED灯熄灭。
现在在主函数中的while(1)语句中编写如下代码实现流水灯的功能
if(flagLed == 1) // 单片机发送信息后, 流水灯流动两次 { while(ledTime > 0 ) { for(i = 0; i < 8; i++) { P1 =ledData[i]; delay(100); } ledTime--; } flagLed = 0; ledTime = 2; // 流水灯时, led和OLED共用P1^2和P1^3引脚 OLED_Init(); // 流水灯流动完后, 防止OLED出现乱码, 所以重新初始化OLED OLED_Fill(0x00); }
在实现流水灯流动两次之后,将flagLed变量赋值0和ledTime变量赋值2,为下一次的发送数据后实现流水灯的效果做准备。因为流水灯时,led和OLED共用P12和P13引脚,所以流水灯流动完后, 防止OLED屏幕出现乱码, 要重新初始化OLED,OLED屏幕就可以正常显示字符数据了。
编译程序,将生成的hex文件烧入单片机中。运行单片机,发送数据后流水灯流动的效果录制了“发送数据后LED实现流水灯”的视频文件与程序文件夹中。
现在已经实现了发送光标处的数据、发送数据后流水灯流动的效果了。
到这一步,我们的通讯功能的系统已经全部完成了。
七、总结
学习51单片机提高了我们对于C语言的编程能力,从最简单的点亮LED灯开始,就用到了C语言的循环的知识点。在LED流水灯中,我们组运用了C语言的变量、数组存储我们的数据、用顺序结构与循环结构相结合进行了不同算法的编程实现同一种功能。在许多的简单的编程中我们运用了C语言函数进行模块化的编程,将实现一种功能分为几个小的功能进行模块化编程。在进行51单片机的过程中,让我们可以分析实际的问题,并运用C语言解决实际的问题。让我们对于C语言有了进一步的掌握与C语言编程的能力。