乐此不疲 发表于 2022-12-10 16:46:52

求助!!DAM_LCM例程改编驱动并口TFT无法实现请求指点

本帖最后由 乐此不疲 于 2022-12-10 21:25 编辑

最近一直在用STC32G屠龙刀的板子做驱动SSD1963-7寸TFT屏的测试,正常的硬件LCM接口应用程序已经完成调试,但DMA_LCM模式驱动却总是不能实现,现象为发送一包数据后就卡死了,请各位大神帮忙指点一下,先谢了!

部分例程如下:

/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.STCMCU.com --------------------------------------------*/
/* --- Web: www.STCMCUDATA.com---------------------------------------*/
/* --- QQ:800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/


/*************功能说明    **************

本例程基于STC32G为主控芯片的实验箱进行编写测试。

使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。

edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。

LCM接口+DMA驱动液晶屏程序

8bit I8080模式, P6口接D8~D15

sbit LCD_RS = P4^5;      //数据/命令切换
sbit LCD_WR = P4^2;      //写控制
sbit LCD_RD = P4^4;      //读控制
sbit LCD_CS = P3^4;      //片选
sbit LCD_RESET = P4^3;   //复位

LCM指令通过中断方式等待发送完成

DMA设置长度256字节,通过中断方式判断传输完成

下载时, 选择时钟 24MHz (用户可自行修改频率).

******************************************/

#include "..\..\comm\STC32G.h"
#include "stdio.h"
#include "intrins.h"
#include "font.h"

#define   MAIN_Fosc       24000000L   //定义主时钟

typedef   unsigned char   u8;
typedef   unsigned int    u16;
typedef   unsigned long   u32;

sbit LCD_RS = P4^5;      //数据/命令切换
sbit LCD_WR = P4^2;      //写控制
sbit LCD_RD = P4^4;      //读控制
sbit LCD_CS = P3^4;      //片选
sbit LCD_RESET = P4^3;   //复位

//IO连接
#defineLCD_DataPort P6   //8位数据口

//支持横竖屏快速定义切换
#define USE_HORIZONTAL            0   //定义液晶屏顺时针旋转方向         0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

//画笔颜色
#define WHITE                  0xFFFF
#define BLACK                  0x0000         
#define BLUE             0x001F
#define BRED             0XF81F
#define GRED             0XFFE0
#define GBLUE            0X07FF
#define RED                  0xF800
#define MAGENTA                0xF81F
#define GREEN                  0x07E0
#define CYAN                   0x7FFF
#define YELLOW               0xFFE0
#define BROWN            0XBC40 //棕色
#define BRRED            0XFC07 //棕红色
#define GRAY             0X8430 //灰色

#define DARKBLUE               0X01CF      //深蓝色
#define LIGHTBLUE               0X7D7C      //浅蓝色
#define GRAYBLUE                0X5458 //灰蓝色
#define LIGHTGREEN            0X841F //浅绿色
#define LGRAY            0XC618 //浅灰色(PANNEL),窗体背景色
#define LGRAYBLUE      0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE         0X2B12 //浅棕蓝色(选择条目的反色)

//定义LCD的尺寸
#define LCD_W 240
#define LCD_H 320

#define DMA_AMT_LEN2047//n+1

u16 POINT_COLOR=0x0000;      //画笔颜色

u16 index;
//u16 xdata Buffer={0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
u16 xdata Color;
bit DmaFlag;
bit LcmFlag;

typedef struct
{                                                                                    
      u16 width;                        //LCD 宽度
      u16 height;                        //LCD 高度
      u16 id;                              //LCD ID
      u8dir;                        //横屏还是竖屏控制:0,竖屏;1,横屏。      
      u8 wramcmd;                //开始写gram指令
      u8 rramcmd;   //开始读gram指令
      u8 setxcmd;                //设置x坐标指令
      u8 setycmd;                //设置y坐标指令         
}_lcd_dev;         

_lcd_dev lcddev;

void delay_ms(u16 ms);
void GPIO_Init(void);
void LCM_Config(void);
void DMA_Config(void);
void LCD_Init(void);
void Test_Color(void);
void LCD_WR_DATA_16Bit(u16 Data);
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd);
void Show_Str(u16 x, u16 y, u16 fc, u16 bc, u8 *str,u8 size,u8 mode);
void LCD_direction(u8 direction);

void main(void)
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    P0M1 = 0x30;   P0M0 = 0x30;   //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P1M1 = 0x32;   P1M0 = 0x32;   //设置P1.1、P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V), P1.1在PWM当DAC电路通过电阻串联到P2.3
    P2M1 = 0x3c;   P2M0 = 0x3c;   //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V),设置开漏模式需要断开PWM当DAC电路中的R2电阻
    P3M1 = 0x50;   P3M0 = 0x50;   //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
    P4M1 = 0x3c;   P4M0 = 0x3c;   //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P5M1 = 0x0c;   P5M0 = 0x0c;   //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
    P6M1 = 0xff;   P6M0 = 0xff;   //设置为漏极开路(实验箱加了上拉电阻到3.3V)
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

      GPIO_Init();
      LCM_Config();
      DMA_Config();
      EA = 1;
      
      LCD_Init(); //LCM初始化

      while(1)
      {
                Test_Color();
      }
}

