gd32vw553 开发板入门(带 LCD1062 驱动)
分享作者:wx17467211408277
评测品牌:萤火工场
评测型号:GD32VW553-IOT
发布时间:2025-05-26 09:58:04
0
前言
这里记录一下点灯和点 1602 LCD 屏的过程。
开源口碑分享内容

一、资源下载与环境搭建

先去 RISC-V 下载专区,下载 《GD32VW553-IOT开源硬件下载资料》条目后的一个打包下载。

这篇文章中只用到以下文件:

  • GD32VW553-IOT开源硬件下载资料/应用软件/GD32AllInOneProgrammer_win_V4.2.10.28180.7z -> 串口下载工具。
  • GD32VW553-IOT开源硬件下载资料/应用软件/GD32EmbeddedBuilder_v1.4.12.28625.7z
    -> GD32 基于 Eclipse 制作的集成 ide,内置了 RISC-V 编译器和 GD32VW553-IOT 的硬件库支持。

解压即可完成搭建。

二、使用 GD32EmbeddedBuilder 创建默认点灯项目

建议参考 样片外观及GPIO测评

也没什么好写的就是简单步骤:

  1. 新建一个 c 项目。
  2. 填写项目名称,选择为 RISC-V 编译环境。
  3. 选择 GD32VW553HMQ7 芯片型号。

完成后就会有一个默认点灯的程序

三、官方点灯项目源码解析


#include "gd32vw55x.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "gd32vw553h_eval.h"

// 中断函数会调用这个 1ms 一次,所以刚好是 500ms 切换一次
void led_spark(void)
{
  static __IO uint32_t timingdelaylocal = 0U;
  if(timingdelaylocal) {
    if(timingdelaylocal < 500U) {
      gd_eval_led_on(LED2);
      gd_eval_led_on(LED3);
    } else {
      gd_eval_led_off(LED2);
      gd_eval_led_off(LED3);
    }

    timingdelaylocal--;
  } else {
    timingdelaylocal = 1000U;
  }
}

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
#ifdef __FIRMWARE_VERSION_DEFINE
  uint32_t fw_ver = 0;
#endif /* __FIRMWARE_VERSION_DEFINE */

  // 应该是初始化系统时钟
  systick_config();
  // 应该是初始化中断
  eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
  // 初始化几个 GPIO
  gd_eval_led_init(LED1);
  gd_eval_led_init(LED2);
  gd_eval_led_init(LED3);
  // 开启 USART,也就是串口通信
  gd_eval_com_init(EVAL_COM0);
  // 初始化一个 GPIO 为按键模式
  gd_eval_key_init(KEY_TAMPER_WAKEUP, KEY_MODE_GPIO);

#ifdef __FIRMWARE_VERSION_DEFINE
  fw_ver = gd32vw55x_firmware_version_get();
  /* print firmware version */
  printf("\r\nGD32VW55X series firmware version: V%d.%d.%d", (uint8_t)(fw_ver >> 24), (uint8_t)(fw_ver >> 16), (uint8_t)(fw_ver >> 8));
#endif /* __FIRMWARE_VERSION_DEFINE */

  // 打印硬件状态
  printf("\r\nCK_SYS is %d\r\n", rcu_clock_freq_get(CK_SYS));
  printf("\r\nCK_AHB is %d\r\n", rcu_clock_freq_get(CK_AHB));
  printf("\r\nCK_APB1 is %d\r\n", rcu_clock_freq_get(CK_APB1));
  printf("\r\nCK_APB2 is %d\r\n", rcu_clock_freq_get(CK_APB2));

  while(1) {
    // 检查 PA1 的状态,也就是按键按下
    if(RESET == gd_eval_key_state_get(KEY_TAMPER_WAKEUP)) {
      delay_1ms(50);
      if(SET == gd_eval_key_state_get(KEY_TAMPER_WAKEUP)) {
        // 按键抬起就切换,效果就是
        gd_eval_led_toggle(LED1);
      }
    }
  }
}

四、下载程序

下载程序有个 usb-ttl 就行了,想调试的话得买 gd-link 或者 jlink v9,我手里用的 CH341A 的 ttl 和另一个 DAPLink 的 ttl。

直接用之前新建的项目编译后获得一个 10+KB 的 xxx.bin 文件,打开 GD32 All In One Programmer 选择好文件。

用 ttl 方式连接开发板(可以用 ch341 或者各种 link 的 ttl 模式),插上后会看到串口多出一个,我这里是 COM3。

选择对应 COM 口,直接点 Connect 是不行的,因为这个开发板没有做下载按钮,记得先把开发板上的 r4 r5 直接短接(接个 100Ω 电阻也行)。

你要手动用跳线帽改了以后, boot0 设置为 1,按 reset 按键。

然后点连接就会连接上了,点击右手边的下载就会下载到开发板里了。

