/** * @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