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:
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 = ® 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 = ®1; 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,®,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,®,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++; } }