这个开发板的跳线帽比较松,我掉了一个直接把 boot1 的 0 接线直接焊死了。

ttl 接线

缺少的丝印

焊接了 r4 r5 的情况,下图情况是 boot0 = 0, boot1 = 0。

五、led 灯电路

这里用面包板把电路搭起来,原理就是 led 灯正极接 1k 电阻再接 3.3v 电源,负极接程序里的 GPIO 口。

搭建完硬件电路把开发板放上去,并且把 boot0 调整回 0, type-c 上电后就能看到 LED2,LED3 在间断闪烁,LED1 在长亮。

六、LCD1062 驱动代码

搜了老半天全网没有找到有人用 GD32 玩 LCD1602,然后就参考 51 的 4 线版本做了一个,实际上大部分逻辑是不用自己写的,核心就三个函数。

// 对应 LCD1602 上面的 7 个引脚,由于全是 GPIOA 就不定义 PORT 了,只定义 PIN
#define LCD_RS_PIN    	GPIO_PIN_4  // PA4
#define LCD_RW_PIN    	GPIO_PIN_5  // PA5 这个可以省掉,直接接 GND
#define LCD_E_PIN     	GPIO_PIN_6  // PA6

#define LCD_D4_PIN    	GPIO_PIN_0  // PA0
#define LCD_D5_PIN    	GPIO_PIN_1  // PA1
#define LCD_D6_PIN    	GPIO_PIN_2  // PA2
#define LCD_D7_PIN    	GPIO_PIN_3  // PA3

#include "gd32vw55x.h"
#include "systick.h"

#define LCD1602_RS_L() GPIO_BC(GPIOA) = LCD_RS_PIN
#define LCD1602_RS_H() GPIO_BOP(GPIOA) = LCD_RS_PIN

#define LCD1602_RW_L() GPIO_BC(GPIOA) = LCD_RW_PIN
#define LCD1602_RW_H() GPIO_BOP(GPIOA) = LCD_RW_PIN

#define LCD1602_E_L() GPIO_BC(GPIOA) = LCD_E_PIN
#define LCD1602_E_H() GPIO_BOP(GPIOA) = LCD_E_PIN

// 等待 1us
static void delay_us(uint32_t us)
{
  uint32_t i = us * 160;
  while (i--) {}
}


// 初始化所有用到的 GPIO 为输出模式,且无上下拉电阻模式,并且全部设置为低位
void LCD1602_4L_Init(void) {
  rcu_periph_clock_enable(RCU_GPIOA);
  // 配置RS引脚
  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LCD_RS_PIN);
  gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, LCD_RS_PIN);
  GPIO_BC(GPIOA) = LCD_RS_PIN;  // 初始置低

  // 配置RW引脚(建议接地,此处保留可配置性)
  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LCD_RW_PIN);
  gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, LCD_RW_PIN);
  GPIO_BC(GPIOA) = LCD_RW_PIN;  // 初始置低

  // 配置E引脚
  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LCD_E_PIN);
  gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, LCD_E_PIN);
  GPIO_BC(GPIOA) = LCD_E_PIN;    // 初始置低

  // 配置数据线D4-D7
  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LCD_D4_PIN);
  gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, LCD_D4_PIN);
  GPIO_BC(GPIOA) = LCD_D4_PIN;    // 初始置低

  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LCD_D5_PIN);
  gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, LCD_D5_PIN);
  GPIO_BC(GPIOA) = LCD_D5_PIN;    // 初始置低

  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LCD_D6_PIN);
  gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, LCD_D6_PIN);
  GPIO_BC(GPIOA) = LCD_D6_PIN;    // 初始置低

  gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LCD_D7_PIN);
  gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, LCD_D7_PIN);
  GPIO_BC(GPIOA) = LCD_D7_PIN;    // 初始置低

  // 初始化 lcd
  LCD1602_4L_WriteCMD(0x28);//四线模式
  LCD1602_4L_WriteCMD(0x0c);
  LCD1602_4L_WriteCMD(0x06);
  LCD1602_4L_WriteCMD(0x01);//清屏
  delay_us(50);
}


