- 此系列文章源于SYSU 2024电信学院通信工程专业工训课的训练题目,同时也是2024电子设计校内赛的题目。题目的要求描述如下:
无线手持二维码识别器是一个通过图像识别技术,对二维码图片进行识别,并解析出二维码的内容数据,并通过无线传输给手机或电脑的一种设备,通过这个设备,可以快速对物品进行扫描并在电脑端进行归档。要求能够对二维码图片进行扫描,二维码可自行生成,源信息包含字母和数字,能够支持将扫描数据上传到电脑,并在电脑端设计上位机进行数据显示,能够支持识别特殊二维码时,进行报警鸣叫处理,同时要使用3D建模软件对识别器进行建模并制作,大小符合手持。- 项目使用的主控为立创梁山派GD32F470ZGT6,项目源码存放地址为https://github.com/liangbm3/GD32_QR_Decoder
- 此系列文章更多的是对开发过程的记录和思路的呈现,文章分则可以作为教程实现单个功能,合则可以作为复现整个项目的教程。
- 文章的撰写部分参考了立创官方文档
- 文章未经作者许可,不得转载。
文章目录
前言
根据项目需求,需要实现显示二维码识别后的结果,显示菜单等功能,因此需要移植屏幕,我选用的是1.8寸的LCD屏幕,采用SPI通信协议,共8个引脚,参考商家给出的中景园stm32例程和手册,将屏幕移植到GD32上。
一、LCD的引脚定义
LCD屏幕的外观如图
引脚定义如图
二、SPI通信协议介绍
SPI(Serial Peripheral Interface)是一种同步串行通信协议,用于在微控制器和外部设备之间进行数据传输。它由一个主设备(通常是微控制器MCU)和一个或多个从设备组成,即一主多从模式。它通常用于短距离、高速、全双工的通信,它在许多嵌入式系统和电子设备中被广泛应用,如存储器芯片、传感器、显示器驱动器、无线模块等。
在SPI协议中,主设备是通信的发起方和控制方,而从设备则是被动接收和响应主设备的命令和数据。主设备通过时钟信号来同步数据传输,同时使用多个双向数据线来实现数据的传输和接收。
SPI协议是一种全双工通信方式,意味着主设备和从设备可以同时发送和接收数据。它还使用一种选择信号(通常称为片选或使能信号),用于选择与主设备进行通信的特定从设备。
- 主设备通过MOSI线向从设备发送数据。在每个时钟周期中,主设备将一个位发送到MOSI线上,从设备在下一个时钟周期中读取该位。
- 从设备通过MISO线向主设备发送数据。在每个时钟周期中,从设备将一个位发送到MISO线上,主设备在下一个时钟周期中读取该位。
- 数据传输可以是全双工的,即主设备和从设备可以同时发送和接收数据。
- 数据传输的长度可以是可变的,通常以字节为单位。
- 数据传输可以是单向的,即主设备只发送数据或只接收数据。
- 数据传输可以是多主设备的,即多个主设备可以与多个从设备进行通信。
SPI的四种逻辑线:
MISO:Master input slave output ,也叫SDO,主机输入,从机输出(数据来自从机);
MOSI:Master output slave input ,也叫SDI,主机输出,从机输入(数据来自主机);
SCLK :Serial Clock 串行时钟信号,也叫SCK,由主机产生发送给从机;
SS:Slave Select 片选信号,也叫CS,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。
SPI的接线示意图:
三、移植过程
1.引脚连线表
屏幕引脚 | 开发板引脚 |
---|---|
GND | GND |
VCC | 3V3 |
SCL | PB13 |
SDA | PB15 |
RES | PD0 |
DC | PG6 |
CS | PB12 |
BL | PG7 |
2.软件SPI
(1)将文件添加到工程
1.按照工程模板建立工程,本次移植基于上篇文章的usart工程进行添加。
2.在商家给出的配套资料中找到stm32f103c8t6的例程,找到LCD文件夹,复制其中的文件,然后复制到工程文件夹的/Hardware/LCD/目录,如图
3.将文件添加到工程,并添加include路径,如图
(2)修改头文件、引脚宏定义
1.将#include "delay.h"
删去,并把#include "sys.h"
改为#include "systick.h"
,因为需要用到延时操作,而在systick.h
中已经预先定义好了延时函数,然后我们在lcd_init.h
下添加#define delay_ms delay_1ms
即可使用GD32中的延时函数
2.在原来的代码中,使用了u8
、u16
、u32
等数据类型,这些是Keil C51编译器定义的数据类型,在GD32中无法被识别,因此我们需要在lcd.h和lcd_init.h中添加以下宏
#ifndef u8
#define u8 uint8_t
#endif
#ifndef u16
#define u16 uint16_t
#endif
#ifndef u32
#define u32 uint32_t
#endif
3.添加GD32头文件#include "gd32f4xx.h"
4.根据引脚连线进行引脚宏定义,方便后期修改
#define RCU_LCD_SCL RCU_GPIOB//SCK
#define PORT_LCD_SCL GPIOB
#define GPIO_LCD_SCL GPIO_PIN_13
#define RCU_LCD_SDA RCU_GPIOB//MOSI
#define PORT_LCD_SDA GPIOB
#define GPIO_LCD_SDA GPIO_PIN_15
#define RCU_LCD_CS RCU_GPIOB//NSS
#define PORT_LCD_CS GPIOB
#define GPIO_LCD_CS GPIO_PIN_12
#define RCU_LCD_DC RCU_GPIOG //DC
#define PORT_LCD_DC GPIOG
#define GPIO_LCD_DC GPIO_PIN_6
#define RCU_LCD_RES RCU_GPIOD//RES
#define PORT_LCD_RES GPIOD
#define GPIO_LCD_RES GPIO_PIN_0
#define RCU_LCD_BLK RCU_GPIOG//BLK
#define PORT_LCD_BLK GPIOG
#define GPIO_LCD_BLK GPIO_PIN_7
(3)将stm32相关函数用GD32相关函数重写
1.重写初始化函数
void LCD_GPIO_Init(void)
{
/* 使能时钟 */
rcu_periph_clock_enable(RCU_LCD_SCL);
rcu_periph_clock_enable(RCU_LCD_SDA);
rcu_periph_clock_enable(RCU_LCD_CS);
rcu_periph_clock_enable(RCU_LCD_DC);
rcu_periph_clock_enable(RCU_LCD_RES);
rcu_periph_clock_enable(RCU_LCD_BLK);
/* 配置SCL */
gpio_mode_set(PORT_LCD_SCL, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_SCL);
gpio_output_options_set(PORT_LCD_SCL, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SCL);
gpio_bit_write(PORT_LCD_SCL, GPIO_LCD_SCL, SET);
/* 配置SDA */
gpio_mode_set(PORT_LCD_SDA, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_SDA);
gpio_output_options_set(PORT_LCD_SDA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SDA);
gpio_bit_write(PORT_LCD_SDA, GPIO_LCD_SDA, SET);
/* 配置DC */
gpio_mode_set(PORT_LCD_DC, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_DC);
gpio_output_options_set(PORT_LCD_DC, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_DC);
gpio_bit_write(PORT_LCD_DC, GPIO_LCD_DC, SET);
/* 配置CS */
gpio_mode_set(PORT_LCD_CS, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_CS);
gpio_output_options_set(PORT_LCD_CS, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_CS);
gpio_bit_write(PORT_LCD_CS, GPIO_LCD_CS, SET);
/* 配置RES */
gpio_mode_set(PORT_LCD_RES, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_RES);
gpio_output_options_set(PORT_LCD_RES, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_RES);
gpio_bit_write(PORT_LCD_RES, GPIO_LCD_RES, SET);
/* 配置BLK */
gpio_mode_set(PORT_LCD_BLK, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_LCD_BLK);
gpio_output_options_set(PORT_LCD_BLK, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_BLK);
gpio_bit_write(PORT_LCD_BLK, GPIO_LCD_BLK, SET);
}
2.将端口的操作函数进行更改,如图
更改如下
#define LCD_SCLK_Clr() gpio_bit_write(PORT_LCD_SCL, GPIO_LCD_SCL, RESET)//SCL=SCLK
#define LCD_SCLK_Set() gpio_bit_write(PORT_LCD_SCL, GPIO_LCD_SCL, SET)
#define LCD_MOSI_Clr() gpio_bit_write(PORT_LCD_SDA, GPIO_LCD_SDA, RESET)//SDA=MOSI
#define LCD_MOSI_Set() gpio_bit_write(PORT_LCD_SDA, GPIO_LCD_SDA, SET)
#define LCD_RES_Clr() gpio_bit_write(PORT_LCD_RES, GPIO_LCD_RES, RESET)//RES
#define LCD_RES_Set() gpio_bit_write(PORT_LCD_RES, GPIO_LCD_RES, SET)
#define LCD_DC_Clr() gpio_bit_write(PORT_LCD_DC, GPIO_LCD_DC, RESET)//DC
#define LCD_DC_Set() gpio_bit_write(PORT_LCD_DC, GPIO_LCD_DC, SET)
#define LCD_CS_Clr() gpio_bit_write(PORT_LCD_CS, GPIO_LCD_CS, RESET)//CS
#define LCD_CS_Set() gpio_bit_write(PORT_LCD_CS, GPIO_LCD_CS, SET)
#define LCD_BLK_Clr() gpio_bit_write(PORT_LCD_BLK, GPIO_LCD_BLK, RESET)//BLK
#define LCD_BLK_Set() gpio_bit_write(PORT_LCD_BLK, GPIO_LCD_BLK, SET)
(4)进行测试
1.编写测试函数如下
int main()
{
float t = 0;
usart_gpio_config(115200);
dma_config();
systick_config();
LCD_Init();
LCD_Fill(0, 0, LCD_W, LCD_H, WHITE);
while (1)
{
delay_1ms(100);
LCD_ShowString(0, 0, "QQ", RED, WHITE, 24, 0);
LCD_ShowString(24, 30, "LCD_W:", RED, WHITE, 16, 0);
LCD_ShowIntNum(72, 30, LCD_W, 3, RED, WHITE, 16);
LCD_ShowString(24, 50, "LCD_H:", RED, WHITE, 16, 0);
LCD_ShowIntNum(72, 50, LCD_H, 3, RED, WHITE, 16);
LCD_ShowFloatNum1(20, 80, t, 4, RED, WHITE, 16);
t += 0.11;
LCD_ShowPicture(65, 80, 40, 40, gImage_1);
if (g_recv_complete_flag)
{
g_recv_complete_flag = 0;
printf("字节长度:%d ", g_recv_length);
printf("内容:%s\r\n", g_recv_buff);
memset(g_recv_buff, 0, g_recv_length);
g_recv_length = 0;
}
}
}
2.测试结果如下
3.硬件SPI
为了减少CPU资源的占用,充分利用梁山派开发板上的硬件资源,考虑将软件SPI改为硬件SPI。硬件SPI与软件SPI相比,硬件SPI是靠硬件上面的SPI控制器,所有的时钟边缘采样,时钟发生,还有时序控制,都是由硬件完成的。它降低了CPU的使用率,提高了运行速度。软件SPI就是用代码控制IO输出高低电平,模拟SPI的时序,这种方法通信速度较慢,且不可靠。
(1)选择SPI外设
通过查看手册,如图
PB13对应SPI1的SCK,复用功能为AF5;PB14对应SPI1的MISO,复用功能为AF5;PB15对应SPI1的MOSI,复用功能为AF5。由于屏幕只有数据输入,没有数据输出,使用GD32只需发送数据即可。因此选用PB13,PB15,片选由软件进行控制。
(2)进行代码添加
1.添加硬件SPI相关宏定义
#define RCU_SPI_HARDWARE RCU_SPI1
#define PORT_SPI SPI1
#define LINE_AF_SPI GPIO_AF_5
2.在初始化函数中添加SPI的初始化
添加开启SPI的时钟
rcu_periph_clock_enable(RCU_SPI_HARDWARE); // 开启SPI时钟
更改SCK、MOSI对应端口的配置,更改为
/*配置SPI的SCK GPIO*/
gpio_af_set(PORT_LCD_SCL, LINE_AF_SPI, GPIO_LCD_SCL);//设置端口复用
gpio_mode_set(PORT_LCD_SCL, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_LCD_SCL);
gpio_output_options_set(PORT_LCD_SCL, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SCL);
gpio_bit_set(PORT_LCD_SCL, GPIO_LCD_SCL);
/*配置SPI的MOSI GPIO*/
gpio_af_set(PORT_LCD_SDA, LINE_AF_SPI, GPIO_LCD_SDA);//设置端口复用
gpio_mode_set(PORT_LCD_SDA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_LCD_SDA);
gpio_output_options_set(PORT_LCD_SDA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_LCD_SDA);
gpio_bit_set(PORT_LCD_SDA, GPIO_LCD_SDA);
配置SPI的参数
/*配置SPI参数*/
spi_parameter_struct spi_init_struct;
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; //配置为传输模式全通工
spi_init_struct.device_mode = SPI_MASTER; //配置为主机
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; // 8位数据
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
spi_init_struct.nss = SPI_NSS_SOFT; // 软件CS
spi_init_struct.prescale = SPI_PSC_2;//2分频
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(PORT_SPI, &spi_init_struct);
最后使能SPI
spi_enable(PORT_SPI);
3.更改发送函数
根据官方手册,时钟空闲位高电平,数据在时钟信号的第二个时钟上升沿进行采样和稳定。
据此和和参考stm32的函数进行配置
原来的发送函数是通过模拟SPI时序进行发送的,如图
这段代码先是拉低了CS的电平,使能从机输入,然后拉低时钟线,主机逐位去输出8位数据,最后拉高时钟线,完成数据传输,最后失能从机输入。
读懂这段代码后,我们调用GD32的库函数,用硬件SPI进行实现,更改如下
LCD_CS_Clr();
while (RESET == spi_i2s_flag_get(PORT_SPI, SPI_FLAG_TBE))
;
spi_i2s_data_transmit(PORT_SPI, dat);
while (RESET == spi_i2s_flag_get(PORT_SPI, SPI_FLAG_RBNE))
;
spi_i2s_data_receive(PORT_SPI);
LCD_CS_Set();
这里通过查询发送缓冲区和状态缓冲区的状态寄存器,确保其为空时,再进行数据发送
(3)进行测试
采用上述提到的测试代码,测试得到相同的结果,说明是成功的。
本移植代码在我的二维码识别项目的SPI_LCD分支下,源码发布地址:https://github.com/liangbm3/GD32_QR_Decoder/releases/tag/SPI_LCDV1.0
如果觉得文章有帮助,请点赞收藏关注