(STM32) RT-Thread journey from scratch--SPI driver ST7735

Previous:

(STM32) RT-Thread journey from scratch--PWM drives ST7735 dimming

The driver used in this article is provided by ST official:

https://github.com/STMicroelectronics/stm32-st7735.git

If you need it, you can directly git clone it for transplantation. The core code has nothing to do with the chip model, and the portability is very good! The ST7735 library used in this article has added some new content than the official one, which is provided by the seller of the development board. Address:

https://github.com/WeActTC

In the first article, we opened up the SPI and read the chip ID. In this article, we can directly use the official driver library as long as we complete a few interface functions. As for which interfaces to improve, you need to look at which functions are required by the official driver library.

1. Add official source code

You can add several source files by referring to the structure in the picture above, among which font.h can be found on the github of WeAct.

Remember to include the header file path after adding it.

2. How to use the official library

After the addition is complete, create a new lcd-related source file, such as my file structure:

Modify a place in st7735.h:

This variable is defined in st7735.c. How to use the library is as follows:

Among them, ST7735Ctx is defined in the library, we can use it directly. But two more variables are needed:

st7735_pObj is the device driver instance, which is actually displayed on the screen. The key is the variable st7735_pIO, which is responsible for providing the underlying interface. Some of the function pointers it contains are exactly what we need to accomplish.

On the basis of the previous chapter, two sets of SPI usage methods are used. If USE_RTT_SPI is defined, the SPI interface function provided by the RTT driver framework is used. If it is commented out, the SPI function of the HAL library is used directly. I will implement it in detail attached to the back.

The function we actually call when we use it is:

For example, to fill the entire screen with black:

 

 

 

 

 

 

Attached:

mspi.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-11-14     cx       the first version
 */
#include <rtthread.h>
#include <rtdevice.h>
#include <stm32h7xx.h>
#include <drv_spi.h>
#include <mspi.h>
#include "st7735_reg.h"
#include "st7735.h"

#define USE_RTT_SPI

SPI_HandleTypeDef *spi_handle;

#ifdef USE_RTT_SPI

static struct rt_spi_device *spi_lcd;

#define LCD_RD_HIGH rt_pin_write(SPI_RD_PIN_NUM, PIN_HIGH)
#define LCD_RD_LOW  rt_pin_write(SPI_RD_PIN_NUM, PIN_LOW)
#else
SPI_HandleTypeDef hspi4;
#define SPI_Drv (&hspi4)
#define LCD_CS_HIGH HAL_GPIO_WritePin(GPIOE,GPIO_PIN_11,GPIO_PIN_SET)
#define LCD_CS_LOW  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_11,GPIO_PIN_RESET)

#define LCD_RD_HIGH HAL_GPIO_WritePin(GPIOE,GPIO_PIN_13,GPIO_PIN_SET)
#define LCD_RD_LOW  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_13,GPIO_PIN_RESET)
#endif