static void LCD1602_4L_Write(unsigned char cmd, unsigned char data) {
  // 写命令和写数据只有一个 RS 寄存器不同
  if (cmd) LCD1602_RS_L(); else LCD1602_RS_H();
  // RW 是可以不整的,如果不需要读的话
  LCD1602_RW_L();
  // 开启写入数据
  LCD1602_E_H();

  // 按倒序写 0b11110000 的数据到 D4-D7
  // gpio_bit_write 是 GPIO_BC GPIO_BOP 的包装
  gpio_bit_write(GPIOA, LCD_D4_PIN, data&0x10);
  gpio_bit_write(GPIOA, LCD_D5_PIN, data&0x20);
  gpio_bit_write(GPIOA, LCD_D6_PIN, data&0x40);
  gpio_bit_write(GPIOA, LCD_D7_PIN, data&0x80);

  // 等待并开启下一次写入
  delay_us(1);
  LCD1602_E_L();
  delay_us(1);
  LCD1602_E_H();

  // 按倒序写 0b00001111 的数据到 D4-D7
  gpio_bit_write(GPIOA, LCD_D4_PIN, data&0x01);
  gpio_bit_write(GPIOA, LCD_D5_PIN, data&0x02);
  gpio_bit_write(GPIOA, LCD_D6_PIN, data&0x04);
  gpio_bit_write(GPIOA, LCD_D7_PIN, data&0x08);

  // 最后再关闭写入
  delay_us(1);
  LCD1602_E_L();
	delay_us(100);
}

void LCD1602_4L_WriteCMD(unsigned char CMD) {
  LCD1602_4L_Write(1, CMD);
}

void LCD1602_4L_WriteData(unsigned char CMD) {
  LCD1602_4L_Write(0, CMD);
}

// 设置位置
void LCD1602_4L_SetCursor(unsigned char Line,unsigned char Column)
{
  if(Line==1)
  {
    LCD1602_4L_WriteCMD(0x80|(Column-1));
  }
  if(Line==2)
  {
    LCD1602_4L_WriteCMD(0x80|(Column-1)+0x40);
  }
  if(Line==3)
  {
    LCD1602_4L_WriteCMD(0x80|(Column-1)+20);
  }
  if(Line==4)
  {
    LCD1602_4L_WriteCMD(0x80|(Column-1)+0x54);
  }
}

// 补一个这个用来直接写文字,比较方便
void LCD1602_4L_PrintString(unsigned char Line,unsigned char Column,unsigned char *String)
{
  unsigned char i;
  LCD1602_4L_SetCursor(Line, Column);
  for(i=0;String[i]!=0;i++)
  {
    LCD1602_4L_WriteData(String[i]);
  }
}

// 补一个这个用来直接清理文字
void LCD1602_4L_Clear(unsigned char Line,unsigned char Column, unsigned char Length) {
  unsigned char i;
  LCD1602_4L_SetCursor(Line, Column);
  for(i=0;i<Length;i++)
  {
    LCD1602_4L_WriteData(' ');
  }
}

最后是示例运行代码

// 去掉了 LED 灯了,这里不需要
void led_spark(void) {}

int main(void)
{
#ifdef __FIRMWARE_VERSION_DEFINE
  uint32_t fw_ver = 0;
#endif /* __FIRMWARE_VERSION_DEFINE */

  systick_config();
  eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);

  // 串口还是初始化一下,在没有 link 来做调试的情况下可以用 printf 大法手动调试
  gd_eval_com_init(EVAL_COM0);
  
  // 初始化 lcd
  LCD1602_4L_Init();


#ifdef __FIRMWARE_VERSION_DEFINE
  fw_ver = gd32vw55x_firmware_version_get();
  /* print firmware version */
  printf("\r\nGD32VW55X series firmware version: V%d.%d.%d", (uint8_t)(fw_ver >> 24), (uint8_t)(fw_ver >> 16), (uint8_t)(fw_ver >> 8));
#endif /* __FIRMWARE_VERSION_DEFINE */

  /* print out the clock frequency of system, AHB, APB1 and APB2 */
  printf("\r\nCK_SYS is %d\r\n", rcu_clock_freq_get(CK_SYS));
  printf("\r\nCK_AHB is %d\r\n", rcu_clock_freq_get(CK_AHB));
  printf("\r\nCK_APB1 is %d\r\n", rcu_clock_freq_get(CK_APB1));
  printf("\r\nCK_APB2 is %d\r\n", rcu_clock_freq_get(CK_APB2));

  while(1) {
    // 每两秒切换文字,在两行上,并且清理掉之前的文字。
    LCD1602_4L_PrintString(1, 1, "GD32,OK");
    delay_1ms(2000);
    LCD1602_4L_Clear(1, 1, 16);
    LCD1602_4L_PrintString(2, 1, "iCEasy,Yes");
    delay_1ms(2000);
    LCD1602_4L_Clear(2, 1, 16);
  }
}

顺便说一下我的接线:


LCD 针脚目标说明
GNDGND接开发板地
VDD+5V接开发板5V(必须用 5V 给开发板供电)
VO2KΩ -> GND通过 2KΩ 接开发板地
RSPA4接开发板 PA4
RWPA5接开发板 PA5(可以忽略直接接地)
EPA6接开发板 PA6
D4-D7PA0-PA34 个数据线接到开发板的 PA0-PA3
BLA100Ω -> +5V通过 100Ω 接开发板 5V
BLKGND接开发板地

程序写入后接线的效果

全部评论
暂无评论
0/144