本帖最后由 jinglixixi 于 2023-3-6 22:48 编辑
#申请原创#
在HC32F4A0开发板上,为了满足显示的需要配备有一个LCD屏接口,见图1所示。 由图中的注释可知,它是用来支持一款分辨率为800*480像素点的4.3寸屏,其驱动方式应该是以并行的方式,共有34个引脚。 无奈的是手中并没有与之相匹配的显示屏,只有一款34引脚的SPI接口LCD屏,其引脚排列如图2所示。 虽然在尺寸上略有不足,但终归是彩屏可以用来显示图像呀。 图1 LCD接口
图2 MDM_2802显示模组引脚排列
在利用LCD接口的情况下,经编程处理其使用效果如图3所示。
图3 使用效果 那它的驱动是怎样实现的呢? 其实本来应该很简单,那就是以GPIO口来模拟SPI的工作方式来驱动它。
但在具体实施时却发现一个问题,那就是该LCD接口所连接的引脚并非全是GPIO口,而是部分引脚经芯片扩展而获得,如引脚LCD_RST就属于这种类型,见图4所示。
图4 I2C扩展I/O口 也就是说,本来以模拟方式就可解决的问题现在又增添了I2C扩展I/O口的问题。 在查清引脚连接关系的情况下,MDM_2802显示模组与开发板的连接关系为: CS ----PG12 RS ----PG2 SCLK----PC0 RD ----PF11 RST ----TCA9539(LCD_RST) BKL ---- PI0 VDD --- 3.3V GND--- GND SDO ----PE3 SDI ----PF10/PE5(以J17进行切换)
为实现高低电平的模拟输出所作的定义为: #defineCLR_CLK() GPIO_ResetPins(GPIO_PORT_C, GPIO_PIN_00) #defineSET_CLK() GPIO_SetPins(GPIO_PORT_C,GPIO_PIN_00) //CLK
#defineCLR_SDA() GPIO_ResetPins(GPIO_PORT_E,GPIO_PIN_05) #defineSET_SDA() GPIO_SetPins(GPIO_PORT_E,GPIO_PIN_05) //DIN
#defineSET_REST() GPIO_SetPins(GPIO_PORT_E,GPIO_PIN_13) #defineCLR_REST() GPIO_ResetPins(GPIO_PORT_E,GPIO_PIN_13) //RES
#defineCLR_CS() GPIO_ResetPins(GPIO_PORT_G,GPIO_PIN_12) #defineSET_CS() GPIO_SetPins(GPIO_PORT_G,GPIO_PIN_12) //CS
对所用引脚的初始化配置函数为: void LCD_Init(void)
{
stc_gpio_init_t stcGpioInit;
(void)GPIO_StructInit(&stcGpioInit);
stcGpioInit.u16PinState = PIN_STAT_RST;
stcGpioInit.u16PinDir = PIN_DIR_OUT;
(void)GPIO_Init(GPIO_PORT_E, GPIO_PIN_13, &stcGpioInit);
(void)GPIO_Init(GPIO_PORT_F, GPIO_PIN_11, &stcGpioInit);
(void)GPIO_Init(GPIO_PORT_G, GPIO_PIN_12, &stcGpioInit);
(void)GPIO_Init(GPIO_PORT_G, GPIO_PIN_02, &stcGpioInit);
(void)GPIO_Init(GPIO_PORT_E, GPIO_PIN_05, &stcGpioInit);
(void)GPIO_Init(GPIO_PORT_C, GPIO_PIN_00, &stcGpioInit);
}
以I/O口模拟SPI方式字节数据的函数为: void send_byte(uint8_t data)
{
uint8_t count;
for(count = 0; count < 8; count++)
{
CLR_CLK();
if(data & 0x80)
{
SET_SDA();
}
else
{
CLR_SDA();
}
data <<= 1;
SET_CLK();
}
}
配置的写寄存器和数据的函数如下: void write_register(uint8_t cmd)
{
CLR_CS();
CLR_CLK();
CLR_SDA();
SET_CLK();
send_byte(cmd);
SET_CS();
}
void write_data(uint8_t data)
{
CLR_CS();
CLR_CLK();
SET_SDA();
SET_CLK();
send_byte(data);
SET_CS();
}
显示屏的初始化函数为: void ili9341_init(void)
{
write_register(0xCF);
write_data(0x00);
write_data(0xC1);
write_data(0X30);
write_register(0xED);
write_data(0x64);
write_data(0x03);
write_data(0X12);
write_data(0X81);
write_register(0xE8);
write_data(0x85);
write_data(0x10);
write_data(0x7A);
write_register(0xCB);
write_data(0x39);
write_data(0x2C);
write_data(0x00);
write_data(0x34);
write_data(0x02);
write_register(0xF7);
write_data(0x20);
write_register(0xEA);
write_data(0x00);
write_data(0x00);
write_register(0xC0);
write_data(0x1B);
write_register(0xC1);
write_data(0x01);
write_register(0xC5);
write_data(0x30);
write_data(0x30);
write_register(0xC7);
write_data(0XB7);
write_register(0x36);
write_data(0x08);
write_register(0x3A);
write_data(0x55);
write_register(0xB1);
write_data(0x00);
write_data(0x1A);
write_register(0xB6);
write_data(0x0A);
write_data(0xA2);
write_register(0xF2);
write_data(0x00);
write_register(0x26);
write_data(0x01);
write_register(0xE0);
write_data(0x0F);
write_data(0x2A);
write_data(0x28);
write_data(0x08);
write_data(0x0E);
write_data(0x08);
write_data(0x54);
write_data(0XA9);
write_data(0x43);
write_data(0x0A);
write_data(0x0F);
write_data(0x00);
write_data(0x00);
write_data(0x00);
write_data(0x00);
write_register(0XE1);
write_data(0x00);
write_data(0x15);
write_data(0x17);
write_data(0x07);
write_data(0x11);
write_data(0x06);
write_data(0x2B);
write_data(0x56);
write_data(0x3C);
write_data(0x05);
write_data(0x10);
write_data(0x0F);
write_data(0x3F);
write_data(0x3F);
write_data(0x0F);
write_register(0x2B);
write_data(0x00);
write_data(0x00);
write_data(0x01);
write_data(0x3f);
write_register(0x2A);
write_data(0x00);
write_data(0x00);
write_data(0x00);
write_data(0xef);
write_register(0x11);
DDL_DelayMS(10);
write_register(0x29);
}
实现色彩清屏处理的函数为: void ili9341_clear(int c)
{
uint32_t index=0;
set_cursor(0x00, 0x0000);
gram_prepare();
for(index = 0; index < (320*240); index++)
{
write_data(c >> 8);
write_data(c);
}
}
仿照清屏函数,实现一个50*50像素点的图标显示函数为: void showimage(void)
{
int i,j;
set_cursor(10,10);
gram_prepare();
for(j=0;j<50;j++)
{
set_cursor(10,10+j);
gram_prepare();
for(i=0;i<50;i++)
{
write_data(gImage_tb[(j*50+i)*2]);
write_data(gImage_tb[(j*50+i)*2+1]);
}
}
}
其中,语句set_cursor(10,10)是将绘制图标的位置设置为x=10,y=10的坐标处;函数write_data()的作用则是输出色彩的数据。 实现字符显示的函数为: void _GUI_DispChar(char c, int x, int y, const char *pdata, int font_xsize, int font_ysize, int fcolor, int bcolor)
{
uint8_t j,pos,t;
uint8_t temp;
uint8_t XNum;
uint32_t base;
XNum = (font_xsize/8) + 1;
if(font_ysize%8 == 0)
{
XNum--;
}
if(c < ' ')
{
return;
}
c = c - ' ';
base = (c*XNum*font_ysize);
for(j = 0; j < XNum; j++)
{
for(pos = 0; pos < font_ysize; pos++)
{
temp = (uint8_t)pdata[base + pos + j*font_ysize];
if(j < XNum)
{
for(t = 0; t < 8; t++)
{
if((temp>>t)&0x01)
{
ili9341_draw_pixel(fcolor, x+t, y+pos);
}
else
{
ili9341_draw_pixel(bcolor, x+t, y+pos);
}
}
}
else
{
for(t = 0; t < font_xsize%8; t++)
{
if((temp >> t) & 0x01)
{
ili9341_draw_pixel(fcolor, x+t, y+pos);
}
else
{
ili9341_draw_pixel(bcolor, x+t, y+pos);
}
}
}
}
x += 8;
}
}
实现汉字显示的函数为: void showhanzi16(unsigned int x,unsigned int y,unsigned char index, int fcolor, int bcolor)
{
unsigned char i,j,k;
unsigned char *temp=hanzi16;
temp+=index*32;
for(j=0;j<16;j++)
{
for(k=0;k<2;k++)
{
for(i=0;i<8;i++)
{
if((*temp&(1<<i))!=0)
{
ili9341_draw_pixel(fcolor, x+i+k*8, y+j);
}
else
{
ili9341_draw_pixel(bcolor, x+i+k*8, y+j);
}
}
temp++;
}
}
}
汉字显示函数所用的字模可由软件PCtoLCD2002来提取,其数据提取格式如图6所示。 图5 工具软件
图6 提取格式 字库在数组中的存放形式如下: unsigned char hanzi16[]=
{
0x80,0x20,0x82,0x20,0xF4,0x17,0x84,0x08,0x80,0x00,0xE1,0x23,0x02,0x20,0xEA,0x13,
0x28,0x0A,0x24,0x42,0xE7,0x43,0x24,0x22,0x44,0x21,0x04,0x17,0xF4,0x08,0x20,0x04,
... ...
0x80,0x00,0x84,0x78,0x88,0x48,0xE8,0x4B,0x81,0x48,0x82,0x78,0x82,0x48,0xE8,0x4B,
0x28,0x4A,0x24,0x7A,0x27,0x4A,0xE4,0x4B,0x24,0x4A,0x04,0x44,0x04,0x54,0x00,0x22,
}
实现图3所示效果的主程序为: int32_t main(void)
{
LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
BSP_CLK_Init();
BSP_IO_Init();
BSP_LED_Init();
BSP_LCD_IO_Init();
LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
GPIO_ResetPins(GPIO_PORT_G, GPIO_PIN_02);
GPIO_ResetPins(GPIO_PORT_F, GPIO_PIN_11);
BSP_LCD_BKLCmd(EIO_PIN_SET);
BSP_LCD_RSTCmd(EIO_PIN_SET);
LCD_Init();
ili9341_init();
ili9341_clear(RED);
BACK_COLOR=RED;
POINT_COLOR=YELLOW;
LCD_ShowString(70,17,"MDM-2802 & HC32F4A0");
LCD_ShowString(70,37,"MP3");
showhanzi16(100,37,32, YELLOW, RED);
showhanzi16(120,37,33, YELLOW, RED);
showhanzi16(140,37,34, YELLOW, RED);
showhanzi16(160,37,35, YELLOW, RED);
showhanzi16(180,37,36, YELLOW, RED);
LCD_ShowString(60,300,"BY: jinglixixi");
LCD_DrawLine(WHITE ,0, 10, 239, 10);
LCD_DrawLine(WHITE ,0, 60, 239, 60);
LCD_DrawLine(WHITE,0, 296, 239, 296);
showimage();
showhanzi16(30,80,37, YELLOW, RED);
showhanzi16(60,80,0, YELLOW, RED);
showhanzi16(80,80,1, YELLOW, RED);
showhanzi16(100,80,2, YELLOW, RED);
showhanzi16(60,110,3, YELLOW, RED);
showhanzi16(80,110,4, YELLOW, RED);
showhanzi16(100,110,5, YELLOW, RED);
showhanzi16(120,110,6, YELLOW, RED);
showhanzi16(60,140,7, YELLOW, RED);
showhanzi16(80,140,8, YELLOW, RED);
showhanzi16(60,170,9, YELLOW, RED);
showhanzi16(80,170,10, YELLOW, RED);
showhanzi16(60,200,11, YELLOW, RED);
showhanzi16(80,200,12, YELLOW, RED);
showhanzi16(100,200,13, YELLOW, RED);
showhanzi16(120,200,14, YELLOW, RED);
showhanzi16(60,230,15, YELLOW, RED);
showhanzi16(80,230,16, YELLOW, RED);
showhanzi16(100,230,17, YELLOW, RED);
showhanzi16(120,230,18, YELLOW, RED);
showhanzi16(140,230,19, YELLOW, RED);
showhanzi16(60,260,20, YELLOW, RED);
showhanzi16(80,260,21, YELLOW, RED);
showhanzi16(100,260,22, YELLOW, RED);
showhanzi16(120,260,23, YELLOW, RED);
showhanzi16(140,260,24, YELLOW, RED);
showhanzi16(160,260,25, YELLOW, RED);
showhanzi16(180,260,26, YELLOW, RED);
for (;;) {
BSP_LED_Toggle(LED_BLUE);
BSP_LED_Toggle(LED_YELLOW);
BSP_LED_Toggle(LED_RED);
DDL_DelayMS(500);
}
}
其中:语句GPIO_ResetPins(GPIO_PORT_G, GPIO_PIN_02)和GPIO_ResetPins(GPIO_PORT_F,GPIO_PIN_11)的作用是向引脚RS和RD提供低电平,而引脚BSP_LCD_BKLCmd(EIO_PIN_SET)和BSP_LCD_RSTCmd(EIO_PIN_SET)的作用则是向引脚 BKL和RST提供高电平。
至于扩展I/O的使用,是建立在函数BSP_LCD_IO_Init()的基础上,通过它实现了扩展I/O口的初始化,随后就可仿照GPIO的使用来控制高低电平的输出。
函数BSP_LCD_IO_Init()的内容如下: void BSP_LCD_IO_Init(void)
{
/* Init LCD backlight IO */
GPIO_OutputCmd(LCD_BKL_PORT, LCD_BKL_PIN, ENABLE);
/* Init LCD control IO before direction setting */
BSP_IO_WritePortPin(LCD_RST_PORT, LCD_RST_PIN, EIO_PIN_SET);
/* LCD panel control IO set to output */
BSP_IO_ConfigPortPin(LCD_RST_PORT, LCD_RST_PIN, EIO_DIR_OUT);
/* Init touch panel control IO before direction setting */
BSP_IO_WritePortPin(LCD_CTRST_PORT, LCD_CTRST_PIN, EIO_PIN_RESET);
BSP_IO_WritePortPin(LCD_CTINT_PORT, LCD_CTINT_PIN, EIO_PIN_RESET);
BSP_IO_ConfigPortPin(LCD_CTRST_PORT, LCD_CTRST_PIN, EIO_DIR_OUT);
BSP_IO_ConfigPortPin(LCD_CTINT_PORT, LCD_CTINT_PIN, EIO_DIR_OUT);
DDL_DelayMS(100UL);
BSP_IO_WritePortPin(LCD_CTINT_PORT, LCD_CTINT_PIN, EIO_PIN_SET);
DDL_DelayMS(100UL);
BSP_IO_WritePortPin(LCD_CTRST_PORT, LCD_CTRST_PIN, EIO_PIN_SET);
DDL_DelayMS(10UL);
BSP_IO_WritePortPin(LCD_CTINT_PORT, LCD_CTINT_PIN, EIO_PIN_RESET);
DDL_DelayMS(100UL);
BSP_IO_ConfigPortPin(LCD_CTRST_PORT, LCD_CTRST_PIN, EIO_DIR_IN);
BSP_IO_ConfigPortPin(LCD_CTINT_PORT, LCD_CTINT_PIN, EIO_DIR_IN);
}
以语句BSP_LCD_RSTCmd(EIO_PIN_SET)为例,其执行的I/O控制的操作为BSP_IO_WritePortPin(LCD_RST_PORT,LCD_RST_PIN, EIO_PIN_SET);。 这要我们就以I/O口模拟SPI的方式实现了MDM_2802显示模组的驱动,并用到了芯片TCA9539所扩展的I/O口。 这种使用方式,对于那些只惯于以一种方式来解决问题的人来说将是难于完成驱动目标的。
|