#ifdef USE_RTT_SPI
int32_t mspi_send_reg(uint8_t reg,uint8_t *data,uint32_t len)
{
    struct rt_spi_message msg;
    uint32_t remsg = RT_NULL;
    msg.send_buf = &reg;
    msg.recv_buf = RT_NULL;
    msg.length = 1;
    msg.cs_take = 1;
    msg.cs_release = 0;
    msg.next = RT_NULL;
    LCD_RD_LOW;
    remsg = (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    LCD_RD_HIGH;
    if(len > 0)
    {
        msg.send_buf = data;
        msg.recv_buf = RT_NULL;
        msg.length = len;
        msg.cs_take = 0;
        msg.cs_release = 1;
        msg.next = RT_NULL;
        remsg += (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    }
    if(remsg!=RT_NULL)
        return -1;
    else
        return 0;
}
int32_t mspi_read_reg(uint8_t reg,uint8_t *data)
{
    struct rt_spi_message msg;
    uint32_t remsg = RT_NULL;
    uint8_t reg1 = reg;
    msg.send_buf = &reg1;
    msg.recv_buf = RT_NULL;
    msg.length = 1;
    msg.cs_take = 1;
    msg.cs_release = 0;
    msg.next = RT_NULL;
    LCD_RD_LOW;
    remsg = (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    LCD_RD_HIGH;
    if(remsg == 0)
    {
        msg.send_buf = RT_NULL;
        msg.recv_buf = data;
        msg.length = 1;
        msg.cs_take = 0;
        msg.cs_release = 1;
        msg.next = RT_NULL;
        remsg += (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
    }
    if(remsg!=RT_NULL)
        return -1;
    else
        return 0;
}
int32_t mspi_send_data(uint8_t *data,uint32_t len)
{
    struct rt_spi_message msg;
    msg.send_buf = data;
    msg.recv_buf = RT_NULL;
    msg.length = len;
    msg.cs_take = 1;
    msg.cs_release = 1;
    msg.next = RT_NULL;
    return (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
}
int32_t mspi_read_data(uint8_t *data,uint32_t len)
{
    struct rt_spi_message msg;
    msg.send_buf = RT_NULL;
    msg.recv_buf = data;
    msg.length = len;
    msg.cs_take = 1;
    msg.cs_release = 1;
    msg.next = RT_NULL;
    return (uint32_t)rt_spi_transfer_message(spi_lcd,&msg);
}

#else
int32_t mspi_send_reg(uint8_t reg,uint8_t *data,uint32_t len)
{
    int32_t result;
    LCD_CS_LOW;
    LCD_RD_LOW;
    result = HAL_SPI_Transmit(SPI_Drv,&reg,1,100);
    LCD_RD_HIGH;
    if(len > 0)
        result += HAL_SPI_Transmit(SPI_Drv,data,len,500);
    LCD_CS_HIGH;
    if(result>0){
        result = -1;}
    else{
        result = 0;}
    return result;
}

int32_t mspi_read_reg(uint8_t reg,uint8_t *data)
{
    int32_t result;
    LCD_CS_LOW;
    LCD_RD_LOW;

    result = HAL_SPI_Transmit(SPI_Drv,&reg,1,100);
    LCD_RD_HIGH;
    result += HAL_SPI_Receive(SPI_Drv,data,1,500);
    LCD_CS_HIGH;
    if(result>0){
        result = -1;}
    else{
        result = 0;}
    return result;
}

int32_t mspi_send_data(uint8_t *data,uint32_t len)
{
    int32_t result;
    LCD_CS_LOW;
    //LCD_RD_HIGH;
    result =HAL_SPI_Transmit(SPI_Drv,data,len,100);
    LCD_CS_HIGH;
    if(result>0){
        result = -1;}
    else{
        result = 0;}
    return result;
}

int32_t mspi_read_data(uint8_t *data,uint32_t len)
{
    int32_t result;
    LCD_CS_LOW;
    //LCD_RD_HIGH;
    result = HAL_SPI_Receive(SPI_Drv,data,len,500);
    LCD_CS_HIGH;
    if(result>0){
        result = -1;}
    else{
        result = 0;}
    return result;
}
#endif
int32_t mspi_get_tick(void)
{
    return HAL_GetTick();
}
void mspi_rw_gpio_init(void)
{
#ifdef USE_RTT_SPI
    rt_pin_mode(SPI_RD_PIN_NUM, PIN_MODE_OUTPUT);
    rt_pin_write(SPI_RD_PIN_NUM, PIN_HIGH);
#else
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOE_CLK_ENABLE();
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11|GPIO_PIN_13, GPIO_PIN_SET);
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
#endif
}
void mspi_init(void)
{
    mspi_rw_gpio_init();
#ifdef USE_RTT_SPI
    struct rt_spi_configuration cfg;
    rt_hw_spi_device_attach("spi4", "spi40", GPIOE, GPIO_PIN_11);
    spi_lcd = (struct rt_spi_device *)rt_device_find("spi40");
    if(!spi_lcd)
    {
        rt_kprintf("spi40 can't find\n");
    }
    else
    {
        spi_lcd->bus->owner = spi_lcd;
        cfg.data_width = 8;
        cfg.mode = RT_SPI_MASTER | RT_SPI_3WIRE | RT_SPI_MODE_0 | RT_SPI_MSB;
        cfg.max_hz = 12.5 * 1000 * 1000;
        rt_spi_configure(spi_lcd, &cfg);
    }
    //You can also use the functions provided by the framework for initialization, and use HAL library functions for sending and receiving. The operation object is the following spi_handle
//    struct stm32_spi *spi_drv =  rt_container_of(spi_lcd->bus, struct stm32_spi, spi_bus);
//    spi_handle = &spi_drv->handle;
#else
    hspi4.Instance = SPI4;
    hspi4.Init.Mode = SPI_MODE_MASTER;
    hspi4.Init.Direction = SPI_DIRECTION_1LINE;
    hspi4.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi4.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi4.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi4.Init.NSS = SPI_NSS_SOFT;
    hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
    hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi4.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi4.Init.CRCPolynomial = 0x0;
    hspi4.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
    hspi4.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
    hspi4.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
    hspi4.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    hspi4.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
    hspi4.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
    hspi4.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
    hspi4.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
    hspi4.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
    hspi4.Init.IOSwap = SPI_IO_SWAP_DISABLE;
    if (HAL_SPI_Init(&hspi4) != HAL_OK)
    {
      rt_kprintf("ERROR\n");
    }
#endif
}

mlcd.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-11-15     cx       the first version
 */
#include <stdio.h>
#include <rtthread.h>
#include <rtdevice.h>
#define DBG_TAG "mlcd"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "mpwm.h"
#include "mspi.h"
#include "st7735_reg.h"
#include "st7735.h"
#include "mlcd.h"
#include "font.h"
#include <stm32h7xx.h>

int32_t mlcd_st7735_init(void);

ST7735_IO_t st7735_pIO = {
    mlcd_st7735_init,
    NULL,
    0,
    mspi_send_reg,
    mspi_read_reg,
    mspi_send_data,
    mspi_read_data,
    mspi_get_tick
};

ST7735_Object_t st7735_pObj;

int32_t mlcd_st7735_init(void)
{
    int32_t result = ST7735_OK;
    mpwm_init();
    mspi_init();
    return result;
}

int mlcd_init(void)
{
    uint32_t st7735_id;
    uint8_t text[50];
    #ifdef TFT96
    ST7735Ctx.Orientation = ST7735_ORIENTATION_LANDSCAPE_ROT180;
    ST7735Ctx.Panel = HannStar_Panel;
    ST7735Ctx.Type = ST7735_0_9_inch_screen;
    #elif TFT18
    ST7735Ctx.Orientation = ST7735_ORIENTATION_PORTRAIT;
    ST7735Ctx.Panel = BOE_Panel;
    ST7735Ctx.Type = ST7735_1_8_inch_screen;
    #else
    LOG_E("Unknown Screen");
    #endif
    ST7735_RegisterBusIO(&st7735_pObj,&st7735_pIO);
    ST7735_LCD_Driver.Init(&st7735_pObj,ST7735_FORMAT_RBG565,&ST7735Ctx);
    ST7735_LCD_Driver.ReadID(&st7735_pObj,&st7735_id);
    rt_kprintf("LCD ID:%08X\n",st7735_id);

    ST7735_LCD_Driver.FillRect(&st7735_pObj, 0, 0, ST7735Ctx.Width,ST7735Ctx.Height, BLACK);

    sprintf((char *)&text, "Something V1.0");
    LCD_ShowString(4, 4, ST7735Ctx.Width, 16, 16, text);

    return 0;
}

uint16_t POINT_COLOR=0xFFFF;    //brush color
uint16_t BACK_COLOR=BLACK;  //background color
//display a character at the specified position
//x,y: starting coordinates
//num: Characters to display: " "--->"~"
//size: font size 12/16
//mode: superposition mode (1) or non-superposition mode (0)

void LCD_ShowChar(uint16_t x,uint16_t y,uint8_t num,uint8_t size,uint8_t mode)
{
  uint8_t temp,t1,t;
    uint16_t y0=y;
    uint16_t x0=x;
    uint16_t colortemp=POINT_COLOR;
  uint32_t h,w;

    uint16_t write[size][size==12?6:8];
    uint16_t count;

  ST7735_GetXSize(&st7735_pObj,&w);
    ST7735_GetYSize(&st7735_pObj,&h);

    //settings window
    num=num-' ';//Get the offset value
    count = 0;

    if(!mode) //non-overlapping method
    {
        for(t=0;t<size;t++)
        {
            if(size==12)temp=asc2_1206[num][t];  //call 1206 fonts
            else temp=asc2_1608[num][t];         //call 1608 font

            for(t1=0;t1<8;t1++)
            {
                if(temp&0x80)
                    POINT_COLOR=(colortemp&0xFF)<<8|colortemp>>8;
                else
                    POINT_COLOR=(BACK_COLOR&0xFF)<<8|BACK_COLOR>>8;

                write[count][t/2]=POINT_COLOR;
                count ++;
                if(count >= size) count =0;

                temp<<=1;
                y++;
                if(y>=h){POINT_COLOR=colortemp;return;}//super area
                if((y-y0)==size)
                {
                    y=y0;
                    x++;
                    if(x>=w){POINT_COLOR=colortemp;return;}//super area
                    break;
                }
            }
        }
    }
    else//Overlay method
    {
        for(t=0;t<size;t++)
        {
            if(size==12)temp=asc2_1206[num][t];  //call 1206 fonts
            else temp=asc2_1608[num][t];         //call 1608 font
            for(t1=0;t1<8;t1++)
            {
                if(temp&0x80)
                    write[count][t/2]=(POINT_COLOR&0xFF)<<8|POINT_COLOR>>8;
                count ++;
                if(count >= size) count =0;

                temp<<=1;
                y++;
                if(y>=h){POINT_COLOR=colortemp;return;}//super area
                if((y-y0)==size)
                {
                    y=y0;
                    x++;
                    if(x>=w){POINT_COLOR=colortemp;return;}//super area
                    break;
                }
            }
        }
    }
    ST7735_FillRGBRect(&st7735_pObj,x0,y0,(uint8_t *)&write,size==12?6:8,size);
    POINT_COLOR=colortemp;
}

//display string
//x,y: starting point coordinates
//width,height: area size
//size: font size
//*p: string starting address
void LCD_ShowString(uint16_t x,uint16_t y,uint16_t width,uint16_t height,uint8_t size,uint8_t *p)
{
    uint8_t x0=x;
    width+=x;
    height+=y;
    while((*p<='~')&&(*p>=' '))//Determine whether it is an illegal character!
    {
        if(x>=width){x=x0;y+=size;}
        if(y>=height)break;//quit
        LCD_ShowChar(x,y,*p,size,0);
        x+=size/2;
        p++;
    }
}












Tags: Embedded system Single-Chip Microcomputer stm32 rt-thread

Posted by pietbez on Sat, 19 Nov 2022 02:51:56 +0300