void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{         
      u16 i,j;                        
      u16 width=ex-sx+1;               //得到填充的宽度
      u16 height=ey-sy+1;                //高度
      LCD_SetWindows(sx,sy,ex,ey);//设置显示窗口

      for(j=0,i=0;i<=DMA_AMT_LEN;i++)
      {
                Color = color;
      }
      index = 75;   //(320 * 240 * 2) / 2048 = 75
      LCD_CS=0;
      DMA_LCM_CR = 0xa0;      //Write dat
      while(!LCD_CS);
}

void Test_Color(void)
{
      u8 buf = {0};

      LCD_Fill(0,0,lcddev.width,lcddev.height,WHITE);
      Show_Str(20,30,BLUE,YELLOW,"LCM Test",16,1);delay_ms(800);
      LCD_Fill(0,0,lcddev.width,lcddev.height,RED);
      Show_Str(20,30,BLUE,YELLOW,"RED ",16,1);delay_ms(800);
      LCD_Fill(0,0,lcddev.width,lcddev.height,GREEN);
      Show_Str(20,30,BLUE,YELLOW,"GREEN ",16,1);delay_ms(800);
      LCD_Fill(0,0,lcddev.width,lcddev.height,BLUE);
      Show_Str(20,30,RED,YELLOW,"BLUE ",16,1);delay_ms(800);
}


/*****************************************************************************
* @name       :void LCM_Config(void)
* @date       :2018-11-13
* @function   :Config LCM
* @parameters :None
* @retvalue   :None
******************************************************************************/      
void LCM_Config(void)
{
      LCMIFCFG = 0x84;      //bit7 1:Enable Interrupt, bit1 0:8bit mode; bit0 0:8080,1:6800
      LCMIFCFG2 = 0x09;      //RS:P45,RD:P44,WR:P42; Setup Time=2,HOLD Time=1
      LCMIFSTA = 0x00;
}

/*****************************************************************************
* @name       :void DMA_Config(void)
* @date       :2020-12-09
* @function   :Config DMA
* @parameters :None
* @retvalue   :None
******************************************************************************/      
void DMA_Config(void)
{
      DMA_LCM_AMT = (u8)DMA_AMT_LEN;                              //设置传输总字节数(低8位):n+1
      DMA_LCM_AMTH = (u8)(DMA_AMT_LEN>>8);      //设置传输总字节数(高8位):n+1
      DMA_LCM_TXAH = (u8)((u16)&Color >> 8);
      DMA_LCM_TXAL = (u8)((u16)&Color);
//      DMA_LCM_RXAH = (u8)((u16)&Buffer >> 8);
//      DMA_LCM_RXAL = (u8)((u16)&Buffer);
      DMA_LCM_STA = 0x00;
      DMA_LCM_CFG = 0x82;
      DMA_LCM_CR = 0x00;
}

/*****************************************************************************
* @name       :void LCM_Interrupt(void)
* @date       :2018-11-13
* @function   :None
* @parameters :None
* @retvalue   :
******************************************************************************/
void LCMIF_DMA_Interrupt(void) interrupt 13
{
      if(LCMIFSTA & 0x01)
      {
                LCMIFSTA = 0x00;
                LcmFlag = 0;
      }
      
      if(DMA_LCM_STA & 0x01)
      {
                if(DmaFlag)
                {
                        DmaFlag = 0;
                        DMA_LCM_CR = 0;
                }
                else
                {
                        index--;
                        if(index == 0)
                        {
                              DMA_LCM_CR = 0;
                              LCD_CS=1;
                        }
                        else
                        {
                              DMA_LCM_CR = 0xa0;      //Write dat
                        }
                }
                DMA_LCM_STA = 0;
      }
}


问题现象为调用Test_Color()函数后就卡在了函数LCD_Fill(0,0,lcddev.width,lcddev.height,WHITE);这里了。
问题一:想知道上述现象的原因,卡死的问题怎样解决
问题二:我感觉这个函数有些问题,但作为玩51单片机的菜鸟第一次学习DMA的应用对例程的问题就无法理解了。

乘风飞扬 发表于 2022-12-12 16:23:59



由于LCM、DMA中断向量号超过31,直接使用的话编译会报错,所以例程是借用13号中断向量地址作为LCM与DMA中断入口,这就需要添加中断入口映射指令:

至于j, width, height变量原本是用来计算需要显示的点数,这里已经不需要,可以全部删掉。

