【GigaDevice GD32H759I-EVAL】TLI and LCD display implementation (1)
[Copy link]
This post was last edited by Yin Xiaozhou on 2024-6-23 15:19
LCD is a display device that supports full-color display. The LCD display size on the GD32H7 development board is a 4.3-inch LCD display with a resolution of 480 x 272. The RGB interface does not have an LCD controller.
TFT-LCD Interface (TLI)
TLI is the liquid crystal controller built into the GDS2F4701IH6 microcontroller. It is responsible for controlling the liquid crystal display (LCD) and transmitting pixel data between the LCD module.
- Supports two independent display layers to achieve layered display effects of background and foreground.
- By connecting to the RGB interface of the LCD module, direct transmission of liquid crystal control and pixel data can be achieved.
TLI pins and clock domains
Interface Pin Description
- Control signal line :
- HS (HSYNC): corresponds to LCD_HSYNC of the LCD module, used for horizontal synchronization signal.
- VS (VSYNC): corresponds to LCD_VSYNC of the LCD module, used for vertical synchronization signal.
- DE (Data Enable): corresponds to LCD_DE of the LCD module, indicating data valid signal.
- PIXCLK: corresponds to LCD_CLK of the LCD module, indicating the pixel clock signal.
- RGB bidirectional data line :
- RED[7:0]: corresponds to LCD_R[7:0] of the LCD module, used to transmit red component data.
- GREEN[7:0]: corresponds to LCD_G[7:0] of the LCD module, used to transmit green component data.
- BLUE[7:0]: corresponds to LCD_B[7:0] of the LCD module, used to transmit blue component data.
Clock Domain Description
The TLI module contains three clock domains, each of which serves different modules and functions:
- AHB clock domain :
- Mainly serves the pixel DMA module.
- When the pixel DMA module obtains pixel data from the system memory, it needs to use the AHB bus.
- The AHB bus provides high-speed data transmission capability, ensuring that pixel data can be efficiently transferred from the memory to the display module.
- APB clock domain :
- Registers operate in this clock domain.
- The TLI module is configured and controlled by accessing registers via the APB bus.
- The APB bus is typically used to access microcontroller peripherals and configuration registers.
- TLI clock domain :
- All modules except AHB and APB clock domains work in this clock domain.
- The TLI clock frequency is obtained by dividing the PLLSAI-R frequency and can be configured as needed to meet the clock requirements of the LCD module.
- The TLI clock is output through the PIXCLK pin to provide a pixel clock signal for the LCD module.
Image Processing Unit
The image processing unit of TLI consists of six parts, namely, pixel DMA unit, LCD timing controller, pixel processing unit 0, pixel processing unit 1, window and blending unit, and dithering unit. These six parts are introduced in turn below.
- Pixel DMA Unit :
- Responsible for transferring the video memory data stored in the external SDRAM to the buffer of the pixel processing unit.
- Since the LCD display module does not have its own video memory, an external storage such as SDRAM is required to store the image data to be displayed.
- The SDRAM is mounted on the AHB bus so that the pixel DMA unit can read data from it efficiently.
- LCD Timing Controller :
- Used to generate RGB interface timing signals that meet the requirements of LCD panels.
- These timing signals are necessary for the LCD panel to correctly receive and display images.
- Pixel Processing Unit 0 & Pixel Processing Unit 1 :
- The image data of the background layer and the foreground layer are processed separately.
- Convert pixel data in RGB565 format to ARGB8888 format. ARGB8888 format provides higher color depth and transparency control (alpha channel).
- If the number of channel bits of the source data is less than 8, the high-order data is copied and filled.
- Windowing and Blending Unit :
- Responsible for mixing the two layers of ARGB8888 image data output by pixel processing unit 0 and pixel processing unit 1.
- Complex display effects, such as semi-transparent windows, can be achieved by setting different blending parameters (such as transparency).
- The blended data is output in RGB888 format because most LCD panels do not support direct display of the alpha channel.
- Dithering Unit :
- The main function is to perform dithering on data that is greater than the color depth of the LCD panel.
- Dithering is a technique that simulates higher color depths by varying color in time and space.
- For example, when using an 18-bit LCD display, the dithering unit can compress 24-bit RGB888 data into 18-bit data for display, thereby improving the display effect without increasing the panel hardware cost.
- Overall workflow :
- The pixel DMA unit reads image data in RGB565 format from external SDRAM.
- The pixel processing unit converts the read data into ARGB8888 format and may perform other necessary processing.
- The window and blend unit blends the image data of the background layer and the foreground layer.
- The dithering unit performs dithering processing on the mixed data to match the color depth of the LCD panel.
- The LCD timing controller generates timing signals and outputs data to the LCD panel for display.
Image Processing Accelerator (IPA)
IPA is the built-in image processing accelerator of the GD32H7 microcontroller. When using TLI to control the LCD display, the pixel data capacity in the video memory is very large. Therefore, when you need to quickly draw rectangles, straight lines, layered data mixing, and image data format conversion, you can choose to use the IPA peripheral. The IPA operation mode is similar to the TLI image processing unit process, but it is more efficient. IPA supports 4 conversion modes:
- Copy a source image to a destination image :
- Application scenario : This mode can be used when you need to copy an existing image (source image) directly to another location (destination image). This is usually used for moving, copying, or simple screen updates of images.
- Features : This mode does not involve any format conversion or color mixing, just simple image data copying.
- Copy a source image to a destination image and convert it to a specific format :
- Application scenario : This mode is useful when you need to copy a source image to a destination and convert the image's format (such as color depth, color space, etc.) from one format to another at the same time.
- Features : In addition to copying image data, it also includes format conversion of image data, which allows you to change its visual properties before transmitting or displaying the image.
- Mix two different source images and convert the result to a specific color format :
- Application scenarios : Image blending is necessary in many applications such as graphic editing, video synthesis, augmented reality (AR), etc. This mode allows you to blend two images together and possibly change the color format of the blended image.
- Features : Supports mixed processing of layered data, allowing you to perform complex combinations and color conversions on image data from different sources to create unique visual effects.
- Fill a target image area with a specific color :
- Application scenario : This mode is useful when you want to draw a solid color block on a specific area of an image (such as background color, text box background, etc.).
- Features : Allows you to specify a color and fill the specified area in the image with this color. This is often used for tasks such as creating static backgrounds, text rendering, etc.
Benefits of using IPA :
- Performance : Since IPA is a hardware accelerator designed specifically for image processing, it typically has higher performance and lower power consumption than software implementations.
- Flexibility : By supporting multiple conversion modes and color formats, IPA is able to handle a wide range of image processing tasks.
- Efficiency : Can be used in conjunction with DMA to reduce CPU involvement and further improve data processing efficiency. Compared with software image processing through TLI (or other methods), IPA can usually complete tasks faster, thereby improving the overall efficiency of the system.
1. AHB interface overview
AHB (Advanced High-performance Bus) is an advanced high-performance bus designed for connecting high-performance modules (such as CPU, DMA and DSP, etc.). In embedded system design, AHB bus is used as the on-chip system bus of SoC (System on Chip), providing high-bandwidth and high-efficiency data transmission capabilities.
AHB interface in IPA
In IPA (image processing accelerator), the AHB interface is divided into AHB slave device interface and AHB master device interface, which are used for data input and output respectively.
- AHB slave interface :
- Function : Connects IPA and AMBA AHB bus, allowing AHB master devices in the system (such as CPU) to read and write to IPA.
- Data transfer : On the AHB slave interface, data usually flows from the AHB bus to the IPA, realizing data transfer from an external data source (such as SDRAM) to the IPA.
- AHB master device interface :
- Function : Output the data processed by IPA (such as pixel data to be displayed) to the AHB bus, and then transmit it to other system components (such as LCD controller).
- Data flow : On the AHB master interface, data flows from IPA to the AHB bus and then to the target device.
AHB bus data source
The data source of the AHB bus is usually SDRAM (Synchronous Dynamic Random Access Memory). In image processing systems, the pixel data to be displayed is generally stored in a specific area of SDRAM (used as the display memory of the LCD). The size of these pixel data is usually at least the capacity of one frame of pixel data to ensure that the complete image can be displayed on the screen.
2. LUT unit
LUT, or color lookup table, is a table used to map input pixel values to output color values. In image processing, LUT is often used in scenes such as color space conversion, color correction, and contrast enhancement. LUT is especially useful when the image data uses a non-direct pixel format, such as indexed color mode.
How LUTs Work
In the LUT, each input pixel value (or index) corresponds to an output color value. For example, if each pixel in the image data is represented by 8 bits of data (i.e., 256 possible color values), then the LUT can contain 256 entries, each of which stores a corresponding color value (such as ARGB8888 or RGB888 format).
When processing an image, the system looks up the corresponding color value in the LUT based on the 8-bit value of each pixel and uses it for display. This allows images with higher color depth (such as 24-bit or 32-bit color) to be displayed even if the original image data only has 8-bit depth.
Advantages of LUTs
- Increased color depth : As mentioned above, LUTs allow more colors to be represented using fewer bits.
- Color correction : By modifying the entries in the LUT, you can adjust the image's brightness, contrast, saturation and other properties.
- Real-time processing : Since the LUT is a pre-calculated table, color conversion can be done quickly at the hardware level, enabling real-time image processing.
Limitations of LUTs
- Color range limitation : Since the color data types in the LUT are fixed, the colors of the actual displayed image can only be limited to the colors in the LUT. This may cause some colors to not be accurately represented in the image.
- Storage requirements : The size of the LUT depends on the range of input pixel values and the desired color depth. For larger images or higher color depths, the LUT may take up a lot of storage space.
- Limited flexibility : LUTs are usually suitable for fixed color conversion tasks. For tasks that require more complex color processing (such as non-linear color correction), more advanced techniques or algorithms may need to be used.
LUTs are a powerful and flexible tool that can play an important role in image processing. However, there are limitations and constraints that you need to be aware of when using LUTs to ensure optimal image quality and performance.
3. PCE unit overview
PCE is an important unit in the image processing system, mainly used for processing and expanding layer pixel channels. It mainly includes two parts: foreground layer and background layer, which are used to process pixel data transmitted from AHB slave device interface.
Main functions of the PCE unit
- Pixel data format conversion :
- Regardless of the format of the original pixel data (such as RGB565, YUV420, etc.), the PCE unit can convert it to ARGB8888 format. ARGB8888 is a commonly used 32-bit color format, where A represents the Alpha channel (transparency), R, G, and B represent the three color components of red, green, and blue respectively, and each color component occupies 8 bits, for a total of 32 bits.
- Data buffer FIFO :
- The foreground and background layers of the PCE unit are equipped with a data buffer FIFO (First In, First Out). FIFO is used to cache pixel data obtained from the AHB slave device interface to ensure the stability and continuity of data during transmission.
- The presence of FIFO enables the PCE unit to be more efficient and flexible when processing pixel data. It allows data to be cached and temporarily stored when needed, avoiding data loss or delay caused by mismatched data transmission speeds.
- Layer pixel channel processing :
- The PCE unit can process the pixel data of the foreground layer and the background layer at the same time to achieve layered image processing. This is very useful for achieving complex image effects (such as text overlay, picture synthesis, etc.).
Summarize
The PCE unit is an important component in the image processing system. It can convert the input pixel data into a unified ARGB8888 format and realize efficient data processing through the data buffer FIFO of the foreground layer and background layer. This makes the PCE unit more flexible and efficient in processing complex image effects.
4. Mixing unit
The mixing unit is used to combine the data output by the foreground layer PCE and the background layer PCE into a single layer of data output. After passing through the mixer, the two layers of data are combined into a layer of ARGB8888 format image. Note: The data format output by the TLI mixing unit is
RGB888
Alpha channel values are blended based on the following formula (AF is the foreground layer alpha value, AB is the background layer alpha value):
The red, green, and blue channel values are mixed based on the following formula (RF, GF, BF are the red, green, and blue values of the foreground layer; RB, GB, BB are the red, green, and blue values of the background layer):
5. PCC unit
PCC is the target pixel channel compression unit, which can convert the ARGB8888 pixel image data converted by the mixing unit into the target format, such as RGB888, RGB565, ARGB1555 and ARGB4444.
TLI configuration and usage steps
-
Initialization steps
- System clock configuration
- Make sure that PLL2 (or the source of the TLI clock) is configured with the appropriate multiplication and division factors to meet the clock rate requirements of the TLI module.
- Enable the clocks for PLL2 and TLI in the RCU (Reset and Clock Control Unit).
- GPIO Configuration
- Assign GPIO pins for TLI functions, which typically include signals such as RGB data pins, pixel clock (PIXCLK), horizontal sync (HSYNC), vertical sync (VSYNC), and display enable (DE).
- According to the hardware reference manual or datasheet, set these GPIO pins to the appropriate alternate-function mode and enable their high-speed output capability (if supported).
-
TLI Core Configuration
- Start TLI
- The TLI module is enabled by setting the TLIEN bit in the TLI_CTL register.
- Configure display timing
- Program registers such as TLI_SPSZ, TLI_BPSZ, TLI_ASZ, and TLI_TSZ to define the correct pulse width, background width, active area size, and total size for horizontal and vertical synchronization. These parameters usually match the timing requirements of the connected LCD display.
-
Layer configuration
- Defining layer properties
- For each layer, set the color format (such as RGB565, ARGB8888, etc.), window size (width and height), and position (X and Y coordinates in the display area).
- If needed, configure color keying and CLUT (color lookup table). Color keying allows you to replace a specific color with transparent or another color, while CLUT is used for color mapping or palette functions.
- Layer Control and Configuration Registers
- Use registers such as LxWHPCR, LxWVPCR, LxDCCR to define properties such as the layer's window size, position, and default color.
-
Layer overlay and transparency settings
- Mixed Factor Configuration
- Use registers such as LxPFCR and LxCAxCR to configure the blending factor (also called alpha value) of the layer to control the transparency and blending effect between layers.
- Color Keying
- If desired, configure the LxCKCR register to enable the color keying feature and specify the color to be made transparent.
-
Start and stop display updates
- Start display
- Make sure all layers are properly configured and enabled.
- Enables the TLI master control, starting the data transfer from memory to TLI. This typically involves a DMA (Direct Memory Access) controller for efficient pixel data transfer.
- The TLI outputs the received pixel data to the connected LCD display.
- Stop showing updates
- Disable one or more layers, or disable the TLI master control, to stop data transfer and display output.
Related code
The relevant code is in the Utilities folder
#include "gd32h7xx.h"
#include "systick.h"
#include <stdio.h>
#include "gd32h759i_eval.h"
#include "gd32h759i_lcd_eval.h"
/*!
\brief toggle the led every 500ms
\param[in] none
\param[out] none
\retval none
*/
void led_spark(void)
{
static __IO uint32_t timingdelaylocal = 0U;
if(timingdelaylocal) {
if(timingdelaylocal < 500U) {
gd_eval_led_on(LED1);
} else {
gd_eval_led_off(LED1);
}
timingdelaylocal--;
} else {
timingdelaylocal = 1000U;
}
}
/*!
\brief enable the CPU cache
\param[in] none
\param[out] none
\retval none
*/
void cache_enable(void)
{
/* enable i-cache */
SCB_EnableICache();
/* enable d-cache */
SCB_EnableDCache();
}
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
/* enable the CPU cache */
cache_enable();
/* configure systick */
systick_config();
/* initilize the LEDs, USART and key */
gd_eval_led_init(LED1);
gd_eval_led_init(LED2);
gd_eval_com_init(EVAL_COM);
gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO);
gd_eval_lcd_init();
lcd_string_display(LCD_LINE_1,"GD32H7_EVAL_LCD");
lcd_vertical_char_display(LCD_LINE_6,48,'G');
while(1) {
if(RESET == gd_eval_key_state_get(KEY_WAKEUP)) {
delay_1ms(50);
if(RESET == gd_eval_key_state_get(KEY_WAKEUP)) {
gd_eval_led_toggle(LED2);
}
while(RESET == gd_eval_key_state_get(KEY_WAKEUP)) {
}
}
}
}
/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
usart_data_transmit(EVAL_COM, (uint8_t)ch);
while(RESET == usart_flag_get(EVAL_COM, USART_FLAG_TBE));
return ch;
}
/*!
\file gd32h7xxi_lcd_eval.c
\brief gd32h7xxi lcd driver
\version 2024-01-05, V1.2.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "gd32h759i_lcd_eval.h"
#include "gd32h759i_eval_exmc_sdram.h"
#include <string.h>
#define LCD_FRAME_BUFFER ((uint32_t)0xC0000000)
#define BUFFER_OFFSET ((uint32_t)0x7F800)
static font_struct *current_font;
static uint16_t current_textcolor = 0x0000;
static uint16_t current_backcolor = 0xFFFF;
static uint32_t current_framebuffer = LCD_FRAME_BUFFER;
static uint32_t current_layer = LCD_LAYER_BACKGROUND;
static void lcd_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c);
static void lcd_vertical_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c);
static void pixel_set(int16_t x, int16_t y);
#define HORIZONTAL_SYNCHRONOUS_PULSE 41
#define HORIZONTAL_BACK_PORCH 2
#define ACTIVE_WIDTH 480
#define HORIZONTAL_FRONT_PORCH 2
#define VERTICAL_SYNCHRONOUS_PULSE 10
#define VERTICAL_BACK_PORCH 2
#define ACTIVE_HEIGHT 272
#define VERTICAL_FRONT_PORCH 2
/*!
\brief initializes the LCD of GD EVAL board
\param[in] none
\param[out] none
\retval none
*/
void gd_eval_lcd_init(void)
{
lcd_init();
lcd_layer_init(LCD_LAYER_BACKGROUND, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT);
lcd_layer_init(LCD_LAYER_FOREGROUND, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT);
tli_layer_enable(LAYER0);
tli_layer_enable(LAYER1);
tli_enable();
lcd_layer_set(LCD_LAYER_BACKGROUND);
lcd_transparency_set(255);
lcd_clear(LCD_COLOR_WHITE);
lcd_layer_set(LCD_LAYER_FOREGROUND);
lcd_transparency_set(0);
lcd_clear(LCD_COLOR_WHITE);
lcd_layer_set(LCD_LAYER_BACKGROUND);
}
/*!
\brief initialize the LCD GPIO and TLI
\param[in] none
\param[out] none
\retval none
*/
void lcd_init(void)
{
tli_parameter_struct tli_init_struct;
/* enable GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_GPIOH);
rcu_periph_clock_enable(RCU_GPIOG);
/* configure HSYNC(PE15), VSYNC(PA7), PCLK(PG7) */
gpio_af_set(GPIOE, GPIO_AF_14, GPIO_PIN_15);
gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_7);
gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_7);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
/* configure LCD_R7(PG6), LCD_R6(PH12), LCD_R5(PH11), LCD_R4(PA5), LCD_R3(PH9),LCD_R2(PH8),
LCD_R1(PH3), LCD_R0(PH2) */
gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_6);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_12);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_11);
gpio_af_set(GPIOA, GPIO_AF_14, GPIO_PIN_5);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_9);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_8);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_3);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_2);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_2);
/* configure LCD_G7(PD3), LCD_G6(PC7), LCD_G5(PC1), LCD_G4(PH15), LCD_G3(PH14), LCD_G2(PH13),LCD_G1(PB0), LCD_G0(PB1) */
gpio_af_set(GPIOD, GPIO_AF_14, GPIO_PIN_3);
gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_7);
gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_1);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_15);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_14);
gpio_af_set(GPIOH, GPIO_AF_14, GPIO_PIN_13);
gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_0);
gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_1);
gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_15);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_15);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_0);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_1);
/* configure LCD_B7(PB9), LCD_B6(PB8), LCD_B5(PB5), LCD_B4(PC11), LCD_B3(PG11),LCD_B2(PG10), LCD_B1(PG12), LCD_B0(PG14) */
gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_9);
gpio_af_set(GPIOB, GPIO_AF_14, GPIO_PIN_8);
gpio_af_set(GPIOB, GPIO_AF_3, GPIO_PIN_5);
gpio_af_set(GPIOC, GPIO_AF_14, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_11);
gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_10);
gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_12);
gpio_af_set(GPIOG, GPIO_AF_14, GPIO_PIN_14);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_11);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_12);
gpio_mode_set(GPIOG, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_14);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_14);
/* configure LCD_DE(PF10) */
gpio_af_set(GPIOF, GPIO_AF_14, GPIO_PIN_10);
gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
/* LCD PWM BackLight(PG13) */
gpio_mode_set(GPIOG, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_13);
gpio_output_options_set(GPIOG, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_13);
gpio_bit_set(GPIOG, GPIO_PIN_13);
rcu_periph_clock_enable(RCU_TLI);
/* configure PLL2 to generate TLI clock 25MHz/25*192/3 = 64MHz*/
rcu_pll_input_output_clock_range_config(IDX_PLL1,RCU_PLL1RNG_1M_2M,RCU_PLL1VCO_192M_836M);
if(ERROR == rcu_pll2_config(25,192,3,3,3)){
while(1){
}
}
rcu_pll_clock_output_enable(RCU_PLL2R);
rcu_tli_clock_div_config(RCU_PLL2R_DIV8);
rcu_osci_on(RCU_PLL2_CK);
if(ERROR == rcu_osci_stab_wait(RCU_PLL2_CK)){
while(1){
}
}
/* configure the EXMC access mode */
exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);
/* TLI initialization */
tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;
tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;
tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;
tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_INVERTEDTLI;
/* LCD display timing configuration */
tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1;
tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1;
tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1;
tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1;
tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1;
tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1;
tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1;
tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1;
/* LCD background color configure*/
tli_init_struct.backcolor_red = 0xFF;
tli_init_struct.backcolor_green = 0xFF;
tli_init_struct.backcolor_blue = 0xFF;
tli_init(&tli_init_struct);
}
/*!
\brief initialize TLI layer0 or layer1
\param[in] layer: LCD layer
\arg LCD_LAYER_BACKGROUND
\arg LCD_LAYER_FOREGROUND
\param[in] width: width of the window
\param[in] height: height of the window
\param[out] none
\retval none
*/
void lcd_layer_init(uint32_t layer, uint32_t width, uint32_t height)
{
tli_layer_parameter_struct tli_layer_init_struct;
if(LCD_LAYER_BACKGROUND == layer){
/* TLI layer0 configuration */
tli_layer_init_struct.layer_window_leftpos = (HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH);
tli_layer_init_struct.layer_window_rightpos = (width + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1);
tli_layer_init_struct.layer_window_toppos = (VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH);
tli_layer_init_struct.layer_window_bottompos = (height + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1);
tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
tli_layer_init_struct.layer_sa = 0xFF;
tli_layer_init_struct.layer_default_blue = 0xFF;
tli_layer_init_struct.layer_default_green = 0xFF;
tli_layer_init_struct.layer_default_red = 0xFF;
tli_layer_init_struct.layer_default_alpha = 0xFF;
tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
tli_layer_init_struct.layer_frame_bufaddr = LCD_FRAME_BUFFER;
tli_layer_init_struct.layer_frame_line_length = ((width * 2) + 3);
tli_layer_init_struct.layer_frame_buf_stride_offset = (width * 2);
tli_layer_init_struct.layer_frame_total_line_number = height;
tli_layer_init(LAYER0, &tli_layer_init_struct);
}else if(LCD_LAYER_FOREGROUND == layer){
/* TLI layer1 configuration */
tli_layer_init_struct.layer_window_leftpos = (HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH);
tli_layer_init_struct.layer_window_rightpos = (width + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1);
tli_layer_init_struct.layer_window_toppos = (VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH);
tli_layer_init_struct.layer_window_bottompos = (height + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1);
tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
tli_layer_init_struct.layer_sa = 0xFF;
tli_layer_init_struct.layer_default_blue = 0xFF;
tli_layer_init_struct.layer_default_green = 0xFF;
tli_layer_init_struct.layer_default_red = 0xFF;
tli_layer_init_struct.layer_default_alpha = 0x0;
tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
tli_layer_init_struct.layer_frame_bufaddr = LCD_FRAME_BUFFER + BUFFER_OFFSET;
tli_layer_init_struct.layer_frame_line_length = ((width * 2) + 3);
tli_layer_init_struct.layer_frame_buf_stride_offset = (width * 2);
tli_layer_init_struct.layer_frame_total_line_number = height;
tli_layer_init(LAYER1, &tli_layer_init_struct);
}
tli_reload_config(TLI_REQUEST_RELOAD_EN);
lcd_font_set(&LCD_DEFAULT_FONT);
}
/*!
\brief enable the LCD layer0 or layer1
\param[in] layer: LCD layer
\arg LCD_LAYER_BACKGROUND
\arg LCD_LAYER_FOREGROUND
\param[out] none
\retval none
*/
void lcd_layer_enable(uint32_t layer)
{
if(LCD_LAYER_BACKGROUND == layer){
tli_layer_enable(LAYER0);
}else if(LCD_LAYER_FOREGROUND == layer){
tli_layer_enable(LAYER1);
}
tli_enable();
}
/*!
\brief set the LCD layer
\param[in] layer: LCD layer
\arg LCD_LAYER_BACKGROUND
\arg LCD_LAYER_FOREGROUND
\param[out] none
\retval none
*/
void lcd_layer_set(uint32_t layer)
{
if(LCD_LAYER_BACKGROUND == layer){
current_framebuffer = LCD_FRAME_BUFFER;
current_layer = LCD_LAYER_BACKGROUND;
}else{
current_framebuffer = LCD_FRAME_BUFFER + BUFFER_OFFSET;
current_layer = LCD_LAYER_FOREGROUND;
}
}
/*!
\brief set the transparency of LCD
\param[in] trans: transparency of LCD, from 0 to 255
\param[out] none
\retval none
*/
void lcd_transparency_set(uint8_t trans)
{
if (LCD_LAYER_BACKGROUND == current_layer){
TLI_LXSA(LAYER0) &= ~(TLI_LXSA_SA);
TLI_LXSA(LAYER0) = trans;
}else{
TLI_LXSA(LAYER1) &= ~(TLI_LXSA_SA);
TLI_LXSA(LAYER1) = trans;
}
tli_reload_config(TLI_REQUEST_RELOAD_EN);
}
/*!
\brief configure the packeted pixel format
\param[in] pixel_format: pixel format
\arg LAYER_PPF_ARGB8888
\arg LAYER_PPF_RGB888
\arg LAYER_PPF_RGB565
\arg LAYER_PPF_ARGB1555
\arg LAYER_PPF_ARGB4444
\arg LAYER_PPF_L8
\arg LAYER_PPF_AL44
\arg LAYER_PPF_AL88
\param[out] none
\retval none
*/
void lcd_pixel_format_config(uint32_t pixel_format)
{
if(LCD_LAYER_BACKGROUND == current_layer){
TLI_LXPPF(LAYER0) &= ~(TLI_LXPPF_PPF);
TLI_LXPPF(LAYER0) = pixel_format;
}else{
TLI_LXPPF(LAYER1) &= ~(TLI_LXPPF_PPF);
TLI_LXPPF(LAYER1) = pixel_format;
}
}
/*!
\brief configure the frame buffer base address
\param[in] address: frame buffer base address
\param[out] none
\retval none
*/
void lcd_address_config(uint32_t address)
{
if (LCD_LAYER_BACKGROUND == current_layer){
TLI_LXFBADDR(LAYER0) &= ~(TLI_LXFBADDR_FBADD);
TLI_LXFBADDR(LAYER0) = address;
}else{
TLI_LXFBADDR(LAYER1) &= ~(TLI_LXFBADDR_FBADD);
TLI_LXFBADDR(LAYER1) = address;
}
tli_reload_config(TLI_REQUEST_RELOAD_EN);
}
/*!
\brief clear the LCD with specified color
\param[in] color: LCD color
\arg LCD_COLOR_WHITE
\arg LCD_COLOR_BLACK
\arg LCD_COLOR_GREY
\arg LCD_COLOR_BLUE
\arg LCD_COLOR_BLUE2
\arg LCD_COLOR_RED
\arg LCD_COLOR_MAGENTA
\arg LCD_COLOR_GREEN
\arg LCD_COLOR_CYAN
\arg LCD_COLOR_YELLOW
\param[out] none
\retval none
*/
void lcd_clear(uint16_t color)
{
uint32_t index = 0;
for (index = 0x00; index < BUFFER_OFFSET; index++){
*(__IO uint16_t*)(current_framebuffer + (2*index)) = color;
}
}
/*!
\brief set the text font
\param[in] font: the text font
\param[out] none
\retval none
*/
void lcd_font_set(font_struct *font)
{
current_font = font;
}
/*!
\brief get the text font
\param[in] none
\param[out] none
\retval the text font
*/
font_struct* lcd_font_get(void)
{
return current_font;
}
/*!
\brief set the text color
\param[in] color: LCD color
\arg LCD_COLOR_WHITE
\arg LCD_COLOR_BLACK
\arg LCD_COLOR_GREY
\arg LCD_COLOR_BLUE
\arg LCD_COLOR_BLUE2
\arg LCD_COLOR_RED
\arg LCD_COLOR_MAGENTA
\arg LCD_COLOR_GREEN
\arg LCD_COLOR_CYAN
\arg LCD_COLOR_YELLOW
\param[out] none
\retval none
*/
void lcd_text_color_set(uint16_t color)
{
current_textcolor = color;
}
/*!
\brief get the current text color
\param[in] none
\param[out] none
\retval LCD color
\arg LCD_COLOR_WHITE
\arg LCD_COLOR_BLACK
\arg LCD_COLOR_GREY
\arg LCD_COLOR_BLUE
\arg LCD_COLOR_BLUE2
\arg LCD_COLOR_RED
\arg LCD_COLOR_MAGENTA
\arg LCD_COLOR_GREEN
\arg LCD_COLOR_CYAN
\arg LCD_COLOR_YELLOW
*/
uint16_t lcd_text_color_get(void)
{
return current_textcolor;
}
/*!
\brief set the background color
\param[in] color: LCD color
\arg LCD_COLOR_WHITE
\arg LCD_COLOR_BLACK
\arg LCD_COLOR_GREY
\arg LCD_COLOR_BLUE
\arg LCD_COLOR_BLUE2
\arg LCD_COLOR_RED
\arg LCD_COLOR_MAGENTA
\arg LCD_COLOR_GREEN
\arg LCD_COLOR_CYAN
\arg LCD_COLOR_YELLOW
\param[out] none
\retval none
*/
void lcd_background_color_set(uint16_t color)
{
current_backcolor = color;
}
/*!
\brief get the current background color
\param[in] none
\param[out] none
\retval LCD color
\arg LCD_COLOR_WHITE
\arg LCD_COLOR_BLACK
\arg LCD_COLOR_GREY
\arg LCD_COLOR_BLUE
\arg LCD_COLOR_BLUE2
\arg LCD_COLOR_RED
\arg LCD_COLOR_MAGENTA
\arg LCD_COLOR_GREEN
\arg LCD_COLOR_CYAN
\arg LCD_COLOR_YELLOW
*/
uint16_t lcd_background_color_get(void)
{
return current_backcolor;
}
/*!
\brief set point with the specified position and color
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[in] color: LCD color
\arg LCD_COLOR_WHITE
\arg LCD_COLOR_BLACK
\arg LCD_COLOR_GREY
\arg LCD_COLOR_BLUE
\arg LCD_COLOR_BLUE2
\arg LCD_COLOR_RED
\arg LCD_COLOR_MAGENTA
\arg LCD_COLOR_GREEN
\arg LCD_COLOR_CYAN
\arg LCD_COLOR_YELLOW
\param[out] none
\retval none
*/
void lcd_point_set(uint16_t xpos, uint16_t ypos, uint16_t color)
{
*(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*ypos) + xpos)) = color;
}
/*!
\brief get the color of point with the specified position
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[out] none
\retval value of point(x, y)
*/
uint16_t lcd_point_get(uint16_t xpos, uint16_t ypos)
{
return *(__IO uint16_t*)(current_framebuffer + 2*((LCD_PIXEL_WIDTH*ypos) + xpos));
}
/*!
\brief draw a line on LCD
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[in] length: length of line
\param[in] line_direction: direction of line
\arg LCD_LINEDIR_HORIZONTAL
\arg LCD_LINEDIR_VERTICAL
\param[out] none
\retval none
*/
void lcd_line_draw(uint16_t xpos, uint16_t ypos, uint16_t length, uint8_t line_direction)
{
if(LCD_LINEDIR_HORIZONTAL == line_direction){
uint16_t x;
for(x = xpos; x < xpos + length; x++){
pixel_set(x, ypos);
}
}else{
uint16_t y;
for(y = ypos; y < ypos + length; y++){
pixel_set(xpos, y);
}
}
}
/*!
\brief draw a rectangle on LCD
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[in] width: width of rectangle
\param[in] height: height of rectangle
\param[out] none
\retval none
*/
void lcd_rectangle_draw(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height)
{
/* draw horizontal lines */
lcd_line_draw(xpos, ypos, width, LCD_LINEDIR_HORIZONTAL);
lcd_line_draw(xpos, (ypos+ height), width + 1, LCD_LINEDIR_HORIZONTAL);
/* draw vertical lines */
lcd_line_draw(xpos, ypos, height, LCD_LINEDIR_VERTICAL);
lcd_line_draw((xpos + width), ypos, height, LCD_LINEDIR_VERTICAL);
}
/*!
\brief draw a circle on LCD using Bresenham algorithm
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[in] radius: radius of the circle
\param[out] none
\retval none
*/
void lcd_circle_draw(uint16_t xpos, uint16_t ypos, uint16_t radius)
{
int x, y, e;
e = 3-2*radius;
x = 0;
y = radius;
/* set four pixel (x-r, y), (x+r, y), (x, y-r), (x, y-r) */
pixel_set(-radius+xpos, ypos);
pixel_set(radius+xpos, ypos);
pixel_set(xpos, -radius+ypos);
pixel_set(xpos, radius+ypos);
while(x <= y){
if(e < 0){
/* choose the right of the current pixel as the next pixel */
e = e+4*x+6;
x++;
}else{
/* choose the right bottom of the current pixel as the next pixel */
e = e+4*(x-y)+10;
x++;
y--;
}
pixel_set(x+xpos, y+ypos);
pixel_set(-x+xpos, y+ypos);
pixel_set(-x+xpos, -y+ypos);
pixel_set(x+xpos, -y+ypos);
pixel_set(y+xpos, x+ypos);
pixel_set(-y+xpos, x+ypos);
pixel_set(-y+xpos, -x+ypos);
pixel_set(y+xpos, -x+ypos);
}
}
/*!
\brief set plot point of ellipse
\param[in] center_x: x position of ellipse center
\param[in] center_y: y position of ellipse center
\param[in] x: x value
\param[in] y: y value
\param[out] none
\retval none
*/
static void plotpoint_set(int center_x,int center_y,int x,int y)
{
pixel_set(center_x+x, center_y+y);
pixel_set(center_x-x, center_y+y);
pixel_set(center_x+x, center_y-y);
pixel_set(center_x-x, center_y-y);
}
/*!
\brief draw a ellipse on LCD using the midpoint ellipse algorithm
\param[in] xpos: x position of ellipse center
\param[in] ypos: y position of ellipse center
\param[in] axis1: major axis
\param[in] axis2: minor axis
\param[out] none
\retval none
*/
void lcd_ellipse_draw(uint16_t xpos, uint16_t ypos, uint16_t axis1, uint16_t axis2)
{
int sq_axis1 = axis1*axis1, sq_axis2 = axis2*axis2;
int p;
int x = 0, y = axis2;
int px = 0, py = 2*sq_axis1*y;
/* draw four points on the long and short axis of the ellipse */
plotpoint_set(xpos, ypos, x, y);
/* calculate the initial value in area 1 */
p = (int)((sq_axis2-(sq_axis1*axis2)+(0.25*sq_axis1)));
while(px < py){
++x;
px += 2*sq_axis2;
if(p<0){
p += sq_axis2+px;
}else{
--y;
py -= 2*sq_axis1;
p += sq_axis2+px-py;
}
plotpoint_set(xpos, ypos, x, y);
}
/* calculate the initial value with the last point calculated in the area 1 */
p = (int)((sq_axis2*(x+0.5)*(x+0.5)+sq_axis1*(y-1)*(y-1)-sq_axis1*sq_axis2));
while(y > 0){
--y;
py -= 2*sq_axis1;
if(p > 0){
p += sq_axis1-py;
}else{
++x;
px += 2*sq_axis2;
p += sq_axis1-py+px;
}
plotpoint_set(xpos, ypos, x, y);
}
}
/*!
\brief fill the whole rectangle
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[in] width: width of the rectangle
\param[in] height: height of the rectangle
\param[out] none
\retval none
*/
void lcd_rectangle_fill(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height)
{
uint16_t x, y;
for(x = xpos; x < xpos + width; x++){
for(y = ypos; y < ypos + height; y++){
pixel_set(x, y);
}
}
}
/*!
\brief display the string on LCD
\param[in] stringline: line to display the character
\arg LCD_LINE_x(x=0..39 for 480*272)
\param[in] ptr: a pointer to the string
\param[out] none
\retval none
*/
void lcd_string_display(uint16_t stringline, uint8_t *ptr)
{
uint16_t column = 0;
while((column < LCD_PIXEL_WIDTH) && (*ptr != 0)){
/* display character on LCD */
lcd_char_display(stringline, column, *ptr);
column += current_font->width;
ptr++;
}
}
/*!
\brief display the vertical string on LCD
\param[in] stringline: line to display the character
\arg LCD_LINE_x(x=0..39 for 480*272)
\param[in] offset: offset value
\param[in] ptr: a pointer to the string
\param[out] none
\retval none
*/
void lcd_vertical_string_display(uint16_t stringline, uint16_t offset, uint8_t *ptr)
{
uint16_t column = LCD_PIXEL_HEIGHT - (current_font->width + offset + 2);
while((column > 0) && (*ptr != 0)){
/* display character on LCD */
lcd_vertical_char_display(stringline, column, *ptr);
column -= current_font->width;
ptr++;
}
}
/*!
\brief display the character on LCD
\param[in] line: line to display the character
\arg LCD_LINE_x(x=0..39 for 480*272)
\param[in] column: column address
\param[in] ascii: character ascii code(from 0x20 to 0x7E)
\param[out] none
\retval none
*/
void lcd_char_display(uint16_t line, uint16_t column, uint8_t ascii)
{
ascii -= 32;
lcd_char_draw(line, column, ¤t_font->table[ascii * current_font->height]);
}
/*!
\brief display the vertical character on LCD
\param[in] line: line to display the character
\arg LCD_LINE_x(x=0..39 for 480*272)
\param[in] column: column address
\param[in] ascii: character ascii code(from 0x20 to 0x7E)
\param[out] none
\retval none
*/
void lcd_vertical_char_display(uint16_t line, uint16_t column, uint8_t ascii)
{
ascii -= 32;
lcd_vertical_char_draw(line, column, ¤t_font->table[ascii * current_font->height]);
}
/*!
\brief draw the character on LCD
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[in] c: a pointer to the character
\param[out] none
\retval none
*/
static void lcd_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c)
{
uint32_t index = 0, counter = 0, x = 0;
uint32_t xaddress = 0;
x = xpos * LCD_PIXEL_WIDTH * 2;
xaddress += ypos;
for(index = 0; index < current_font->height; index++){
for(counter = 0; counter < current_font->width; counter++){
if((((c[index] & ((0x80 << ((current_font->width / 12) * 8)) >> counter)) == 0x00) && (current_font->width <= 12))||
(((c[index] & (0x1 << counter)) == 0x00) && (current_font->width > 12))){
/* write the background color */
*(__IO uint16_t*) (current_framebuffer + (2*xaddress) + x) = current_backcolor;
}else{
/* write the text color */
*(__IO uint16_t*) (current_framebuffer + (2*xaddress) + x) = current_textcolor;
}
xaddress++;
}
xaddress += (LCD_PIXEL_WIDTH - current_font->width);
}
}
/*!
\brief draw the vertical character on LCD
\param[in] xpos: position of x
\param[in] ypos: position of y
\param[in] c: a pointer to the character
\param[out] none
\retval none
*/
static void lcd_vertical_char_draw(uint16_t xpos, uint16_t ypos, const uint16_t *c)
{
uint32_t index = 0, counter = 0;
for(index = 0; index < current_font->height; index++){
for(counter = 0; counter < current_font->width; counter++){
if((((c[index] & ((0x80 << ((current_font->width / 12) * 8)) >> counter)) == 0x00) && (current_font->width <= 12))||
(((c[index] & (0x1 << counter)) == 0x00) && (current_font->width > 12))){
if((int16_t)(xpos + index) < 0 || (int16_t)(xpos + index) > (LCD_PIXEL_WIDTH - 1) || (int16_t)(ypos + current_font->width - counter) < 0 || (int16_t)(ypos + current_font->width - counter) > (LCD_PIXEL_HEIGHT - 1)){
return;
}
*(__IO uint16_t*)(current_framebuffer + 2*(LCD_PIXEL_WIDTH * (int16_t)(ypos + current_font->width - counter) + (int16_t)(xpos + index))) = current_backcolor;
}else{
pixel_set(xpos + index, ypos + current_font->width - counter);
}
}
}
}
/*!
\brief set a pixel
\param[in] x: x position
\param[in] y: y position
\param[out] none
\retval none
*/
static void pixel_set(int16_t x, int16_t y)
{
if(x < 0 || x > (LCD_PIXEL_WIDTH - 1) || y < 0 || y > (LCD_PIXEL_HEIGHT - 1)){
return;
}
/* draw pixel with current text color */
*(__IO uint16_t*)(current_framebuffer + 2*(LCD_PIXEL_WIDTH * y + x)) = current_textcolor;
}
Code Effect
The font is crooked
|