STM32103CBT6显示ST7789通过SPI方式显示柬埔寨文

为了在STM32103CBT6微控制器上通过SPI接口驱动ST7789 LCD显示屏并显示柬埔寨文,我们需要完成以下几个步骤:

  1. 初始化硬件:配置STM32的GPIO和SPI外设。
  2. 编写LCD驱动代码:实现ST7789的基本操作函数。
  3. 处理字符集:加载支持Cambodia字体的数据,并将其转换为屏幕上的像素点。
  4. 显示文本:编写函数来将字符串渲染到LCD上。

下面是一个完整的示例代码,展示了如何在STM32103CBT6上通过SPI接口驱动ST7789 LCD显示屏并显示"សួស្តី"(意思是"你好")。

硬件准备

  • STM32F103CBT6开发板
  • ST7789VW LCD显示屏

软件准备

  • STM32CubeMX用于初始化外设配置
  • STM32CubeIDE用于编写代码

示例代码

1. 使用STM32CubeMX配置项目

使用STM32CubeMX生成初始化代码。以下是关键配置:

  • SPI1: Mode -> Master, Data Size -> 8 Bits, Clock Polarity -> High, Clock Phase -> 2 Edge, NSS -> Software, Baud Rate Prescaler -> 256 (1 MHz), First Bit -> MSB, CRC Calculation -> Disable
  • GPIO:
    • PA5 -> SCK (SPI1_SCK)
    • PA6 -> MISO (SPI1_MISO)
    • PA7 -> MOSI (SPI1_MOSI)
    • PB1 -> RST (Reset Pin)
    • PB0 -> DC (Data/Command Control Pin)
    • PE1 -> CS (Chip Select Pin)
2. 编写LCD驱动代码

创建一个文件lcd_st7789.hlcd_st7789.c来包含ST7789的基本操作函数。

lcd_st7789.h
复制代码
#ifndef LCD_ST7789_H_
#define LCD_ST7789_H_

#include "stm32f1xx_hal.h"

void LCD_Init(void);
void LCD_SetCursor(uint16_t x, uint16_t y);
void LCD_WritePixel(uint16_t color);
void LCD_FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);

#endif /* LCD_ST7789_H_ */
lcd_st7789.c
复制代码
#include "lcd_st7789.h"
#include "main.h"

#define LCD_WIDTH   240
#define LCD_HEIGHT  240

static SPI_HandleTypeDef hspi1;

void LCD_Reset(void) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
    HAL_Delay(10);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
    HAL_Delay(10);
}

void LCD_Command(uint8_t cmd) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
}

void LCD_Data(uint8_t data) {
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
    HAL_SPI_Transmit(&hspi1, &data, 1, 100);
}

void LCD_Init(void) {
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
    hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;
    if (HAL_SPI_Init(&hspi1) != HAL_OK) {
        // Initialization Error
        Error_Handler();
    }

    LCD_Reset();

    LCD_Command(0x11); // Sleep Out
    HAL_Delay(120);

    LCD_Command(0xB1);
    LCD_Data(0x01);
    LCD_Data(0x2C);
    LCD_Data(0x2D);

    LCD_Command(0xB2);
    LCD_Data(0x01);
    LCD_Data(0x2C);
    LCD_Data(0x2D);

    LCD_Command(0xB3);
    LCD_Data(0x01);
    LCD_Data(0x2C);
    LCD_Data(0x2D);
    LCD_Data(0x01);
    LCD_Data(0x2C);
    LCD_Data(0x2D);

    LCD_Command(0xB4);
    LCD_Data(0x07);

    LCD_Command(0xC0);
    LCD_Data(0xA2);
    LCD_Data(0x02);
    LCD_Data(0x84);

    LCD_Command(0xC1);
    LCD_Data(0xC5);

    LCD_Command(0xC2);
    LCD_Data(0x0A);
    LCD_Data(0x00);

    LCD_Command(0xC3);
    LCD_Data(0x8A);
    LCD_Data(0x2A);

    LCD_Command(0xC4);
    LCD_Data(0x8A);
    LCD_Data(0xEE);

    LCD_Command(0xC5);
    LCD_Data(0x0E);

    LCD_Command(0xE0);
    LCD_Data(0xF0);
    LCD_Data(0x09);
    LCD_Data(0x0B);
    LCD_Data(0x06);
    LCD_Data(0x04);
    LCD_Data(0x15);
    LCD_Data(0x07);
    LCD_Data(0x31);
    LCD_Data(0xD8);
    LCD_Data(0x4D);
    LCD_Data(0x40);
    LCD_Data(0x15);
    LCD_Data(0x12);
    LCD_Data(0x12);
    LCD_Data(0x18);

    LCD_Command(0xE1);
    LCD_Data(0xF0);
    LCD_Data(0x09);
    LCD_Data(0x0B);
    LCD_Data(0x06);
    LCD_Data(0x04);
    LCD_Data(0x15);
    LCD_Data(0x07);
    LCD_Data(0x31);
    LCD_Data(0xD8);
    LCD_Data(0x4D);
    LCD_Data(0x40);
    LCD_Data(0x15);
    LCD_Data(0x12);
    LCD_Data(0x12);
    LCD_Data(0x18);

    LCD_Command(0x21); // Inversion On

    LCD_Command(0x11); // Sleep Out
    HAL_Delay(120);

    LCD_Command(0x29); // Display On
}

void LCD_SetCursor(uint16_t x, uint16_t y) {
    LCD_Command(0x2A);
    LCD_Data((x >> 8) & 0xFF);
    LCD_Data(x & 0xFF);
    LCD_Data(((x + 1) >> 8) & 0xFF);
    LCD_Data((x + 1) & 0xFF);

    LCD_Command(0x2B);
    LCD_Data((y >> 8) & 0xFF);
    LCD_Data(y & 0xFF);
    LCD_Data(((y + 1) >> 8) & 0xFF);
    LCD_Data((y + 1) & 0xFF);

    LCD_Command(0x2C);
}

