目录
理论部分
三种常用的程序调试方法
第一种是串口调试
第二种是显示屏调试
第三种是Keil调试模式
其他调试方式
OLED显示屏的介绍
OLED的硬件电路
OLED驱动程序中所包含的驱动函数
OLED_Init();
OLED_Clear();
OLED的显示函数
OLED_ShowChar(1, 1, 'A');
OLED_ShowString(1, 3, "HelloWorld!");
OLED_ShowNum(2, 1, 12345, 5);
OLED_ShowSignedNum(2, 7, -66, 2);
OLED_ShowHexNum(3, 1, 0xAA55, 4);
OLED_ShowBinNum(4, 1, 0xAA55, 16);
代码部分
OLED.c
OLED.h
main.c
Keil调试模式演示
声明:本专栏是本人跟着B站江科大的视频的学习过程中记录下来的笔记,我之所以记录下来是为了方便自己日后复习。如果你也是跟着江科大的视频学习的,可以配套本专栏食用,如有问题可以QQ交流群:963138186
本篇来学习一下OLED调试工具,至于OLED屏幕的原理和代码编写以后再讲,目前只要知道怎么用的就可以了。
理论部分
这就是OLED显示屏:
我们用OLED当一个调试的显示屏,方便我们调试程序。
这个OLED模块和我之前在51单片机专栏里介绍LCD模块的驱动函数基本都是一模一样的。所以上手这个OLED屏幕应该还是比较容易的。
三种常用的程序调试方法
首先我们先来看一下目前常用的程序调试方式。
这里列举了三种常用的程序调试方法:
第一种是串口调试
第一种是串口调试,它是通过串口通信将调试信息发送到电脑端,电脑使用串口助手显示调试信息。这种调试方式是非常见的,就是用串口将单片机连接到电脑,相当于把电脑的屏幕挂在单片机上,方便单片机显示调试信息。
一般我们设计pcb电路板的时候,都会在pcb边上留一个串口通信的硬件,需要调试程序的时候,把单片机连在电脑上,然后就可以在电脑屏幕打印信息了。
调试好之后,就断开串口程序,就能正常工作了。
第二种是显示屏调试
第二种是显示屏调试,它是直接将显示屏连接到单片机,将调试信息打印在显示屏上,这个方法就比较直接,单片机没有显示参数的东西,我们就直接接个屏幕上去。这里我们使用的是这种比较小巧的0.96寸OLED显示屏,使用还是非常方便的。
串口调试和显示屏调试也是各有利弊的:
串口的优势是可以借助强大的电脑来调试,电脑端的软件不仅可以显示单独的参数,而且可以显示曲线、图形、图像等,还可以自己做一个软件,来实现一个强大的用户交互界面。串口调试的弊端就是调试的时候需要拖着电脑,而且通常的串口助手只能以信息流的方式呈现数据,就是只能一行一行的打印。如果有很多不断变化的数据要显示,就只能在电脑上刷屏显示,这样用起来就不太方便。
而显示屏调试的优势是对于不断变化的数据可以覆盖刷新显示,而且显示屏可以始终接在单片机上,显示方式很直接。还有一般我们做一些稍微复杂的东西,都会需要有一个人机交互的界面,这样就可以直接把显示屏当做产品的一部分,还是比较好用的。显示屏的弊端就是屏幕太小,显示内容有限,没有电脑软件么强大的功能。
所以这两种方法各有各的适用场景,需要根据实际情况选择合适的方法。
第三种是Keil调试模式
第三种是Keil调试模式,它是借助keil软件的调试模式,可使用单步运行,设置断点查看寄存器及变量等高级功能。Keil调试模式的功能还是非常强大的。这个在下一节可以给大家具体演示一下。
其他调试方式
当然除了这三种方法之外,还有很多种程序调试的方法:
比如点灯调试法,即如果你不清楚程序是不是执行到了某个位置,就可以在那个位置放一个点灯的代码,如果运行到了灯就亮,没运行到灯就不亮。
还有注释调试法,如果你的程序原来是好的,但是加了某段程序就死了。这时你可以把新加入的程序全部注释掉,恢复到正常运行的状态,然后依次一行一行的解除注释,直到错误出现。或者注释掉一部分来测试另一部分,然后再注释掉另一部分测试这一部分,减少程序运行的部分,来定位出问题的程序在哪里。
另外还有对照法,就是你跟着别人写了一个的程序,感觉它的逻辑没问题。但是别人的可以运行,你的却不能运行,这时就可以对照一下,在他的程序逻辑上逐步替换成你的程序逻辑。这样就比较容易发现错误在什么地方。
总之测试程序的基本思想就是缩小范围、控制变量、对比测试等,测试的方法是多样的。
OLED显示屏的介绍
我们来看一下OLED显示屏的介绍。
OLED(Organic Light Emitting Diode):有机发光二极管
OLED显示屏的特点:性能优异的新型显示屏,具有功耗低(是因为OLED每一个像素都是一个单独的发光二极管,每一个像素都是可以自发光的,不像LCD样需要有背光,所以OLED屏幕会比较省电)、相应速度快(这个特性可以使OLED有更高的刷新率,总线时序快,也可以避免阻塞我们的程序)、宽视角(因为OLED屏幕是自发光的,所以在任何角度看显示内容都是清晰的。而LCD的话,如果从上面或者下面斜着看这个屏幕,屏幕就会变黑,很难看清上面的内容。OLED屏幕基本上各个视角都可以看清屏幕)、轻薄柔韧(比如现在手机上的折叠屏,柔性屏等用的就是OLED显示屏)等特点。
我们学习用的0.96寸OLED模块:小巧玲珑、占用接口少、简单易用,是电子设计中非常常见的显示屏模块(这个显示屏也算是单片机领域的一个经典显示屏)。当然它不会像我们手机电脑上的屏幕那么强大,这个OLED一般都只有一种颜色,分辨率也比较低。驱动这个OLED只需要几根线和简单的通信协议,占用资源比较少。
供电:3~5.5V,通信协议:I2C/SPI(一般用的是I2C的通信协议,七针脚的屏幕一般用的是SPI的通信协议),分辨率:128*64
OLED有几种规格,第一种就是我们学习使用的
总共有四个针脚,像素是白色的。
右边这里还有一种七针脚版本的,需要占用的IO口就多一些。除了有白色像素的版本,还有蓝色像素的版本和黄蓝双色的版本。这个黄蓝双色并不是真正的双色,它是上面四分之一的像素固定是黄色的,下面四分之三的像素固定是蓝色。这种屏幕适合做一个需要显示标题行的界面。上面的黄色显示标题,下面的蓝色显示内容,这样比较好看一些。
OLED的硬件电路
接着我们来看一下OLED的硬件电路。
这个是四针脚版本的OLED电路,
SCL和SDA是I2C的通信引脚,需要接在单片机I2C通信的引脚上。当然我们用的驱动函数模块用的是GPIO口模拟的I2C通信,所以这两个端口就可以接在任意的GPIO口上。
然后右边七针脚版本的OLED电路也是一样
除了VCC和GND引脚,剩下的引脚是SPI通信协议的引脚,如果是GPIO口模拟的通信协议,也是接在任意的GPIO口上就行了。
最后来看一下驱动函数模块都有哪些东西:
OLED驱动程序中所包含的驱动函数
这个表里的就是OLED驱动程序中所包含的驱动函数,
OLED_Init();
这个是OLED初始化函数,在使用OLED之前需要调用一下这个函数,对OLED进行初始化,然后就可以正常使用了。
OLED_Clear();
这个是清屏函数,调用这个函数屏幕就会清空。
然后下面的这些函数就都是OLED的显示函数了:
OLED的显示函数
OLED_ShowChar(1, 1, 'A');
首先,OLED_ShowChar(1, 1, 'A');
显示一个字符,第一个参数指定起始行。第二个参数指定起始列。第三个参数是要显示的字符,比如一行一列显示字符A,在屏幕上一行一列的位置显示一个A。
OLED_ShowString(1, 3, "HelloWorld!");
接着是OLED_ShowString(1, 3, "HelloWorld!");
显示字符串,比如在一行三列显示字符串。helloword感叹号。
OLED_ShowNum(2, 1, 12345, 5);
然后OLED_ShowNum(2, 1, 12345, 5);
这个是显示无符号十进制数字,前两个参数指定起始坐标。第三个参数是要显示的数字,第四个参数是数字的长度。比如二行一列显示数字12345,长度为5。
OLED_ShowSignedNum(2, 7, -66, 2);
剩下的OLED_ShowSignedNum(2, 7, -66, 2);
就是显示有符号十进制数字,在显示的时候会带一个正负号。
OLED_ShowHexNum(3, 1, 0xAA55, 4);
然后OLED_ShowHexNum(3, 1, 0xAA55, 4);
显示十六进制数。
OLED_ShowBinNum(4, 1, 0xAA55, 16);
显示二进制数。
以下是显示的效果
对应OLED屏幕的坐标图:
以下就是开始演示如何使用OLED显示屏的驱动函数模块
代码部分
首先我们还是看一下接线图,
以后我们就一直把这个屏幕拆在这里,需要用的时候随时可以使用,而且放在右下角也不是很占地方。
我们可以在OLED的下面,像这样先插上两根线,
把OLED的GND引到负极供电孔,OLED的VCC引到正极供电孔。另外这个供电孔也会同时接到STM32的PB6和PB7两个引脚,之后我们直接不初始化这两个引脚就行了。STM32的引脚上电后,如果不初始化,默认是浮空输入的模式。在这个模式下引脚不会输出电平,所以不会有什么影响。
当然你也可以不接这两根跳线
直接给PB6口输出低电平,PB7口输出高电平,用GPIO口直接给OLED供电,这个也是没问题的,因为这个OLED功率很小,所以也是可以驱动的。不过这种用GPIO口供电不是很规范。但是要做实际项目的话,最好还是用电源供电。
插好这两个供电跳线之后,
再把OLED插到PB6到PB9这四个口,这样OLED就接好了。
此时OLED的SCL接到PB8,SDA接到PB9。
接好电路后,我们和之前一样复制上一节的工程文件夹,改一下名字
我们把这个文件夹打开
领取链接:链接:https://pan.baidu.com/s/1Z6TlQZn9S30udgyazkr_Hw
提取码:cbr6
里面有两个版本,
我们打开4针脚的版本的文件夹,里面有三个文件,全部复制
然后到OLED显示屏这个工程里来打开hardware文件夹,把它们粘贴到这里来。
之后打开在Keil里面的hardware组添加这个三个文件的操作和之前的操作都是一样的,不再赘述。
我们打开分别看一下这三个文件都是什么东西。
OLED.c
首先OLED.c里面就是函数的主体代码了,里面包括了引脚配置,引脚初始化,I2C通信的基本时序和OLED用户调用的代码等。
这些函数都已经写好了,绝大部分都不需要更改.
我们需要更改的,就只有上面的这两部分代码:
第一部分是引脚配置,这里选择的是你硬件电路上把SCL和SDA这两个引脚接在了哪两个端口上。比如我们这里SCL接在了PB8。
这个地方就是GPIOB,GPIO_Pin_8,
如果你换个端口,比如接在PA6上,这个地方就要改成GPIOB,GPIO_Pin_6。
下面这个SDA的引脚配置也是一样,SDA接在了哪个位置,这里就改成对应的端口。
然后下面这个OLED_I2C_Init函数里也得更改。
这里面就是把SCL和SDA的两个引脚都初始化为开漏输出的模式。
对于这个模块来说,这里默认用的是SCL接PB8,SDA接PB9,如果你想修改,先把上面两行引脚配置改一下,再把下面这里的端口初始化改一下,剩下的都不需要修改,就可以直接使用这个OLED驱动函数模块了
OLED.h
接下来OLED.h里面,这里就是外部可调用函数的声明,
最后是OLED_Font.h文件,这里存的是OLED的字库数据。
因为这个OLED显示屏是不带字库的,想要显示字符图形,还得先定义字符的点阵数据。这里就是这些字符的点阵数据,也就是字库。OLED.c文件的显示函数会用到这些数据,我们需要把这个文件复制过来,放到工程里。当然,字库也是不用我们修改的。
接下来我们试用一下。
main.c
在主函数中调用这些函数,请认真看一下以下代码注释
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化/*OLED显示*/OLED_ShowChar(1, 1, 'A'); //1行1列显示字符AOLED_ShowString(1, 3, "HelloWorld!"); //1行3列显示字符串HelloWorld!OLED_ShowNum(2, 1, 12345, 5); //2行1列显示十进制数字12345,长度为5,//注:如果写长度为6,那么它就会在12345前面补0,如果指定的长度比要显示的数字长度小,那它就会把高位的数据切掉OLED_ShowSignedNum(2, 7, -66, 2); //2行7列显示有符号十进制数字-66,长度为2//注:指定的长度不包括符号位,如果写OLED_ShowSignedNum(2, 7, 12345, 5)显示效果就是+12345OLED_ShowHexNum(3, 1, 0xAA55, 4); //3行1列显示十六进制数字0xA5A5,长度为4//注:指定长度不包含0xOLED_ShowBinNum(4, 1, 0xAA55, 16); //4行1列显示二进制数字0xA5A5,长度为16//注:C语言无法直接写出二进制数字,故需要用十六进制表示,但是在显示屏上我们看到的是二进制数:1010101001010101//OLED_Clear();//调用这个函数就清屏了,OLED上不会显示任何东西了//如果你只想要清除掉部分数据,可以用OLED_ShowString在你想要清掉的地方显示空格字符,//比如://OLED_ShowString(1, 3, " ");//这样就把OLED_ShowString(1, 3, "HelloWorld!");这句代码之前显示的HelloWorld!给清除掉了while (1){}
}
显示效果:
STM32-OLED调试工具
Keil调试模式演示
接下来演示一下keil的调试模式。
这里用我之前介绍的第一个LED闪烁的工程作为例子。
然后工程选项debug这里可以对调试选项进行配置。
这里默认是选择右边的这一项,这个是在硬件上在线仿真需要我们把stlink或者DAP和STM32都连接好。
如果你不想连接硬件,也可以选择左边的使用仿真器这个选项,这样就是电脑模拟STM32的运行了。
我们就使用硬件的在线仿真。
首先,在进入调试模式之前,需要先连接好STM32之后编译一下,确保工程没有问题。
然后点击这里的放大镜里面带一个d的图标进入调试模式。
在这个界面里,主窗口就是我们的C语言程序。
而上面这个窗口就是C语言翻译成的汇编程序。
感兴趣的话可以对照这里看一下每句c语言实际上都执行了哪些操作。
然后左边的这个窗口是寄存器组和状态标志位等信息。
这个是单片机硬件底层很重要的东西,如果你用汇编编程的话,这些东西都是必须要非常清楚的。但如果你用的是C语言,这些东西就不用管的。
之后上面这一部分是程序运行控制的,
第一个是复位,第二个是全速运行,第三个是停止全速运行。
紧接着后面四个分别是单步运行、跳过当前行单步运行,跳出当前函数单步运行、跳到光标指定行单步运行。
我们可以试一下:
这个黄色箭头指示的就是下一句将要执行的代码。
我们当前的程序就是从main函数开始的,我们可以点一下单步运行,它就执行到了下一行,
然后再单步,它就进到了RCC这个函数里面来了,
点击这个跳出函数,它就可以跳到函数外面来了,
指定这一行,
然后点击运行到光标指定行,程序就运行到这个位置了。
我们可以点击程序,左边这里深灰色的区域设置断点。
然后点击全速运行,程序就会一直运行,直到断点停下。如果没有断点的话,再全速运行程序就不会自动停下来了。我们就需要点击这里的停止按钮,这样程序就会停下来了。
然后我们点击RST复位,程序就会回到最开始的地方,
这里我们可以看到现在程序是在启动文件的复位中断函数里,
说明复位后程序是从这里开始执行的,我们点击单步运行后,可以看到程序会跳到SystemInit函数里。
这里继续单步运行,然后跳出函数继续运行,应该就会执行main函数,不过它突然停不下来了,没关系,我们点击停止一下,然后点击复位,然后指定到main函数,点击直接运行到当前行,现在可以看到程序就执行到了这一行然后停下来了
这就是调试模式下控制程序运行的方法,这个方法可以精确追踪我们的程序是如何运行的。
如果你不清楚程序是如何一步步运行的在这个调试模式里,单步运行探索一下。
接着我们来看一下上面的这一堆功能。
第一个是命令窗口,我们点击它可以打开和关闭命令窗口。
第二个是反汇编窗口,可以打开和关闭上面的汇编窗口。
第三个是符号窗口。
点击后右边会出现如图所示窗口,在这里我们可以实时查看程序中所有变量的值,
比如想看一下这个GPIO_InitStructure结构体的值
就可以在这个窗口里找一下,main.c里面,然后main函数这里就可以看到这个变量了。
里面可以看到结构体的三个成员。
如果想看一下结构体值的变化,可以在这里右键。添加到watch1窗口。
在这里就能看到结构体的值了。
比如我们在这里单步运行GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0这一句后,这里就能看到这个变量值的变化了。
然后剩下的这里还有串口显示、逻辑分析仪等等。
这些工具的功能也都是非常强大的,大家可以自行了解一下。
另外我们还可以点击这个外设菜单栏,系统资源,查看这里就可以看到所有的外设寄存器。
比如我们选择GPIOA
右边的这里就会显示GPIOA外设的所有寄存器。
我们这个程序是翻转PA0端口,我们就可以看一下ODR寄存器,这个就是PA0的输出数据寄存器,
接下来我们运行,可以看到这个ODR0是实时显示输出寄存器的变化的。
我们再在面包板上的PA0插上一个LED。可以看到,STM32也是在实施执行程序的。
虽然说这个实时变化会有一些延迟,但不得不说这个在线调试的功能还是非常强大的。
STM32实时执行程序,Keil软件实时显示外设寄存器状态,还能随时控制程序的开始、停止、单步运行。
所以当你以后遇到一个比较难调的程序,比如不清楚程序是如何执行的,或者想要看一大堆变量,却不方便显示的,或者想看一下寄存器是不是配置正确了。都可以考虑一下使用这个keil自带的调试模式。
最后再说明一下,如果你想修改程序的话,是不能直接在这个调试模式下修改的,修改程序得先退出调试模式
点击原来这里可以退出调试模式
重新编译后,再进入调试模式。
我们也可以点这里的帮助,第一项这里可以打开keil软件的官方帮助文档,
这个帮助文档里有对这整个软件最权威最细致的介绍。
如果你对某个功能不熟悉的话,可以在这里找一找。
以上就是本节的全部内容,我们下篇继续。
QQ交流群:963138186
本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