神农鼎 发表于 2022-12-12 22:48:44

https://www.stcai.com/filedownload/609391

将 KEIL C251 支持的中断号 拓展到 0 ~ 255

乐此不疲 发表于 2022-12-13 10:52:13

多谢楼上二位朋友的指点,在安装keilC251时就根据STC手册提示通过软件做了拓展,而且在不使用DMA时硬件LCM驱动通过中断输出也是正常的并且已经也可以正常刷屏的,只是DMA传输不正常,显示现象为只传输了2048K(程序设定一包数据量)后就卡住了。我的代码是根据8bitLCM输出的代码改编的,我不知道STC的DMA传输数据宽度是否需要设定为16bit才能和LCM的16bit输出接口匹配,如果需要的话貌似STC的DMA无法设定数据宽度,刚刚接触DMA很多不懂。

神农鼎 发表于 2022-12-13 12:13:42

STC 的有 DMA 支持 TFT-i8080/M6800 接口的 MCU, 都可以设置 TFT-i8080/M6800 接口 是8位还是16位

神农鼎 发表于 2022-12-13 12:21:38






乐此不疲 发表于 2022-12-13 13:02:59

多谢临帖指点,或许我的测试存在的问题应该与DMA总线宽度无关,你说的寄存器配置是LCM与TFT之间的总线配置,我的代码不用DMA时已经能够驱动TFT了,这些寄存器配置也没问题的,我对DMA的理解如下图,很愿意和你继续探讨。






乘风飞扬 发表于 2022-12-13 20:37:58

DMA只是一个数据缓冲区,需要根据屏的分辨率计算出刷屏所需的数据量,然后除以DMA缓冲区设置长度,最好能够得到一个整数,这就是刷一整屏需要DMA搬运的次数。DMA每次完成传输后会停止传输并产生中断事件,判断搬运的次数如果没有刷完的话需要手动再启动下一次的DMA传输。
楼主传输了2048K(程序设定一包数据量)后就卡住了。其实不是卡住,而是DMA完成了一次传输后停止了,需要再次启动DMA才能继续下一次的传输。

qq603599910 发表于 2022-12-14 10:17:18

各位大佬,有STC32的ADC-DMA的范本吗?我是模拟电子专业对编程只懂皮毛,项目需要以前都是用范本移植.谢谢!

乐此不疲 发表于 2022-12-14 12:22:21

本帖最后由 乐此不疲 于 2022-12-14 12:28 编辑

乘风飞扬 发表于 2022-12-13 20:37
DMA只是一个数据缓冲区,需要根据屏的分辨率计算出刷屏所需的数据量,然后除以DMA缓冲区设置长度,最好能够 ...
执行函数中有启动传输的代码的。我目前只是在验证例程,所不同的是例程驱动的是8位并口TFT,而我是通过修改例程来驱动16位并口TFT,想必例程是应该能够实现8位并口屏的验证功能的(我没有8位屏所以无法验证),而我的改编也已经实现了硬件LCM16位接口的应用,只是不能实现DMA_LCM功能,从现象来看,DMA在初次启动时确实完成了2K数据的发送,屏幕上也显示了1024个色点(一个色点2个字节),例程详见1楼:

void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
{
    u16 i;
    LCD_SetWindows(sx,sy,ex-1,ey-1);//设置显示窗口
    for(i=0; i<=DMA_AMT_LEN; i++)
    {
      Color = color;
    }
    LCD_CS=0;
    DMA_LCM_CR = 0xa0;
    while(!LCD_CS);
}
/***************************************/
void LCMIF_DMA_Interrupt(void) interrupt 13
{
      if(LCMIFSTA & 0x01)
      {
                LCMIFSTA = 0x00;
                LcmFlag = 0;
      }
      if(DMA_LCM_STA & 0x01)
      {
      DmaFlag = 0;

      index--;
      if(index == 0)
      {
            DMA_LCM_CR = 0;
            LCD_CS=1;
      }
                DMA_LCM_STA = 0;
      }
}      
以下是我对例程的解读,如有问题诚请指正!
首先执行函数准备了一包数据 Color = color;而后使能目标设备 LCD_CS=0;而后 DMA_LCM_CR = 0xa0;使能DMA中断并触发DMA传送,此后执行函数停在while(!LCD_CS);处等待数据传输完成,当中断函数查询到DMA_LCM_STA的B0位为1时说明此包数据已经传输完毕,此后清除标志 DmaFlag = 0; 结束写数据函数的工作,index--递减发送计数次数, DMA_LCM_CR = 0;取消DMA中断, LCD_CS=1;打断阻塞恢复执行函数的运行,DMA_LCM_STA = 0;清除中断标志开始下一包数据的传输。
页: [1] 2
查看完整版本: 求助!!DAM_LCM例程改编驱动并口TFT无法实现请求指点