void LCD_WritePixel(uint16_t color) {
    uint8_t data[] = {color >> 8, color & 0xFF};
    HAL_SPI_Transmit(&hspi1, data, 2, 100);
}

void LCD_FillRect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color) {
    LCD_SetCursor(x, y);
    for (uint16_t i = 0; i < h; i++) {
        for (uint16_t j = 0; j < w; j++) {
            LCD_WritePixel(color);
        }
    }
}
3. 处理字符集

创建一个文件font.hfont.c来包含Cambodia字体的数据。

font.h
复制代码
复制代码
#ifndef FONT_H_
#define FONT_H_

#include <stdint.h>

extern const uint8_t cambodia_font[];

typedef struct {
    uint16_t unicode;
    uint8_t width;
    uint8_t height;
    uint8_t data[];
} FontChar;

const FontChar* GetFontChar(uint16_t unicode);

#endif /* FONT_H_ */
font.c

这个文件包含了Cambodia字体的数据。这里假设你已经有了这样的数据。

复制代码
#include "font.h"

const uint8_t cambodia_font[] = {
    // Cambodian font data in a specific format
    // Example entry for Unicode U+1780 (ក)
    0x17, 0x80, 0x08, 0x08,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

    // Add more characters as needed
    0x00, 0x00 // End marker
};

const FontChar* GetFontChar(uint16_t unicode) {
    for (int i = 0; ; i++) {
        if (cambodia_font[i * 5] == (unicode >> 8) &&
            cambodia_font[i * 5 + 1] == (unicode & 0xFF)) {
            static FontChar fc;
            fc.unicode = unicode;
            fc.width = cambodia_font[i * 5 + 2];
            fc.height = cambodia_font[i * 5 + 3];
            fc.data = &cambodia_font[i * 5 + 4];
            return &fc;
        }
        if (cambodia_font[i * 5] == 0 && cambodia_font[i * 5 + 1] == 0)
            break;
    }
    return NULL;
}
4. 显示文本

main.c中调用上述库函数来显示文本

复制代码
#include "main.h"
#include "lcd_st7789.h"
#include "font.h"

#define SCREEN_WIDTH  240
#define SCREEN_HEIGHT 240

void DrawString(uint16_t x, uint16_t y, const char* str) {
    while (*str) {
        uint32_t c = *(uint8_t*)str++;
        if ((c & 0xE0) == 0xC0) { // Two-byte UTF-8 character
            c = (c & 0x1F) << 6;
            c |= (*(uint8_t*)str++ & 0x3F);
        } else if ((c & 0xF0) == 0xE0) { // Three-byte UTF-8 character
            c = (c & 0x0F) << 12;
            c |= (*(uint8_t*)str++ & 0x3F) << 6;
            c |= (*(uint8_t*)str++ & 0x3F);
        }

        const FontChar* fc = GetFontChar(c);
        if (fc) {
            LCD_SetCursor(x, y);
            for (int h = 0; h < fc->height; h++) {
                for (int w = 0; w < fc->width; w++) {
                    uint8_t pixel = fc->data[h * fc->width + w];
                    LCD_WritePixel(pixel ? 0xFFFF : 0x0000); // White background black text
                }
                x += fc->width;
            }
            y += fc->height;
        }
    }
}

int main(void) {
    HAL_Init();
    SystemClock_Config();

    MX_GPIO_Init();
    MX_SPI1_UART_Init();

    LCD_Init();
    LCD_FillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0x0000); // Clear screen with black

    DrawString(10, 10, "សួស្តី");

    while (1) {
        // Main loop
    }
}

void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }

    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
                                  RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
        Error_Handler();
    }
}

static void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}

static void MX_SPI1_UART_Init(void) {
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
    hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;
    if (HAL_SPI_Init(&hspi1) != HAL_OK) {
        Error_Handler();
    }
}

void Error_Handler(void) {
    while (1) {
        // User can add his own implementation to report the HAL error return state
    }
}

这段代码包括了所有必要的部分来初始化STM32F103CBT6、配置SPI接口、初始化ST7789 LCD显示屏,并显示"សួស្តី"。你需要确保cambodia_font数组包含足够的Cambodia字体数据,并根据实际情况调整字体数据的格式和查找逻辑。

相关推荐
✎ ﹏梦醒͜ღ҉繁华落℘15 小时前
单片机基础知识---stm32单片机的优先级
stm32·单片机·mongodb
u1521096484917 小时前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
zd84510150017 小时前
RS485 总线详解
单片机·嵌入式硬件
半条-咸鱼18 小时前
【STM32】I2C协议原理、HAL读写与OLED显示操作
嵌入式硬件·c·信息与通信
牛根生同志18 小时前
SPI数据收发的时候 TXE与RXNE标志位置位的时机
stm32·spi·transfer
wohoo_wangzi18 小时前
苏州晟雅泰电子:关于W25Q128JVSIQ这个芯片物料的参数,规格及应用领域
嵌入式硬件
goldenrolan20 小时前
学习型红外控制系统稳定性挂测工装专项总结
软件测试·python·stm32·嵌入式·红外
✎ ﹏梦醒͜ღ҉繁华落℘21 小时前
编程基础 --高内聚,低耦合
c语言·单片机
科芯创展21 小时前
1A,1MHz,30VIN,XZ4115,降压恒流LED驱动芯片
单片机·嵌入式硬件
集芯微电科技有限公司21 小时前
四通道2A输出集成功率电感降压模块专为紧凑型方案设计
人工智能·单片机·嵌入式硬件·生成对抗网络·计算机外设