15CubeMx+Keil+Proteus仿真STM32

编程

本文例子参考《STM32单片机开发实例——基于Proteus虚拟仿真与HAL/LL库》

源代码:https://github.com/LanLinnet/STM33F103R6

写在前面

在前面几节的基础上,我们已经基本了解了STM32F103的GPIO、外部中断、定时器、串口通信和一些片内外设,接下来几节都将对其常用的独立外设进行介绍。

项目要求

掌握LCD1602的驱动方法,要求在屏幕第一行显示“Hello World!”。

硬件设计

  1. 在第一节的基础上,在Proteus中添加电路如下图所示。其中我们添加了一个LCD1602液晶显示器LM016L

  2. LCD1602:

    1)简介:LCD1602液晶显示屏能够显示2行,每行16字符,共32个5x7或5x11的点阵字符。

    2)引脚:LCD1602采用标准的16脚接口,每个引脚的功能如下表所示。


    3)存储器:

    • DDRAM-指示显示字符的位置,其地址与字符显示位置的关系如下表所示;

    • CGRAM-用户自定义字模;
    • CGROM-内置160个常用字模,包括ASCII码、日文假名和希腊字母,本项目使用ASCII码显示。

    4)控制指令:LCD1602共有11条控制指令,如下表所示

  3. 打开CubeMX,建立工程。设置PA1-PA3、PC0-PC7均为GPIO_Output,点击“Categories”中的“GPIO”,将"GPIO Output level"设置为High,并设置“User Label”如下图所示。

  4. 点击“Generator Code”生成Keil工程。

软件编写

  1. 考虑到代码的可移植性,这里将LCD1602相关的功能代码全部封装成函数并归入头文件“LCD1602.h”。我们可以先在...15_LCD1602CoreSrc文件夹中建立该头文件,此时Keil可能找不到对应文件,可以直接将文件拽入Keil中进行编辑,然后再在“main.c”文件中进行include。

  2. 点击“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_

  3. 随后我们需要在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 */

联合调试

  1. 点击运行,生成HEX文件。
  2. 在Proteus中加载相应HEX文件,点击运行。可以看到LCD1602的第一行显示了“Hello World!”。

以上是 15CubeMx+Keil+Proteus仿真STM32 的全部内容, 来源链接: utcz.com/z/520532.html

回到顶部