15CubeMx+Keil+Proteus仿真STM32
本文例子参考《STM32单片机开发实例——基于Proteus虚拟仿真与HAL/LL库》
源代码:https://github.com/LanLinnet/STM33F103R6
写在前面
在前面几节的基础上,我们已经基本了解了STM32F103的GPIO、外部中断、定时器、串口通信和一些片内外设,接下来几节都将对其常用的独立外设进行介绍。
项目要求
掌握LCD1602的驱动方法,要求在屏幕第一行显示“Hello World!”。
硬件设计
在第一节的基础上,在Proteus中添加电路如下图所示。其中我们添加了一个LCD1602液晶显示器
LM016L
。LCD1602:
1)简介:LCD1602液晶显示屏能够显示2行,每行16字符,共32个5x7或5x11的点阵字符。
2)引脚:LCD1602采用标准的16脚接口,每个引脚的功能如下表所示。
3)存储器:
- DDRAM-指示显示字符的位置,其地址与字符显示位置的关系如下表所示;
- CGRAM-用户自定义字模;
- CGROM-内置160个常用字模,包括ASCII码、日文假名和希腊字母,本项目使用ASCII码显示。
4)控制指令:LCD1602共有11条控制指令,如下表所示
- DDRAM-指示显示字符的位置,其地址与字符显示位置的关系如下表所示;
打开CubeMX,建立工程。设置PA1-PA3、PC0-PC7均为
GPIO_Output
,点击“Categories”中的“GPIO”,将"GPIO Output level"设置为High
,并设置“User Label”如下图所示。点击“Generator Code”生成Keil工程。
软件编写
考虑到代码的可移植性,这里将LCD1602相关的功能代码全部封装成函数并归入头文件“LCD1602.h”。我们可以先在
...15_LCD1602CoreSrc
文件夹中建立该头文件,此时Keil可能找不到对应文件,可以直接将文件拽入Keil中进行编辑,然后再在“main.c”文件中进行include。点击“Open Project”在Keil中打开工程,打开“LCD1602.h”,添加代码如下。
#ifndef INC_LCD1602_H_
#define INC_LCD1602_H_
#include "main.h"
//选择数据寄存器
#define RS_DataR() HAL_GPIO_WritePin(GPIOA, RS_Pin, GPIO_PIN_SET)
#define RS_InstructionR() HAL_GPIO_WritePin(GPIOA, RS_Pin, GPIO_PIN_RESET)
//选择指令寄存器
//读操作
#define RW_Read() HAL_GPIO_WritePin(GPIOA, RW_Pin, GPIO_PIN_SET)
//写操作
#define RW_Write() HAL_GPIO_WritePin(GPIOA, RW_Pin, GPIO_PIN_RESET)
//Enable操作:高电平-读取信息;下降沿-执行指令
#define E_Set() HAL_GPIO_WritePin(GPIOA, E_Pin, GPIO_PIN_SET)
#define E_Rst() HAL_GPIO_WritePin(GPIOA, E_Pin, GPIO_PIN_RESET)
/*************************************自定义函数****************************************/
//D0-D7设定方向:I-输入;O-输出
void DataDir(char dir)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
HAL_GPIO_WritePin(GPIOC, D0_Pin|D1_Pin|D2_Pin|D3_Pin|D4_Pin|D5_Pin|D6_Pin|D7_Pin, GPIO_PIN_SET);
GPIO_InitStruct.Pin = D0_Pin|D1_Pin|D2_Pin|D3_Pin|D4_Pin|D5_Pin|D6_Pin|D7_Pin;
GPIO_InitStruct.Pull = GPIO_PULLUP;
if(dir == "I")
{
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
}
else if(dir == "O")
{
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
}
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
//D0-D7读数据
uint8_t ReadData()
{
uint8_t dat=0;
//DataDir("I");
if(HAL_GPIO_ReadPin(GPIOC, D0_Pin)==GPIO_PIN_SET) dat|=0x01;
if(HAL_GPIO_ReadPin(GPIOC, D1_Pin)==GPIO_PIN_SET) dat|=0x02;
if(HAL_GPIO_ReadPin(GPIOC, D2_Pin)==GPIO_PIN_SET) dat|=0x04;
if(HAL_GPIO_ReadPin(GPIOC, D3_Pin)==GPIO_PIN_SET) dat|=0x08;
if(HAL_GPIO_ReadPin(GPIOC, D4_Pin)==GPIO_PIN_SET) dat|=0x10;
if(HAL_GPIO_ReadPin(GPIOC, D5_Pin)==GPIO_PIN_SET) dat|=0x20;
if(HAL_GPIO_ReadPin(GPIOC, D6_Pin)==GPIO_PIN_SET) dat|=0x40;
if(HAL_GPIO_ReadPin(GPIOC, D7_Pin)==GPIO_PIN_SET) dat|=0x80;
return dat;
}
//D0-D7写数据
void WriteData(uint8_t dat)
{
uint16_t Set_Pins = 0, Rst_Pins = 0;
//DataDir("O");
if(dat & 0x01) Set_Pins |= D0_Pin;
else Rst_Pins |= D0_Pin;
if(dat & 0x02) Set_Pins |= D1_Pin;
else Rst_Pins |= D1_Pin;
if(dat & 0x04) Set_Pins |= D2_Pin;
else Rst_Pins |= D2_Pin;
if(dat & 0x08) Set_Pins |= D3_Pin;
else Rst_Pins |= D3_Pin;
if(dat & 0x10) Set_Pins |= D4_Pin;
else Rst_Pins |= D4_Pin;
if(dat & 0x20) Set_Pins |= D5_Pin;
else Rst_Pins |= D5_Pin;
if(dat & 0x40) Set_Pins |= D6_Pin;
else Rst_Pins |= D6_Pin;
if(dat & 0x80) Set_Pins |= D7_Pin;
else Rst_Pins |= D7_Pin;
HAL_GPIO_WritePin(GPIOC, Set_Pins, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, Rst_Pins, GPIO_PIN_RESET);
}
//LCD忙等待
void LCD_Busy_Wait()
{
uint8_t status;
DataDir("I");
RS_InstructionR();
RW_Read();
do
{
E_Set();
__NOP();
status = ReadData();
E_Rst();
}
while(status & 0x80);
}
//写LCD指令
void LCD_Write_Cmd(uint8_t cmd)
{
DataDir("O");
WriteData(cmd);
RS_InstructionR();
RW_Write();
E_Rst();
RS_InstructionR();
RW_Write();
E_Set();
__NOP();
E_Rst();
LCD_Busy_Wait();
}
//写LCD数据寄存器
void LCD_Write_Data(uint8_t dat)
{
DataDir("O");
WriteData(dat);
RS_DataR();
RW_Write();
E_Set();
__NOP();
E_Rst();
LCD_Busy_Wait();
}
//LCD初始化
void LCD_Init()
{
LCD_Write_Cmd(0x38);
HAL_Delay(2);
LCD_Write_Cmd(0x01);
HAL_Delay(2);
LCD_Write_Cmd(0x06);
HAL_Delay(2);
LCD_Write_Cmd(0x0c);
HAL_Delay(2);
}
//在x行(0-1),y列(0-15)显示字符串
void LCD_ShowString(uint8_t x, uint8_t y, char *str)
{
uint8_t i=0;
//设置显示起始位置
if(x == 0)
LCD_Write_Cmd(0x80|y);
else if(x == 1)
LCD_Write_Cmd(0xc0|y);
//输出字符串
for(i=0; i<16 && str[i]!=""; i++)
{
LCD_Write_Data(str[i]);
HAL_Delay(2);
}
}
#endif //INC_LCD1602_H_
随后我们需要在main.c文件中的最前面引入我们自定义的头文件
/* USER CODE BEGIN Includes */
#include “LCD1602.h”
/* USER CODE END Includes */
在main函数中定义需要在LCD中显示的字符串
/* USER CODE BEGIN 1 */
char str[]="Hello World!"; //输出字符串内容设置
/* USER CODE END 1 */
最后,调用我们自定义的函数对LCD进行操作
/* USER CODE BEGIN 2 */
LCD_Init(); //初始化LCD1602
LCD_ShowString(0,0,str); //LCD 显示设定字符串
/* USER CODE END 2 */
联合调试
- 点击运行,生成HEX文件。
- 在Proteus中加载相应HEX文件,点击运行。可以看到LCD1602的第一行显示了“Hello World!”。
以上是 15CubeMx+Keil+Proteus仿真STM32 的全部内容, 来源链接: utcz.com/z/520532.html