KT24-1110_65E-HA-651B/cpu/br25/ui_driver/lcd_spi/lcd_drive.c
2024-11-10 18:44:17 +08:00

606 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* LCD 调试等级,
* 0只打印错误
* 1打印错误和警告
* 2全部内容都调试内容打印 */
#define SPI_LCD_DEBUG_ENABLE 0
#include "includes.h"
#include "app_config.h"
#include "ui/ui_api.h"
#include "system/includes.h"
#include "system/timer.h"
#include "asm/spi.h"
#include "clock_cfg.h"
#include "asm/mcpwm.h"
#if (TCFG_UI_ENABLE && (TCFG_SPI_LCD_ENABLE || TCFG_SIMPLE_LCD_ENABLE))
#include "ui/ui_sys_param.h"
/* 选择SPIx模块作为推屏通道0、1、2 */
/* 但SPI0通常作为外部falsh使用一般不用SPI0 */
#define SPI_MODULE_CHOOSE TCFG_TFT_LCD_DEV_SPI_HW_NUM
/* 中断使能,一般推屏不需要 */
#ifndef LCD_SPI_INTERRUPT_ENABLE
#define LCD_SPI_INTERRUPT_ENABLE 1
#endif
#if LCD_SPI_INTERRUPT_ENABLE
#if SPI_MODULE_CHOOSE == 0
#define IRQ_SPI_IDX IRQ_SPI0_IDX
#elif SPI_MODULE_CHOOSE == 1
#define IRQ_SPI_IDX IRQ_SPI1_IDX
#elif SPI_MODULE_CHOOSE == 2
#define IRQ_SPI_IDX IRQ_SPI2_IDX
#else
#error "error! SPI_MODULE_CHOOSE defien error!"
#endif
#endif
int clk_get(const char *name);
int clk_set(const char *name, int clk);
void spi_clear_pending(spi_dev spi);
u8 spi_get_pending(spi_dev spi);
void spi_dma_set_addr_for_isr(spi_dev spi, void *buf, u32 len, u8 rw);;
void spi_dma_wait_finish();
void wdt_clear();
static int __lcd_backlight_ctrl(u8 on);
/* 屏幕驱动的接口 */
extern struct spi_lcd_init dev_drive;
struct lcd_spi_platform_data *spi_dat = NULL;
static int spi_pnd = false;
static u8 backlight_status = 0;
static u8 lcd_sleep_in = 0;
static volatile u8 is_lcd_busy = 0;
#define __this (&dev_drive)
#if LCD_SPI_INTERRUPT_ENABLE
/* SPI中断函数 */
// 注dma模式在发送数据时内部已经清理中断pnd
__attribute__((interrupt("")))
static void spi_isr()
{
if (spi_get_pending(spi_dat->spi_cfg)) {
/* printf("spi_isr\n"); */
/* spi_clear_pending(spi_dat->spi_cfg); */
if (is_lcd_busy) {
is_lcd_busy = 0;
}
spi_set_ie(spi_dat->spi_cfg, 0);
}
}
#endif
static void clock_critical_enter()
{
}
static void clock_critical_exit()
{
if (spi_dat && spi_dat->spi_cfg) {
spi_set_baud(spi_dat->spi_cfg, spi_get_baud(spi_dat->spi_cfg));
}
}
CLOCK_CRITICAL_HANDLE_REG(spi_lcd, clock_critical_enter, clock_critical_exit);
static void spi_init(int spi_cfg)
{
int err;
// spi gpio init
err = spi_open(spi_cfg);
if (err < 0) {
lcd_e("open spi falid\n");
}
#if LCD_SPI_INTERRUPT_ENABLE
// 配置中断优先级,中断函数
/* spi_set_ie(spi_cfg, 1); */
request_irq(IRQ_SPI_IDX, 3, spi_isr, 0);
#endif
}
// io口操作
void lcd_reset_l()
{
gpio_direction_output((u32)spi_dat->pin_reset, 0);
}
void lcd_reset_h()
{
gpio_direction_output((u32)spi_dat->pin_reset, 1);
}
void lcd_cs_l()
{
gpio_direction_output((u32)spi_dat->pin_cs, 0);
}
void lcd_cs_h()
{
gpio_direction_output((u32)spi_dat->pin_cs, 1);
}
void lcd_rs_l()
{
gpio_direction_output((u32)spi_dat->pin_rs, 0);
}
void lcd_rs_h()
{
gpio_direction_output((u32)spi_dat->pin_rs, 1);
}
void lcd_bl_l()
{
gpio_direction_output((u32)spi_dat->pin_bl, 0);
}
void lcd_bl_h()
{
gpio_direction_output((u32)spi_dat->pin_bl, 1);
}
u8 lcd_bl_io()
{
return spi_dat->pin_bl;
}
void spi_dma_wait_finish()
{
if (spi_pnd) {
while (!spi_get_pending(spi_dat->spi_cfg)) {
/* delay(10); */
extern void wdt_clear();
wdt_clear();
}
spi_clear_pending(spi_dat->spi_cfg);
spi_pnd = false;
}
}
int __spi_dma_send(spi_dev spi, void *buf, u32 len, u8 wait)
{
int err = 0;
if (!wait || spi_pnd) {
spi_dma_wait_finish();
}
spi_dma_set_addr_for_isr(spi_dat->spi_cfg, buf, len, 0);
spi_pnd = true;
asm("csync");
if (wait) {
spi_dma_wait_finish();
}
return err;
}
void spi_dma_send_byte(u8 dat)
{
int err = 0;
u32 _dat __attribute__((aligned(4))) = 0;
((u8 *)(&_dat))[0] = dat;
if (spi_dat) {
err = __spi_dma_send(spi_dat->spi_cfg, &_dat, 1, 1);
}
if (err < 0) {
lcd_e("spi dma send byte timeout\n");
}
}
u8 lcd_spi_recv_byte()
{
int err;
int ret;
#if 0
ret = spi_recv_byte_for_isr(spi_dat->spi_cfg);
asm("csync");
spi_pnd = true;
spi_dma_wait_finish();
#else
ret = spi_recv_byte(spi_dat->spi_cfg, &err);
spi_pnd = false;
#endif
return ret;
}
int lcd_spi_send_byte(u8 byte)
{
int ret;
#if 0
spi_send_byte_for_isr(spi_dat->spi_cfg, byte);
asm("csync");
spi_pnd = true;
spi_dma_wait_finish();
#else
ret = spi_send_byte(spi_dat->spi_cfg, byte);
spi_pnd = false;
#endif
return 0;
}
void lcd_spi_set_bit_mode(int mode)
{
spi_set_bit_mode(spi_dat->spi_cfg, mode);
}
void lcd_spi_dma_send_wait(u8 *map, u32 size)
{
int err = 0;
if (spi_dat) {
err = __spi_dma_send(spi_dat->spi_cfg, map, size, 1);
}
if (err < 0) {
lcd_e("spi dma send map timeout\n");
}
}
void spi_dma_send_map(u8 *map, u32 size)
{
int err = 0;
if (spi_dat) {
err = __spi_dma_send(spi_dat->spi_cfg, map, size, 0);
}
if (err < 0) {
lcd_e("spi dma send map timeout\n");
}
}
void spi_dma_recv_data(u8 *buf, u32 size)
{
int err = 0;
if (spi_dat) {
err = spi_dma_recv(spi_dat->spi_cfg, buf, size);
}
if (err < 0) {
lcd_e("spi dma recv timeout\n");
}
}
static void spi_init_code(const InitCode *code, u8 cnt)
{
u8 i, j;
for (i = 0; i < cnt; i++) {
if (code[i].cmd == REGFLAG_DELAY) {
extern void wdt_clear(void);
wdt_clear();
delay2ms(code[i].cnt / 2);
} else {
__this->WriteComm(code[i].cmd);
lcd_d("cmd:%x ", code[i].cmd);
for (j = 0; j < code[i].cnt; j++) {
__this->WriteData(code[i].dat[j]);
lcd_d("%02x ", code[i].dat[j]);
}
lcd_d("\n");
}
}
}
static void lcd_dev_init(void *p)
{
struct ui_devices_cfg *cfg = (struct ui_devices_cfg *)p;
int err = 0;
spi_dat = (struct lcd_spi_platform_data *)cfg->private_data;
ASSERT(spi_dat, "Error! spi io not config");
printf("spi pin rest:%d, cs:%d, rs:%d, spi:%d\n", spi_dat->pin_reset, spi_dat->pin_cs, spi_dat->pin_rs, spi_dat->spi_cfg);
if (spi_dat->pin_reset != -1) {
gpio_direction_output((u32)spi_dat->pin_reset, 1);
}
gpio_direction_output((u32)spi_dat->pin_cs, 1);
gpio_direction_output((u32)spi_dat->pin_rs, 1);
if (!__this->soft_spi) {
spi_init(spi_dat->spi_cfg);
}
if (__this->Reset) { // 如果有硬件复位
__this->Reset();
}
if (__this->initcode && __this->initcode_cnt) {
spi_init_code(__this->initcode, __this->initcode_cnt); // 初始化屏幕
} else if (__this->Init) {
__this->Init();
}
}
static int lcd_init(void *p)
{
printf("lcd_init ...\n");
lcd_dev_init(p);
return 0;
}
static int lcd_get_screen_info(struct lcd_info *info)
{
info->width = __this->lcd_width;
info->height = __this->lcd_height;
info->color_format = __this->color_format;
info->interface = __this->interface;
info->col_align = __this->column_addr_align;
info->row_align = __this->row_addr_align;
info->bl_status = backlight_status;
ASSERT(info->col_align, " = 0, lcd driver column address align error, default value is 1");
ASSERT(info->row_align, " = 0, lcd driver row address align error, default value is 1");
return 0;
}
static int lcd_buffer_malloc(u8 **buf, u32 *size)
{
*buf = __this->dispbuf;
*size = __this->bufsize;
return 0;
}
static int lcd_buffer_free(u8 *buf)
{
return 0;
}
static int lcd_set_draw_area(u16 xs, u16 xe, u16 ys, u16 ye)
{
if ((is_lcd_busy == 0x11) || lcd_sleep_in) {
return 0;
}
is_lcd_busy = 1;
spi_set_ie(spi_dat->spi_cfg, 0);
spi_dma_wait_finish();
__this->SetDrawArea(xs, xe, ys, ye);
/* is_lcd_busy = 0; */
return 0;
}
static int lcd_draw(u8 *buf, u32 len, u8 wait)
{
if ((is_lcd_busy == 0x11) || lcd_sleep_in) {
return 0;
}
/* is_lcd_busy = 1; */
/* int clk = clk_get("sys"); */
/* if (clk != 192000000) { */
/* clk_set("sys", 192000000L); */
/* } */
if (wait) {
__this->WriteMap(buf, len);
#if LCD_SPI_INTERRUPT_ENABLE
spi_set_ie(spi_dat->spi_cfg, 1);
#else
is_lcd_busy = 0;
#endif
} else {
__this->WriteMap(buf, len);
is_lcd_busy = 0;
}
return 0;
}
static int lcd_draw_gage(u8 *buf, u8 page_star, u8 page_len)
{
if ((is_lcd_busy == 0x11) || lcd_sleep_in) {
return 0;
}
__this->WritePAGE(buf, page_star, page_len);
is_lcd_busy = 0;
return 0;
}
static int lcd_clear_screen(u16 color)
{
int i;
int buffer_lines;
int remain;
int draw_line;
int y;
if (__this->color_format == LCD_COLOR_RGB565) {
buffer_lines = __this->bufsize / __this->lcd_width / 2;
for (i = 0; i < buffer_lines * __this->lcd_width; i++) {
__this->dispbuf[2 * i] = color >> 8;
__this->dispbuf[2 * i + 1] = color;
}
y = 0;
remain = __this->lcd_height;
while (remain) {
draw_line = buffer_lines > remain ? remain : buffer_lines;
lcd_set_draw_area(0, __this->lcd_width - 1, y, y + draw_line - 1);
lcd_draw(__this->dispbuf, draw_line * __this->lcd_width * 2, 0);
remain -= draw_line;
y += draw_line;
}
spi_dma_wait_finish();
} else if (__this->color_format == LCD_COLOR_MONO) {
__this->SetDrawArea(0, -1, 0, -1);
memset(__this->dispbuf, 0x00, __this->lcd_width * __this->lcd_height / 8);
__this->WriteMap(__this->dispbuf, __this->lcd_width * __this->lcd_height / 8);
} else {
ASSERT(0, "the color_format %d not support yet!", __this->color_format);
}
return 0;
}
int lcd_backlight_ctrl(u8 on)
{
while (is_lcd_busy);
is_lcd_busy = 0x11;
__lcd_backlight_ctrl(on);
is_lcd_busy = 0;
return 0;
}
int lcd_sleep_ctrl(u8 enter)
{
if ((!!enter) == lcd_sleep_in) {
return -1;
}
while (is_lcd_busy);
is_lcd_busy = 0x11;
if (enter) {
if (__this->EnterSleep) {
__this->EnterSleep();
}
lcd_sleep_in = true;
} else {
if (__this->ExitSleep) {
__this->ExitSleep();
}
clock_add_set(LCD_UI_CLK);
lcd_sleep_in = false;
}
is_lcd_busy = 0;
return 0;
}
static int __lcd_backlight_ctrl(u8 on)
{
static u8 first_power_on = true;
if (on) {
#if (TCFG_SPI_LCD_ENABLE)
on = get_backlight_brightness() & 0xff;
#else
on = 100;
#endif
}
if (first_power_on) {
backlight_status = true;
lcd_clear_screen(0x0000);
backlight_status = false;
//os_time_dly(6);
first_power_on = false;
}
if (__this->BackLightCtrl) {
if (on) {
clock_add_set(LCD_UI_CLK);
if (__this->ExitSleep) {
__this->ExitSleep();
}
__this->BackLightCtrl(on);
} else {
__this->BackLightCtrl(false);
if (__this->EnterSleep) {
__this->EnterSleep();
}
clock_remove_set(LCD_UI_CLK);
}
lcd_sleep_in = !!!on;
backlight_status = on;
}
return 0;
}
int lcd_backlight_status()
{
return backlight_status;
}
struct lcd_interface *lcd_get_hdl()
{
struct lcd_interface *p;
ASSERT(lcd_interface_begin != lcd_interface_end, "don't find lcd interface!");
for (p = lcd_interface_begin; p < lcd_interface_end; p++) {
return p;
}
return NULL;
}
/*
* 注意MCPWM的输出IO可以是固定的硬件IO或者是映射到任何一个IO但由于映射到非硬件IO需要占用一个OUTPUT_CHANNEL,
* 因此建议设计是尽量使用带MCPWM输出的硬件IO。硬件IO一共有六组分别是
* (IO_PORTA_00, IO_PORTA_01)
* (IO_PORTB_00, IO_PORTB_02)
* (IO_PORTB_04, IO_PORTB_06)
* (IO_PORTB_09, IO_PORTB_10)
* (IO_PORTA_09, IO_PORTA_10)
* (IO_PORTC_04, IO_PORTC_05)
* 上述五组分别对应ch0、ch1、ch2、ch3、ch4、ch5。使用对应硬件IO时pwm_p_data.pwm_ch_num和pwm_p_data.pwm_timer_num需要改成对应的值
* 详细请参考mcpwm.c中的mcpwm_test
*/
struct pwm_platform_data lcd_pwm_p_data;
void lcd_mcpwm_init()
{
extern void mcpwm_init(struct pwm_platform_data * arg);
lcd_pwm_p_data.pwm_aligned_mode = pwm_edge_aligned; //边沿对齐
lcd_pwm_p_data.frequency = 10000; //KHz
lcd_pwm_p_data.pwm_ch_num = pwm_ch0; //通道0
lcd_pwm_p_data.pwm_timer_num = pwm_timer0; //时基选择通道0
lcd_pwm_p_data.duty = 0; //占空比50%
//hw
lcd_pwm_p_data.h_pin = -1;//IO_PORTA_00; //没有则填 -1。h_pin_output_ch_num无效可不配置
lcd_pwm_p_data.l_pin = lcd_bl_io();//TCFG_BACKLIGHT_PWM_IO; //硬件引脚l_pin_output_ch_num无效可不配置
//output_channel
/* pwm_p_data.h_pin_output_ch_num = 0; //output channel0 非硬件PWM_IO时需要打开*/
lcd_pwm_p_data.l_pin_output_ch_num = 1; //output channel1 非硬件PWM_IO时需要打开
lcd_pwm_p_data.complementary_en = 1; //两个引脚的波形, 1: 互补, 0: 同步;
mcpwm_init(&lcd_pwm_p_data);
}
REGISTER_LCD_INTERFACE(lcd) = {
.init = lcd_init,
.get_screen_info = lcd_get_screen_info,
.buffer_malloc = lcd_buffer_malloc,
.buffer_free = lcd_buffer_free,
.draw = lcd_draw,
.set_draw_area = lcd_set_draw_area,
.clear_screen = lcd_clear_screen,
.backlight_ctrl = __lcd_backlight_ctrl,
.draw_page = lcd_draw_gage,
};
static u8 lcd_idle_query(void)
{
return !is_lcd_busy;
}
REGISTER_LP_TARGET(lcd_lp_target) = {
.name = "lcd",
.is_idle = lcd_idle_query,
};
#endif