嵌入式技术论坛
直播中

百灵千岛酱

9年用户 528经验值
擅长:处理器/DSP
私信 关注
[问答]

浅谈RK2706播放器printf定位到LCD的通用流程

看RM970原理图得知LCD中IM0 IM1 ID0都接上了GND,查LCD(控制器为SPFD5420A)得知,此时LCD工作于18bit,80-system.既为最常见的MCU接口.所以配置RK2706来说是这么一个通用流程:

1.设置相应引脚为LCD功能

void iomux_lcd(void)
{
   unsigned long muxa;
   muxa = SCU_IOMUXA_CON & ~(IOMUX_LCD_VSYNC|IOMUX_LCD_DEN|0xff);
   muxa |= IOMUX_LCD_D18|IOMUX_LCD_D20|IOMUX_LCD_D22|IOMUX_LCD_D17|IOMUX_LCD_D16;
   SCU_IOMUXA_CON = muxa;
   SCU_IOMUXB_CON |=
   IOMUX_LCD_D815;
}

2.设置LCD寄存器,配置时序.

void lcdctrl_init(void)
{
  /* alpha b111
   * stop at current frame complete
   * MCU mode
   * 24b RGB
   */
  LCDC_CTRL = ALPHA(7) | LCDC_STOP | LCDC_MCU | RGB24B;
  MCU_CTRL = ALPHA_BASE(0x3f) | MCU_CTRL_BYPASS;
  HOR_ACT = 400 + 3;    /* define horizonatal active region */
  VERT_ACT = 240;       /* define vertical active region */
  VERT_PERIOD = 0xfff;  /* CSn/WEn/RDn signal timings */
  LINE0_YADDR = LINE_ALPHA_EN | 0x7fe;
  LINE1_YADDR = LINE_ALPHA_EN | ((1 * 400) - 2);
  LINE2_YADDR = LINE_ALPHA_EN | ((2 * 400) - 2);
  LINE3_YADDR = LINE_ALPHA_EN | ((3 * 400) - 2);
  LINE0_UVADDR = 0x7fe + 1;
  LINE1_UVADDR = ((1 * 400) - 2 + 1);
  LINE2_UVADDR = ((2 * 400) - 2 + 1);
  LINE3_UVADDR = ((3 * 400) - 2 + 1);
  LCDC_INTR_MASK = INTR_MASK_LINE; /* INTR_MASK_EVENLINE; */
}

3.初始化LCD寄存器.

void lcd_init_device()
{
  unsigned int x, y;
  iomux_lcd();       /* setup pins for 18bit lcd interface */
  lcdctrl_init();    /* basic lcdc module configuration */
  lcdctrl_bypass(1); /* run in bypass mode - all writes goes directly to lcd controller */
  lcd_write_reg(RESET, 0x0001);
  delay_nop(10000);
  lcd_write_reg(RESET, 0x0000);
  delay_nop(10000);
  lcd_write_reg(IF_ENDIAN,    0x0000); /* order of receiving data */
  lcd_write_reg(DRIVER_OUT_CTRL, 0x0000);
  lcd_write_reg(ENTRY_MODE,      0x1038);
  lcd_write_reg(WAVEFORM_CTRL,   0x0100);
  lcd_write_reg(SHAPENING_CTRL,  0x0000);
  lcd_write_reg(DISPLAY_CTRL2,   0x0808);
  lcd_write_reg(LOW_PWR_CTRL1,   0x0001);
  lcd_write_reg(LOW_PWR_CTRL2,   0x0010);
  lcd_write_reg(EXT_DISP_CTRL1,  0x0000);
  lcd_write_reg(EXT_DISP_CTRL2,  0x0000);
  lcd_write_reg(BASE_IMG_SIZE,   0x3100);
  ......................
}

细心的朋友会发现这些寄存器初始值都为16bit,而LCD为18bit,所以得用下面函数转换成18bit后才能写给LCD.

__inline unsigned int lcd_data_transform(unsigned int data)
{
    /* 18 bit interface */
    unsigned int r, g, b;
    r = (data & 0x0000fc00)<<8;
    g = ((data & 0x00000300) << 6) | ((data & 0x000000e0) << 5);
    b = (data & 0x00000001f) << 3;
    return (r | g | b);
}

初试化完毕后,一个不约而同的规则便是实现画横竖线与打点了.

