在用AiCube学习自动配置硬件I2C和DMA之后点亮OLED12864之后,
感觉AiCube尝试自动配置硬件SPI和DMA也同样方便。
这里记录一下用AiCube自动配置硬件SPI和DMA点亮OLED的每一步过程。
==========================================================
1、配置 8H8K64U系列 单片机的硬件SPI
创建过硬件I2C之后,这个过程已经轻车熟路
1.1 在AiCube里创建一个 8H8K64U的项目
1.2 在【图形化I/O口配置】里,选择一组硬件SPI的引脚
这次选择的是第一组 (P5.4, P1.3, P1.4, P1.5)。
为了信号稳定,将时钟引脚(P1.5)、发送数据引脚(P1.3)和片选引脚(P5.4)配置成推挽输出模式。
然后在左侧端口列表里,将P1和P5勾选上,这样对引脚工作模式的配置才会生效。
照例将左侧列表里的【时钟】和【USB】都勾选上,为的是得到比较准确的延时函数,还有USB下载的功能。
1.3 在SPI配置窗口,
【主/从模式】选择默认的“主机模式”;
【传输顺序】和【时钟相位】都是用默认配置;
【时钟分频】选项里,默认的对输入时钟进行4分频,对OLED屏幕来说比较通用,
如果使用的是SSD1306芯片的屏幕,在40MHz的频率下,2分频也可以顺利点亮屏幕。
SPI中断和DMA的部分暂时暂时不启用。
硬件SPI的配置环节就完成了。
=======================================================
2、找一个模拟SPI的OLED驱动,添加进生成的工程里,然后用硬件SPI进行替换
2.1 先打开oled.h头文件
照例将AiCube框架里的系统配置文件 config.h 头文件添加到文件开头,这样就可以自由使用SPI的配置,以及数据类型缩写之类。
由于SPI的时钟和发送数据引脚,都由硬件SPI设备接管了,所以对于这两个引脚的定义部分可以注释掉。
(不注释掉也没关系,只是在程序中不再需要对这两个引脚进行手动操作)
只保留RES、D/C和CS的引脚定义就可以了。
2.2 在oled.c文件里,找到发送数据的函数 OLED_WR_Byte。
在函数里,先通过配置D/C引脚的高低电平,来告诉屏幕接下来发送的是控制指令还是内容数据。
然后使用AiCube的库函数里的SPI控制命令,对模拟SPI的语句进行替换。
瞬间清爽。
因为驱动程序里所有的功能函数,都是靠这个函数跟跟屏幕进行联系的,
所以改完这个函数,硬件SPI的替换过程也完成了。
2.3 在main.c文件里,添加OLED的头文件
最好是写在指定段落的 <BEGIN> 和 <END> 标记之间,这样在AiCube重新生成工程代码的时候就不会被清除掉。
然后在main函数的代码指定位置中,添加屏幕初始化函数和显示函数,就可以正常使用OLED屏幕了。
==================================================
3. 使用SPI的DMA外设点亮屏幕
3.1 在AiCube里打开刚才的配置文件,在硬件SPI的基础上添加DMA的配置部分。
(1) 这次在SPI的配置窗口中,选择“使能SPI DMA”。
(2) 因为是驱动OLED屏幕,【DMA模式】一栏选择“只发送数据”就可以。
(3) 【发送字节数】这一项,因为STC8H单片机的DMA每次最多可以传输256个字节,所以这里改成最大值 256。
(4) 【自动控制SS脚】这一栏,如果选择“是”,DMA设备会自动控制CS引脚的状态,此时屏幕上的CS引脚必须连接在这组硬件SPI指定的管脚P5.4上面;
如果选择“否”,就需要在屏幕驱动程序里使用控制代码,由CPU来控制CS引脚的状态。这次先选择“否”。
(5) SPI的DMA中断,也选择打开。
(6) 优先级选择默认的最低。
(7) 最后给DMA的发送缓冲区起一个名称。
DMA部分的配置就添加完成了。
重新生成工程框架之后,之前的一些配置可能会清除掉。
这时可以打开AiCube的备份工程文件(工程名_bk1.uvopt),就可以继续使用前面配置好的东西了。
==================================================
4、创建DMA方式的控制函数
4.1 打开spi.c文件,查看一下变化。
可以看到多出了DMA发送缓冲区的数组的声明,数组大小就是设置的256个字节。
这里需要手动添加一个表示DMA发送状态的标志位变量,比如 fSPIDMATxFlag。
当它等于1的时候表示数据正在发送过程中,等于0的时候表示数据发送完成。
在oled.h头文件里也声明一下这个变量。
然后还多了一个DMA发送的中断函数。
在DMA中断的函数里,每当DMA把数据发完之后,也将这个标志位变量清零。
在SPI初始化的函数里,AiCube也追加了关于DMA的配置代码。
可以直接使用。
4.2 接下来就是创建用DMA发送数据的函数
在oled.c文件里,跟创建I2C发送函数的过程一样,
首先创建发送控制指令和内容数据的函数 DMA_Send_Command 和 DMA_Send_Data
参数 cmd,表示要发送的指令的数组地址。
参数len,表示要发送的指令数量。
在函数中,先等待DMA空闲,防止打扰正在发送的数据。
等待结束之后,将DC引脚设置成对应的高低电平,告诉屏幕接下来发送的是控制指令还是内容数据。
片选使能引脚CS也保持低电平,确保OLED处于工作状态。
然后用memory copy命令,通过参数cmd和len,把要发送的控制指令,从CMD数组搬到DMA的发送缓冲区里,DMA发送字节数配置成len-1。
配置好之后就可以用触发SPI DMA的命令,把这些控制指令发送给屏幕了。
发送完成之后,将发送状态标志位手动置1,准备下一次的数据发送。
5. 有了这两个函数,就可以创建DMA的各种控制和显示的函数了。
因为有原先的那些函数,所以创建过程非常轻松。
5.1 用到控制指令的函数
比如屏幕初始化函数 OLED_Init,
只需要按照原本的代码结构,将控制指令部分变成一个数组,再用SendCommand函数一口气发送就可以了。
还有比较重要的 定位显示起始位置的函数 OLED_Set_Pos,
同样也是先将定位坐标值存入数组,再发送给屏幕。
5.2 有了定位函数,就可以创建各种显示函数了
比如清屏函数,比较通用的方法是,先创建一个有128个0的数组 DAT,
然后配合定位函数,分八次将数据发送到屏幕的八个行上。
(如果用的是AI8051U单片机,DMA每次最多可以发送65535个字节的数据,所以对于SSD1306之类支持水平地址模式的屏幕,可以一次性发送1024个0进行清屏)
根据这个思路,也可以创建出简单的显示图片的函数,
先计算出图片占用屏幕多少行,然后从指定的起始位置开始,向每一行里分别发送图片的对应行里的数据。
显示字符的函数也可以根据这个思路创建,只需要将原本的显示函数里的发送数据的部分,用DMA的方式进行替换就可以了。
代码可以在下方DMA的视频里看到。
最后,在oled.h头文件中把创建好的函数都声明一下,整个创建过程就完成了。
5.3 在main.c函数中,将之前的硬件SPI的显示函数,用DMA的函数替换,就可以用DMA的方式点亮OLED了。