mirror of
https://github.com/Fishwaldo/bl_mcu_sdk.git
synced 2025-07-10 23:08:34 +00:00
641 lines
No EOL
16 KiB
C
641 lines
No EOL
16 KiB
C
/**
|
|
* @file lcd.c
|
|
* @brief
|
|
*
|
|
* Copyright (c) 2021 Bouffalolab team
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
*/
|
|
|
|
#include "lcd.h"
|
|
#include "font.h"
|
|
#include "bflb_core.h"
|
|
|
|
#if defined(LCD_RESET_EN)
|
|
#include "bflb_gpio.h"
|
|
#include "bflb_mtimer.h"
|
|
#endif
|
|
|
|
uint8_t lcd_dir = 0;
|
|
uint16_t lcd_max_x = LCD_W - 1, lcd_max_y = LCD_H - 1;
|
|
|
|
/* MCU LCD Common interface */
|
|
#if (LCD_INTERFACE_TYPE == LCD_INTERFACE_DBI) || (LCD_INTERFACE_TYPE == LCD_INTERFACE_SPI)
|
|
|
|
/**
|
|
* @brief LCD init
|
|
*
|
|
* @return int
|
|
*/
|
|
int lcd_init(void)
|
|
{
|
|
int res;
|
|
|
|
#if defined(LCD_RESET_EN)
|
|
struct bflb_device_s *gpio;
|
|
|
|
/* gpio init */
|
|
gpio = bflb_device_get_by_name("gpio");
|
|
bflb_gpio_init(gpio, LCD_RESET_PIN, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_2);
|
|
|
|
/* lcd reset */
|
|
#if LCD_RESET_ACTIVE_LEVEL
|
|
bflb_gpio_set(gpio, LCD_RESET_PIN);
|
|
#else
|
|
bflb_gpio_reset(gpio, LCD_RESET_PIN);
|
|
#endif
|
|
|
|
bflb_mtimer_delay_ms(LCD_RESET_HOLD_MS);
|
|
|
|
/* lcd recovery */
|
|
#if LCD_RESET_ACTIVE_LEVEL
|
|
bflb_gpio_reset(gpio, LCD_RESET_PIN);
|
|
#else
|
|
bflb_gpio_set(gpio, LCD_RESET_PIN);
|
|
#endif
|
|
|
|
bflb_mtimer_delay_ms(LCD_RESET_DELAY);
|
|
#endif
|
|
|
|
res = _LCD_FUNC_DEFINE(init);
|
|
|
|
return res;
|
|
}
|
|
|
|
static int lcd_async_callback_enable(bool enable)
|
|
{
|
|
_LCD_FUNC_DEFINE(async_callback_enable, enable);
|
|
return 0;
|
|
}
|
|
|
|
int lcd_async_callback_register(void (*callback)(void))
|
|
{
|
|
_LCD_FUNC_DEFINE(async_callback_register, callback);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set display direction and mir
|
|
*
|
|
* @param dir 0~3 : 0~270 Angle
|
|
* @param mir_flag 0:normal 1:Horizontal Mirroring(if support)
|
|
* @return int
|
|
*/
|
|
int lcd_set_dir(uint8_t dir, uint8_t mir_flag)
|
|
{
|
|
dir %= 4;
|
|
lcd_dir = dir;
|
|
if (dir == 0 || dir == 2) {
|
|
lcd_max_x = LCD_W - 1;
|
|
lcd_max_y = LCD_H - 1;
|
|
} else {
|
|
lcd_max_x = LCD_H - 1;
|
|
lcd_max_y = LCD_W - 1;
|
|
}
|
|
|
|
return _LCD_FUNC_DEFINE(set_dir, dir, mir_flag);
|
|
}
|
|
|
|
/**
|
|
* @brief Draws a point at the specified position
|
|
*
|
|
* @param x X coordinate
|
|
* @param y Y coordinate
|
|
* @param color
|
|
* @return int
|
|
*/
|
|
int lcd_draw_point(uint16_t x, uint16_t y, lcd_color_t color)
|
|
{
|
|
_LCD_FUNC_DEFINE(draw_point, x, y, color);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Draw a monochrome rectangle (May be less efficient)
|
|
*
|
|
* @param x1 start coordinate
|
|
* @param y1 start coordinate
|
|
* @param x2 end coordinate
|
|
* @param y2 end coordinate
|
|
* @param color
|
|
* @return int
|
|
*/
|
|
int lcd_draw_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t color)
|
|
{
|
|
_LCD_FUNC_DEFINE(draw_area, x1, y1, x2, y2, color);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Draw a picture in the designated area(blocking),Will wait for the drawing to finish
|
|
*
|
|
* @param x1 start coordinate
|
|
* @param y1 start coordinate
|
|
* @param x2 end coordinate
|
|
* @param y2 end coordinate
|
|
* @param picture
|
|
* @return int
|
|
*/
|
|
int lcd_draw_picture_blocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t *picture)
|
|
{
|
|
_LCD_FUNC_DEFINE(draw_picture_blocking, x1, y1, x2, y2, picture);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Draw a picture in the designated area(nonblocking,if it supports),
|
|
* Must be calle lcd_draw_is_busy! and (lcd_draw_is_busyd()==1) before performing other drawing and changing picture data!
|
|
*
|
|
* @param x1 start coordinate
|
|
* @param y1 start coordinate
|
|
* @param x2 end coordinate
|
|
* @param y2 end coordinate
|
|
* @param picture
|
|
* @return int
|
|
*/
|
|
int lcd_draw_picture_nonblocking(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t *picture)
|
|
{
|
|
_LCD_FUNC_DEFINE(draw_picture_nonblocking, x1, y1, x2, y2, picture);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if it is drawing, must call it After call lcd_draw_picture_nonblocking
|
|
*
|
|
* @return int 1:lcd Drawing,Prohibit other operations! 0:Drawing is over
|
|
*/
|
|
int lcd_draw_is_busy(void)
|
|
{
|
|
return _LCD_FUNC_DEFINE(draw_is_busy);
|
|
}
|
|
|
|
/**
|
|
* @brief clear lcd
|
|
*
|
|
* @param color
|
|
* @return int
|
|
*/
|
|
int lcd_clear(lcd_color_t color)
|
|
{
|
|
lcd_draw_area(0, 0, lcd_max_x, lcd_max_y, color);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
*
|
|
* @param x1 start coordinate
|
|
* @param y1 start coordinate
|
|
* @param x2 end coordinate
|
|
* @param y2 end coordinate
|
|
* @param color
|
|
* @return int
|
|
*/
|
|
int lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t color)
|
|
{
|
|
int xVariation, yVariation, temp;
|
|
int absX, absY, i;
|
|
xVariation = x2 - x1;
|
|
yVariation = y2 - y1;
|
|
absX = ABS(xVariation);
|
|
absY = ABS(yVariation);
|
|
|
|
if (absX > absY) {
|
|
for (i = 0; i < absX + 1; i++) {
|
|
temp = yVariation * 100 / absX * i / 100;
|
|
|
|
if (xVariation > 0) {
|
|
lcd_draw_point(x1 + i, y1 + temp, color);
|
|
} else {
|
|
lcd_draw_point(x1 - i, y1 + temp, color);
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < absY + 1; i++) {
|
|
temp = xVariation * 100 / absY * i / 100;
|
|
|
|
if (yVariation > 0) {
|
|
lcd_draw_point(x1 + temp, y1 + i, color);
|
|
} else {
|
|
lcd_draw_point(x1 + temp, y1 - i, color);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
*
|
|
* @param x1 start coordinate
|
|
* @param y1 start coordinate
|
|
* @param x2 end coordinate
|
|
* @param y2 end coordinate
|
|
* @param color
|
|
* @return int
|
|
*/
|
|
int lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t color)
|
|
{
|
|
lcd_draw_line(x1, y1, x2, y1, color);
|
|
lcd_draw_line(x2, y1, x2, y2, color);
|
|
lcd_draw_line(x2, y2, x1, y2, color);
|
|
lcd_draw_line(x1, y2, x1, y1, color);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief draw a circle
|
|
*
|
|
* @param x coordinate
|
|
* @param y coordinate
|
|
* @param r
|
|
* @param color
|
|
* @return int
|
|
*/
|
|
int lcd_draw_circle(uint16_t x, uint16_t y, uint16_t r, lcd_color_t color)
|
|
{
|
|
int a = 0, b;
|
|
int di;
|
|
b = r;
|
|
di = 3 - (r << 1);
|
|
|
|
while (a <= b) {
|
|
lcd_draw_point(x - b, y - a, color);
|
|
lcd_draw_point(x + b, y - a, color);
|
|
lcd_draw_point(x - a, y + b, color);
|
|
lcd_draw_point(x - b, y - a, color);
|
|
lcd_draw_point(x - a, y - b, color);
|
|
lcd_draw_point(x + b, y + a, color);
|
|
lcd_draw_point(x + a, y - b, color);
|
|
lcd_draw_point(x + a, y + b, color);
|
|
lcd_draw_point(x - b, y + a, color);
|
|
a++;
|
|
|
|
if (di < 0) {
|
|
di += 4 * a + 6;
|
|
} else {
|
|
di += 10 + 4 * (a - b);
|
|
b--;
|
|
}
|
|
|
|
lcd_draw_point(x + a, y + b, color);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if FONT_ASCII_16X8
|
|
|
|
/**
|
|
* @brief Draw font(16*8) ,Use double buffer to speed up drawing
|
|
*
|
|
* @param x start coordinate
|
|
* @param y start coordinate
|
|
* @param color font color
|
|
* @param bk_color Background color
|
|
* @param str The string to be displayed
|
|
* @param num number of characters displayed
|
|
* @return int
|
|
*/
|
|
int lcd_draw_str_ascii16(uint16_t x, uint16_t y, lcd_color_t color, lcd_color_t bk_color, uint8_t *str, uint8_t num)
|
|
{
|
|
lcd_color_t draw_buff[2][16 * 8];
|
|
uint16_t buff_color_num;
|
|
uint8_t buff_using_num = 0;
|
|
uint8_t ch, temp;
|
|
uint16_t x0 = x;
|
|
|
|
lcd_async_callback_enable(false);
|
|
|
|
for (uint16_t i = 0; i < num && str[i]; i++) {
|
|
if (str[i] < 128) {
|
|
if (x > lcd_max_x - 8) {
|
|
x = x0;
|
|
y += 16;
|
|
}
|
|
if (x > lcd_max_x - 8 || y > lcd_max_y - 16)
|
|
break;
|
|
|
|
ch = str[i];
|
|
|
|
if (ch >= ' ') {
|
|
ch = ch - ' ';
|
|
} else if (ch == '\n') {
|
|
x = x0;
|
|
y += 16;
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
buff_color_num = 0;
|
|
for (uint8_t j = 0; j < 16; j++) {
|
|
temp = font_ascii_16x8[ch * 16 + j];
|
|
for (uint8_t k = 0; k < 8; k++) {
|
|
if (temp & (0x80 >> k))
|
|
draw_buff[buff_using_num][buff_color_num++] = color;
|
|
else
|
|
draw_buff[buff_using_num][buff_color_num++] = bk_color;
|
|
}
|
|
}
|
|
|
|
while (lcd_draw_is_busy()) {
|
|
};
|
|
|
|
lcd_draw_picture_nonblocking(x, y, x + 7, y + 15, draw_buff[buff_using_num]);
|
|
buff_using_num = !buff_using_num;
|
|
x += 8;
|
|
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
while (lcd_draw_is_busy()) {
|
|
};
|
|
|
|
lcd_async_callback_enable(true);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* RGB LCD Common interface */
|
|
#elif (LCD_INTERFACE_TYPE == LCD_INTERFACE_DPI) || (LCD_INTERFACE_TYPE == LCD_INTERFACE_DSI_VIDIO)
|
|
|
|
/**
|
|
* @brief lcd_dpi_init
|
|
*
|
|
* @param screen_buffer First screen memory
|
|
* @return int
|
|
*/
|
|
int lcd_init(lcd_color_t *screen_buffer)
|
|
{
|
|
return _LCD_FUNC_DEFINE(init, screen_buffer);
|
|
}
|
|
|
|
/**
|
|
* @brief Switch the screen, If it is in single-screen mode, there is no effect
|
|
*
|
|
* @param screen_id screen id
|
|
* @return int
|
|
*/
|
|
int lcd_screen_switch(lcd_color_t *screen_buffer)
|
|
{
|
|
return _LCD_FUNC_DEFINE(screen_switch, screen_buffer);
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the id of the screen in use
|
|
*
|
|
* @return int screen id
|
|
*/
|
|
lcd_color_t *lcd_get_screen_using(void)
|
|
{
|
|
return _LCD_FUNC_DEFINE(get_screen_using);
|
|
}
|
|
|
|
int lcd_frame_callback_register(uint32_t callback_type, void (*callback)(void))
|
|
{
|
|
return _LCD_FUNC_DEFINE(frame_callback_register, callback_type, callback);
|
|
}
|
|
|
|
int lcd_draw_point(lcd_color_t *screen_buffer, uint16_t x, uint16_t y, lcd_color_t color)
|
|
{
|
|
if (screen_buffer == NULL) {
|
|
return -1;
|
|
} else if (x >= LCD_W || y >= LCD_H) {
|
|
return -2;
|
|
}
|
|
|
|
screen_buffer[x * LCD_W + y] = color;
|
|
return 0;
|
|
}
|
|
|
|
int lcd_draw_area(lcd_color_t *screen_buffer, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t color)
|
|
{
|
|
if (screen_buffer == NULL) {
|
|
return -1;
|
|
} else if (x1 >= LCD_W || y1 >= LCD_H) {
|
|
return -2;
|
|
}
|
|
|
|
screen_buffer += y1 * LCD_W;
|
|
|
|
x2 = (x2 < LCD_W) ? x2 : (LCD_W - 1);
|
|
y2 = (y2 < LCD_H) ? y2 : (LCD_H - 1);
|
|
|
|
for (uint16_t i = y1; i <= y2; i++) {
|
|
for (uint16_t j = x1; j <= x2; j++) {
|
|
screen_buffer[j] = color;
|
|
}
|
|
screen_buffer += LCD_W;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int lcd_clear(lcd_color_t *screen_buffer, lcd_color_t color)
|
|
{
|
|
if (screen_buffer == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
return lcd_draw_area(screen_buffer, 0, 0, LCD_W, LCD_H, color);
|
|
}
|
|
|
|
int lcd_draw_picture(lcd_color_t *screen_buffer, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t *picture)
|
|
{
|
|
if (screen_buffer == NULL) {
|
|
return -1;
|
|
} else if (x1 >= LCD_W || y1 >= LCD_H || x2 >= LCD_W || y2 >= LCD_H) {
|
|
return -2;
|
|
}
|
|
|
|
screen_buffer += y1 * LCD_W;
|
|
|
|
for (uint16_t i = y1; i <= y2; i++) {
|
|
for (uint16_t j = x1; j <= x2; j++) {
|
|
screen_buffer[j] = *picture;
|
|
picture++;
|
|
}
|
|
screen_buffer += LCD_W;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int lcd_draw_line(lcd_color_t *screen_buffer, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t color)
|
|
{
|
|
if (screen_buffer == NULL) {
|
|
return -1;
|
|
} else if (x1 >= LCD_W || y1 >= LCD_H || x2 >= LCD_W || y2 >= LCD_H) {
|
|
return -2;
|
|
}
|
|
|
|
if (x1 == x2) {
|
|
if (y1 > y2) {
|
|
uint16_t a = y1;
|
|
y1 = y2;
|
|
y2 = a;
|
|
}
|
|
screen_buffer += y1 * LCD_W;
|
|
for (; y1 <= y2; y1++) {
|
|
screen_buffer[x1] = color;
|
|
screen_buffer += LCD_W;
|
|
}
|
|
} else if (y1 == y2) {
|
|
if (x1 > x2) {
|
|
uint16_t b = x1;
|
|
x1 = x2;
|
|
x2 = b;
|
|
}
|
|
screen_buffer += y1 * LCD_W;
|
|
for (; x1 <= x2; x1++) {
|
|
screen_buffer[x1] = color;
|
|
}
|
|
} else {
|
|
int xVariation, yVariation, temp;
|
|
int absX, absY, i;
|
|
xVariation = x2 - x1;
|
|
yVariation = y2 - y1;
|
|
absX = ABS(xVariation);
|
|
absY = ABS(yVariation);
|
|
|
|
if (absX > absY) {
|
|
for (i = 0; i < absX + 1; i++) {
|
|
temp = yVariation * 100 / absX * i / 100;
|
|
|
|
if (xVariation > 0) {
|
|
screen_buffer[(x1 + i) * LCD_W + (y1 + temp)] = color;
|
|
} else {
|
|
screen_buffer[(x1 - i) * LCD_W + (y1 + temp)] = color;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < absY + 1; i++) {
|
|
temp = xVariation * 100 / absY * i / 100;
|
|
|
|
if (yVariation > 0) {
|
|
screen_buffer[(x1 + i) * LCD_W + (y1 + temp)] = color;
|
|
} else {
|
|
screen_buffer[(x1 + i) * LCD_W + (y1 - temp)] = color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int lcd_draw_rectangle(lcd_color_t *screen_buffer, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, lcd_color_t color)
|
|
{
|
|
lcd_draw_line(screen_buffer, x1, y1, x2, y1, color);
|
|
lcd_draw_line(screen_buffer, x2, y1, x2, y2, color);
|
|
lcd_draw_line(screen_buffer, x2, y2, x1, y2, color);
|
|
lcd_draw_line(screen_buffer, x1, y2, x1, y1, color);
|
|
return 0;
|
|
}
|
|
|
|
int lcd_draw_circle(lcd_color_t *screen_buffer, uint16_t x, uint16_t y, uint16_t r, lcd_color_t color)
|
|
{
|
|
int a = 0, b;
|
|
int di;
|
|
b = r;
|
|
di = 1 - r;
|
|
|
|
while (a <= b) {
|
|
screen_buffer[(x - b) * LCD_W + (y - a)] = color;
|
|
screen_buffer[(x + b) * LCD_W + (y - a)] = color;
|
|
screen_buffer[(x - a) * LCD_W + (y + b)] = color;
|
|
screen_buffer[(x - b) * LCD_W + (y - a)] = color;
|
|
screen_buffer[(x - a) * LCD_W + (y - b)] = color;
|
|
screen_buffer[(x + b) * LCD_W + (y + a)] = color;
|
|
screen_buffer[(x + a) * LCD_W + (y - b)] = color;
|
|
screen_buffer[(x + a) * LCD_W + (y + b)] = color;
|
|
screen_buffer[(x - b) * LCD_W + (y + a)] = color;
|
|
a++;
|
|
|
|
if (di < 0) {
|
|
di += (a << 1) + 3;
|
|
} else {
|
|
di += ((a - b) << 1) + 5;
|
|
b--;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if FONT_ASCII_16X8
|
|
|
|
/**
|
|
* @brief Draw font(16*8)
|
|
*
|
|
* @param x start coordinate
|
|
* @param y start coordinate
|
|
* @param color font color
|
|
* @param bk_color Background color
|
|
* @param str The string to be displayed
|
|
* @param num number of characters displayed
|
|
* @return int
|
|
*/
|
|
int lcd_draw_str_ascii16(lcd_color_t *screen_buffer, uint16_t x, uint16_t y, lcd_color_t color, lcd_color_t bk_color, uint8_t *str, uint8_t num)
|
|
{
|
|
lcd_color_t draw_buff[16 * 8];
|
|
uint16_t buff_color_num;
|
|
uint8_t ch, temp;
|
|
uint16_t x0 = x;
|
|
|
|
for (uint16_t i = 0; i < num && str[i]; i++) {
|
|
if (str[i] < 128) {
|
|
// if (x > LCD_W - 8) {
|
|
// x = x0;
|
|
// y += 16;
|
|
// }
|
|
if (x > LCD_W - 8 || y > LCD_H - 16)
|
|
break;
|
|
|
|
ch = str[i];
|
|
|
|
if (ch >= ' ') {
|
|
ch = ch - ' ';
|
|
} else if (ch == '\n') {
|
|
x = x0;
|
|
y += 16;
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
buff_color_num = 0;
|
|
for (uint8_t j = 0; j < 16; j++) {
|
|
temp = font_ascii_16x8[ch * 16 + j];
|
|
for (uint8_t k = 0; k < 8; k++) {
|
|
if (temp & (0x80 >> k))
|
|
draw_buff[buff_color_num++] = color;
|
|
else
|
|
draw_buff[buff_color_num++] = bk_color;
|
|
}
|
|
}
|
|
|
|
lcd_draw_picture(screen_buffer, x, y, x + 7, y + 15, draw_buff);
|
|
x += 8;
|
|
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#endif |