/* 画水平线 */
void rt_hw_lcd_draw_hline(rtgui_color_t *c, rt_base_t x1, rt_base_t x2, rt_base_t y)
{
    lcd_write_reg(WINDOW_H_START,  y);
    lcd_write_reg(WINDOW_H_END,    y);
    lcd_write_reg(WINDOW_V_START,  x1);
    lcd_write_reg(WINDOW_V_END,    x2);
    lcd_write_reg(GRAM_H_ADDR,     y);
    lcd_write_reg(GRAM_V_ADDR,     x1);
    lcd_cmd(GRAM_WRITE); /* Prepare to write GRAM */
    while (x1 < x2)
    {
        LCD_DATA = lcd_pixel_transform(*c);
        x1++;
    }
}
/* 垂直线 */
void rt_hw_lcd_draw_vline(rtgui_color_t *c, rt_base_t x, rt_base_t y1, rt_base_t y2)
{
    lcd_write_reg(WINDOW_H_START,  y1);
    lcd_write_reg(WINDOW_H_END,    y2);
    lcd_write_reg(WINDOW_V_START,  x);
    lcd_write_reg(WINDOW_V_END,    x);
    lcd_SetCursor(x, y1);
    lcd_cmd(GRAM_WRITE); /* Prepare to write GRAM */
    while (y1 < y2)
    {
        LCD_DATA = lcd_pixel_transform(*c);
        y1++;
    }
}
/*  设置像素点 颜色,X,Y */
void rt_hw_lcd_set_pixel(rtgui_color_t *c, rt_base_t x, rt_base_t y)
{
    lcd_write_reg(WINDOW_H_START,  y);
    lcd_write_reg(WINDOW_H_END,    y);
    lcd_write_reg(WINDOW_V_START,  x);
    lcd_write_reg(WINDOW_V_END,    x);
    lcd_SetCursor(x,y);
    lcd_cmd(GRAM_WRITE);
    LCD_DATA = lcd_pixel_transform(*c);
}

注意这SPFD5420A打点函数与常见的控制器还不一样,还要设置窗口为此点.要不然会有些莫名奇妙的问题.(起码这边测试的是这样).

实现打点函数后便可以将printf定位到LCD了,新建Retarget.c文件,在里面实现fputc()函数

int fputc(int ch, FILE *f)
{
  int MaxX = 400 / 6;
  int MaxY = 240 / 12;
  int n;
  if(ch != '
' && ch != '
' && ch != '')
  {
        lcd_show_char(CurrentX*6, ((CurrentY + MaxY)%MaxY)*12, ch);
  }
  CurrentX++;
  return 1;
}

这里并没考虑更多情况,请各位按需要自行改进.

最后顺便在rtthread.h加上:

#define rt_kprintf printf

两个应该注意的地方.一是记得开背光,二是Options-Target-Use Micro LIB选项记得勾上,而且启动代码中Micro_Lib_Stack_Size宏大小最好相应改大.

如无意外printf,与rt_kprintf都已经定位到LCD了.

回帖(3)

qjqb

2022-4-21 15:19:34
也可以去实现下RTT的console,这样可以不用做rt_kprintf到printf的转换,然后使用printf

不过默认在RTT下还真不能够使用printf (Keil MDK),需要实现一些retarget.c中的函数。实现retarget.c还是非常有好处的,这样可以使用Keil MDK中提供的libc大部分功能,对于程序移植非常有帮助(就好比GNU GCC上实现newlib c库的移植)。
举报

万航渡路

2022-4-21 15:20:32
非常感谢楼主分享大量的研究结果和心得。有个地方很纠结,就是那个转换函数。

1. 不知道理解对不对,该函数是
将数据 D15D14D13D12 D11D10D9D8 D7D6D5D4 D3D2D1D0
转换成 0000 0000 D15D14D13D12 D11D10 0 0 D9 D8 0 D7 D6 D5 00 D4D3D2D1 D0 000
估计是RGB888的吧?排版很麻烦,

2.不理解为什么配置5420的寄存器(16位,丢掉D0和D9)要换成上述的格式。
根据5420的datasheet,转换方式应该是D15-D8左移2位,D7-D0左移1位。

3.根据RM970的原理图,D17,16是打乱顺序的,SDK的RK27换屏指南说,18位屏采用全部18根数据线时必须这样连接,纠结这个“必须”,更纠结用这种数据线连接顺序并采用原数据转换方式,配置lcd驱动寄存器又是怎样得到正确的结果的呢?

4.另外能请楼主推荐个单步调试方法,想用来验证一下数据的,不知楼主现在有没有空,能进一步弄出视频就完美了。

向楼主学习。
举报

百灵千岛酱

2022-4-21 15:20:48
这个没细究,因为我也是拿来主义,直接copy了Rockbox的代码,当然Rockbox rk2706部分也是来自SDK。关于所说的调试问题,是不可能有单步调试的了,或者说怎么可能有呢?一个rt_kprintf足以。开源固件的工程个人觉得已经很方便了,配置好了只要点击编译就秒下到播放器并且运行。关于这播放器的问题,也只能靠记忆了,因为与现在的工作一点也不相干,也会有一段时间(或者更长)与RT-Thread无缘。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分