[N32G43x] 【N32G43XCL-STB_V1.0】+SPI DMA驱动LCD及疑问

[复制链接]
 楼主| qjp1988113 发表于 2022-1-21 10:37 | 显示全部楼层 |阅读模式
今天,我们来搞下SPI DMA来驱动240X240的ST7789的一个小LCD~LCD电路:
M1png.png

我们设定硬件连接引脚:

//              GND   电源地
//              VCC   3.3v电源
//              SCL   PA5(SCLK)
//              SDA   PA7(MOSI)
//              RES   PA1
//              DC    PA2
//              BLK   PA3

其中仅用到SPI1的SCLK 及MOSI ,即仅发送模式。
下面配置SPI:
  1. void SPI1_Init(void)
  2. {
  3.         GPIO_InitType GPIO_InitStructure;
  4.         SPI_InitType SPI_InitStructure;


  5.   RCC_ConfigPclk2(RCC_HCLK_DIV2);
  6.         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_SPI1, ENABLE);//使能SPI1
  7.         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA|RCC_APB2_PERIPH_AFIO, ENABLE);//使能GPIOA

  8.   
  9.   GPIO_InitStruct(&GPIO_InitStructure);
  10.   
  11.         GPIO_InitStructure.Pin =GPIO_PIN_5|GPIO_PIN_7;
  12.         GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
  13.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出
  14.   GPIO_InitStructure.GPIO_Alternate = GPIO_AF0_SPI1;
  15.         GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure); //初始化GPIOA
  16.         GPIO_SetBits(GPIOA,GPIO_PIN_5|GPIO_PIN_7);

  17.         SPI_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;//只发送模式
  18.         SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;//设置SPI工作模式:主机模式
  19.         SPI_InitStructure.DataLen       = SPI_DATA_SIZE_8BITS;//设置SPI数据大小:8位帧结构
  20.         SPI_InitStructure.CLKPOL        = SPI_CLKPOL_HIGH;//串行同步时钟空闲时SCLK位高电平
  21.         SPI_InitStructure.CLKPHA        = SPI_CLKPHA_SECOND_EDGE;//串行同步时钟空第二个时钟沿捕获
  22.         SPI_InitStructure.NSS           = SPI_NSS_SOFT;//NSS信号由软件管理
  23.         SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_2;//波特率预分频值:波特率预分频值为2
  24.         SPI_InitStructure.FirstBit      = SPI_FB_MSB;//数据传输高位先行
  25.         SPI_InitStructure.CRCPoly = 7;//CRC值计算的多项式
  26.         SPI_Init(SPI1,&SPI_InitStructure);//初始化SPI
  27.         SPI_Enable(SPI1, ENABLE);//使能SPI
  28. }
参看产品手册SPI1挂在APB2总线  最高54M,也是蛋疼的,这个设计~具体还没想明白,一旦波特率定义小了,SPI输出就不正常,这里用最大的2分频~
下面进行DMA配置,这了我们配置2种模式:
1、正常的数据地址递增,外设地址不变   这种模式发送大量数据刷屏
2、正常数据地址固定,外设地址固定      这种情况适合单一颜色刷屏
  1. DMA_InitType DMA_InitStructure;

  2. u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度          单次最大65535   
  3. //DMA1的各通道配置
  4. //这里的传输形式是固定的,这点要根据不同的情况来修改
  5. //从存储器->外设模式/8位数据宽度/存储器增量模式
  6. //DMA_CHx:DMA通道CHx
  7. //cpar:外设地址
  8. //cmar:存储器地址
  9. //cndtr:数据传输量
  10. void MYDMA_Config(DMA_ChannelType* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
  11. {
  12.         RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE);        //使能DMA传输
  13.        
  14.   DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
  15.         DMA1_MEM_LEN=cndtr;
  16.         DMA_InitStructure.PeriphAddr      = cpar;  //DMA外设ADC基地址
  17.         DMA_InitStructure.MemAddr         = cmar;  //DMA内存基地址
  18.         DMA_InitStructure.Direction       = DMA_DIR_PERIPH_DST;  //数据传输方向,从内存读取发送到外设
  19.         DMA_InitStructure.BufSize         = cndtr;  //DMA通道的DMA缓存的大小
  20.         DMA_InitStructure.PeriphInc       = DMA_PERIPH_INC_DISABLE;  //外设地址寄存器不变
  21.         DMA_InitStructure.DMA_MemoryInc   = DMA_MEM_INC_ENABLE;  //内存地址寄存器递增
  22.         DMA_InitStructure.PeriphDataSize  = DMA_PERIPH_DATA_SIZE_BYTE;  //数据宽度为8位
  23.         DMA_InitStructure.MemDataSize     = DMA_MemoryDataSize_Byte; //数据宽度为8位
  24.         DMA_InitStructure.CircularMode    = DMA_MODE_NORMAL;  //工作在正常缓存模式
  25.         DMA_InitStructure.Priority        = DMA_PRIORITY_MEDIUM; //DMA通道 x拥有中优先级
  26.         DMA_InitStructure.Mem2Mem         = DMA_M2M_DISABLE;  //DMA通道x没有设置为内存到内存传输
  27.         DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
  28.                  
  29. }

  30. void MYDMA_Config1(DMA_ChannelType* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
  31. {
  32.         RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA, ENABLE);        //使能DMA传输
  33.        
  34.   DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值
  35.         DMA1_MEM_LEN=cndtr;
  36.         DMA_InitStructure.PeriphAddr      = cpar;  //DMA外设ADC基地址
  37.         DMA_InitStructure.MemAddr         = cmar;  //DMA内存基地址
  38.         DMA_InitStructure.Direction       = DMA_DIR_PERIPH_DST;  //数据传输方向,从内存读取发送到外设
  39.         DMA_InitStructure.BufSize         = cndtr;  //DMA通道的DMA缓存的大小
  40.         DMA_InitStructure.PeriphInc       = DMA_PERIPH_INC_DISABLE;  //外设地址寄存器不变
  41.         DMA_InitStructure.DMA_MemoryInc   = DMA_MEM_INC_DISABLE;  //内存地址寄存器不变
  42.         DMA_InitStructure.PeriphDataSize  = DMA_PERIPH_DATA_SIZE_HALFWORD;  //数据宽度为16位
  43.         DMA_InitStructure.MemDataSize     = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
  44.         DMA_InitStructure.CircularMode    = DMA_MODE_NORMAL;  //工作在正常缓存模式
  45.         DMA_InitStructure.Priority        = DMA_PRIORITY_MEDIUM; //DMA通道 x拥有中优先级
  46.         DMA_InitStructure.Mem2Mem         = DMA_M2M_DISABLE;  //DMA通道x没有设置为内存到内存传输
  47.         DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
  48. }

  49. //开启一次DMA传输
  50. void MYDMA_Enable(DMA_ChannelType*DMA_CHx)
  51. {
  52.         DMA_EnableChannel(DMA_CHx, DISABLE );
  53.   /*从新设置发送数量*/
  54.         DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);
  55.   /* Enable DMA ChannelX */
  56.   DMA_RequestRemap(DMA_REMAP_SPI1_TX,DMA, DMA_CHx, ENABLE);
  57.         DMA_EnableChannel(DMA_CHx, ENABLE);
  58. }       
下面我们就对屏幕进行操作了:
屏幕基本操作相关:
  1. #include "lcd_init.h"
  2. #include "delay.h"
  3. #include "spi.h"

  4. void LCD_GPIO_Init(void)
  5. {
  6.         GPIO_InitType  GPIO_InitStructure;
  7.         RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);         //使能A端口时钟
  8.   
  9.   GPIO_InitStruct(&GPIO_InitStructure);
  10.   
  11.         GPIO_InitStructure.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;         
  12.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                  //推挽输出
  13.         GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
  14.         GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);          //初始化GPIOA
  15.         GPIO_SetBits(GPIOA,GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
  16. }

  17. void delay(int t)
  18. {
  19.         while(t--);
  20. }
  21. /******************************************************************************
  22.       函数说明:LCD串行数据写入函数
  23.       入口数据:dat  要写入的串行数据
  24.       返回值:  无
  25. ******************************************************************************/
  26. void LCD_Writ_Bus(u8 dat)
  27. {       
  28.   while(SPI_I2S_GetStatus(SPI1, SPI_I2S_TE_FLAG) == RESET);//检查接收标志位
  29.         SPI_I2S_TransmitData(SPI1,dat);
  30.         delay(1);
  31. }

  32. /******************************************************************************
  33.       函数说明:LCD写入数据
  34.       入口数据:dat 写入的数据
  35.       返回值:  无
  36. ******************************************************************************/
  37. void LCD_WR_DATA8(u8 dat)
  38. {
  39.         LCD_Writ_Bus(dat);
  40. }


  41. /******************************************************************************
  42.       函数说明:LCD写入数据
  43.       入口数据:dat 写入的数据
  44.       返回值:  无
  45. ******************************************************************************/
  46. void LCD_WR_DATA(u16 dat)
  47. {
  48.         LCD_Writ_Bus(dat>>8);
  49.         LCD_Writ_Bus(dat);
  50. }


  51. /******************************************************************************
  52.       函数说明:LCD写入命令
  53.       入口数据:dat 写入的命令
  54.       返回值:  无
  55. ******************************************************************************/
  56. void LCD_WR_REG(u8 dat)
  57. {
  58.         LCD_DC_Clr();//写命令
  59.         LCD_Writ_Bus(dat);
  60.         LCD_DC_Set();//写数据
  61. }


  62. /******************************************************************************
  63.       函数说明:设置起始和结束地址
  64.       入口数据:x1,x2 设置列的起始和结束地址
  65.                 y1,y2 设置行的起始和结束地址
  66.       返回值:  无
  67. ******************************************************************************/
  68. void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2)
  69. {
  70.         if(USE_HORIZONTAL==0)
  71.         {
  72.                 LCD_WR_REG(0x2a);//列地址设置
  73.                 LCD_WR_DATA(x1);
  74.                 LCD_WR_DATA(x2);
  75.                 LCD_WR_REG(0x2b);//行地址设置
  76.                 LCD_WR_DATA(y1);
  77.                 LCD_WR_DATA(y2);
  78.                 LCD_WR_REG(0x2c);//储存器写
  79.         }
  80.         else if(USE_HORIZONTAL==1)
  81.         {
  82.                 LCD_WR_REG(0x2a);//列地址设置
  83.                 LCD_WR_DATA(x1);
  84.                 LCD_WR_DATA(x2);
  85.                 LCD_WR_REG(0x2b);//行地址设置
  86.                 LCD_WR_DATA(y1+80);
  87.                 LCD_WR_DATA(y2+80);
  88.                 LCD_WR_REG(0x2c);//储存器写
  89.         }
  90.         else if(USE_HORIZONTAL==2)
  91.         {
  92.                 LCD_WR_REG(0x2a);//列地址设置
  93.                 LCD_WR_DATA(x1);
  94.                 LCD_WR_DATA(x2);
  95.                 LCD_WR_REG(0x2b);//行地址设置
  96.                 LCD_WR_DATA(y1);
  97.                 LCD_WR_DATA(y2);
  98.                 LCD_WR_REG(0x2c);//储存器写
  99.         }
  100.         else
  101.         {
  102.                 LCD_WR_REG(0x2a);//列地址设置
  103.                 LCD_WR_DATA(x1+80);
  104.                 LCD_WR_DATA(x2+80);
  105.                 LCD_WR_REG(0x2b);//行地址设置
  106.                 LCD_WR_DATA(y1);
  107.                 LCD_WR_DATA(y2);
  108.                 LCD_WR_REG(0x2c);//储存器写
  109.         }
  110. }

  111. void LCD_Init(void)
  112. {
  113.         SPI1_Init();
  114.         LCD_GPIO_Init();//初始化GPIO
  115.        
  116.         LCD_RES_Clr();//复位
  117.         delay_ms(100);
  118.         LCD_RES_Set();
  119.         delay_ms(100);
  120.        
  121.         LCD_BLK_Set();//打开背光
  122.   delay_ms(100);
  123.        
  124.         //************* Start Initial Sequence **********//
  125.         LCD_WR_REG(0x11); //Sleep out
  126.         delay_ms(120);              //Delay 120ms
  127.         //************* Start Initial Sequence **********//
  128.         LCD_WR_REG(0x36);
  129.         if(USE_HORIZONTAL==0)LCD_WR_DATA8(0x00);
  130.         else if(USE_HORIZONTAL==1)LCD_WR_DATA8(0xC0);
  131.         else if(USE_HORIZONTAL==2)LCD_WR_DATA8(0x70);
  132.         else LCD_WR_DATA8(0xA0);

  133.         LCD_WR_REG(0x3A);
  134.         LCD_WR_DATA8(0x05);

  135.         LCD_WR_REG(0xB2);
  136.         LCD_WR_DATA8(0x0C);
  137.         LCD_WR_DATA8(0x0C);
  138.         LCD_WR_DATA8(0x00);
  139.         LCD_WR_DATA8(0x33);
  140.         LCD_WR_DATA8(0x33);

  141.         LCD_WR_REG(0xB7);
  142.         LCD_WR_DATA8(0x35);  

  143.         LCD_WR_REG(0xBB);
  144.         LCD_WR_DATA8(0x19);

  145.         LCD_WR_REG(0xC0);
  146.         LCD_WR_DATA8(0x2C);

  147.         LCD_WR_REG(0xC2);
  148.         LCD_WR_DATA8(0x01);

  149.         LCD_WR_REG(0xC3);
  150.         LCD_WR_DATA8(0x12);   

  151.         LCD_WR_REG(0xC4);
  152.         LCD_WR_DATA8(0x20);  

  153.         LCD_WR_REG(0xC6);
  154.         LCD_WR_DATA8(0x0F);   

  155.         LCD_WR_REG(0xD0);
  156.         LCD_WR_DATA8(0xA4);
  157.         LCD_WR_DATA8(0xA1);

  158.         LCD_WR_REG(0xE0);
  159.         LCD_WR_DATA8(0xD0);
  160.         LCD_WR_DATA8(0x04);
  161.         LCD_WR_DATA8(0x0D);
  162.         LCD_WR_DATA8(0x11);
  163.         LCD_WR_DATA8(0x13);
  164.         LCD_WR_DATA8(0x2B);
  165.         LCD_WR_DATA8(0x3F);
  166.         LCD_WR_DATA8(0x54);
  167.         LCD_WR_DATA8(0x4C);
  168.         LCD_WR_DATA8(0x18);
  169.         LCD_WR_DATA8(0x0D);
  170.         LCD_WR_DATA8(0x0B);
  171.         LCD_WR_DATA8(0x1F);
  172.         LCD_WR_DATA8(0x23);

  173.         LCD_WR_REG(0xE1);
  174.         LCD_WR_DATA8(0xD0);
  175.         LCD_WR_DATA8(0x04);
  176.         LCD_WR_DATA8(0x0C);
  177.         LCD_WR_DATA8(0x11);
  178.         LCD_WR_DATA8(0x13);
  179.         LCD_WR_DATA8(0x2C);
  180.         LCD_WR_DATA8(0x3F);
  181.         LCD_WR_DATA8(0x44);
  182.         LCD_WR_DATA8(0x51);
  183.         LCD_WR_DATA8(0x2F);
  184.         LCD_WR_DATA8(0x1F);
  185.         LCD_WR_DATA8(0x1F);
  186.         LCD_WR_DATA8(0x20);
  187.         LCD_WR_DATA8(0x23);
  188.         LCD_WR_REG(0x21);

  189.         LCD_WR_REG(0x29);
  190. }
屏幕显示字符图片等相关,这里用到DMA发送大量数据:
  1. #include "lcd.h"
  2. #include "lcd_init.h"
  3. #include "lcdfont.h"
  4. #include "delay.h"
  5. #include "spi.h"
  6. #include "dma.h"

  7. /******************************************************************************
  8.       函数说明:在指定区域填充颜色
  9.       入口数据:xsta,ysta   起始坐标
  10.                 xend,yend   终止坐标
  11.                                                                 color       要填充的颜色
  12.       返回值:  无
  13. ******************************************************************************/
  14. void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
  15. {         
  16.         u16 color1[1],t=1;
  17.         u32 num,num1;
  18.         color1[0]=color;
  19.         num=(xend-xsta)*(yend-ysta);
  20.         LCD_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围
  21.         //SPI1->CTRL1|=1<<11;//设置SPI16位传输模式
  22.   SPI_Enable(SPI1, DISABLE);//使能SPI
  23.   SPI_ConfigDataLen(SPI1, SPI_DATA_SIZE_16BITS);
  24.         SPI_Enable(SPI1, ENABLE);//使能SPI
  25.         while(t)
  26.         {
  27.                 if(num>65534)
  28.                 {
  29.                         num-=65534;
  30.                         num1=65534;
  31.                 }
  32.                 else
  33.                 {
  34.                         t=0;
  35.                         num1=num;
  36.                 }
  37.                 MYDMA_Config1(DMA_CH3,(u32)&SPI1->DAT,(u32)color1,num1);
  38.     //MYDMA_Config1(DMA_CH3,(u32)SPI_MASTER_DR_Base,(u32)color1,num1);
  39.                 SPI_I2S_EnableDma(SPI1,SPI_I2S_DMA_TX,ENABLE);
  40.     //SPI_Enable(SPI1, ENABLE);
  41.                 MYDMA_Enable(DMA_CH3);
  42.                 while(1)
  43.                 {
  44.                         if(DMA_GetFlagStatus(DMA_FLAG_TC3,DMA)!=RESET)//等待通道4传输完成
  45.                         {
  46.                                 DMA_ClearFlag(DMA_FLAG_TC3,DMA);//清除通道3传输完成标志
  47.                                 break;
  48.                         }
  49.                 }
  50.   }

  51. //        while(1)
  52. //        {
  53. ////DMA单次最大65535
  54. //    num1=240*240;//57600
  55. //                MYDMA_Config1(DMA_CH3,(u32)&SPI1->DAT,(u32)color1,num1);
  56. //    //MYDMA_Config1(DMA_CH3,(u32)SPI_MASTER_DR_Base,(u32)color1,num1);
  57. //                SPI_I2S_EnableDma(SPI1,SPI_I2S_DMA_TX,ENABLE);
  58. //    //SPI_Enable(SPI1, ENABLE);
  59. //                MYDMA_Enable(DMA_CH3);
  60. //                while(1)
  61. //                {
  62. //                        if(DMA_GetFlagStatus(DMA_FLAG_TC3,DMA)!=RESET)//等待通道3传输完成
  63. //                        {
  64. //                                DMA_ClearFlag(DMA_FLAG_TC3,DMA);//清除通道3传输完成标志
  65. //                                break;
  66. //                        }
  67. //                }
  68. //  }
  69. //        SPI1->CTRL1=~SPI1->CTRL1;
  70. //        SPI1->CTRL1|=1<<11;
  71. //        SPI1->CTRL1=~SPI1->CTRL1;//设置SPI8位传输模式
  72. //  SPI1->CTRL1&=~(1<<11);
  73.   SPI_Enable(SPI1, DISABLE);//使能SPI
  74.   SPI_ConfigDataLen(SPI1, SPI_DATA_SIZE_8BITS);
  75.         SPI_Enable(SPI1, ENABLE);//使能SPI
  76. }

  77. /******************************************************************************
  78.       函数说明:在指定位置画点
  79.       入口数据:x,y 画点坐标
  80.                 color 点的颜色
  81.       返回值:  无
  82. ******************************************************************************/
  83. void LCD_DrawPoint(u16 x,u16 y,u16 color)
  84. {
  85.         LCD_Address_Set(x,y,x,y);//设置光标位置
  86.         LCD_WR_DATA(color);
  87. }


  88. /******************************************************************************
  89.       函数说明:画线
  90.       入口数据:x1,y1   起始坐标
  91.                 x2,y2   终止坐标
  92.                 color   线的颜色
  93.       返回值:  无
  94. ******************************************************************************/
  95. void LCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color)
  96. {
  97.         u16 t;
  98.         int xerr=0,yerr=0,delta_x,delta_y,distance;
  99.         int incx,incy,uRow,uCol;
  100.         delta_x=x2-x1; //计算坐标增量
  101.         delta_y=y2-y1;
  102.         uRow=x1;//画线起点坐标
  103.         uCol=y1;
  104.         if(delta_x>0)incx=1; //设置单步方向
  105.         else if (delta_x==0)incx=0;//垂直线
  106.         else {incx=-1;delta_x=-delta_x;}
  107.         if(delta_y>0)incy=1;
  108.         else if (delta_y==0)incy=0;//水平线
  109.         else {incy=-1;delta_y=-delta_x;}
  110.         if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
  111.         else distance=delta_y;
  112.         for(t=0;t<distance+1;t++)
  113.         {
  114.                 LCD_DrawPoint(uRow,uCol,color);//画点
  115.                 xerr+=delta_x;
  116.                 yerr+=delta_y;
  117.                 if(xerr>distance)
  118.                 {
  119.                         xerr-=distance;
  120.                         uRow+=incx;
  121.                 }
  122.                 if(yerr>distance)
  123.                 {
  124.                         yerr-=distance;
  125.                         uCol+=incy;
  126.                 }
  127.         }
  128. }


  129. /******************************************************************************
  130.       函数说明:画矩形
  131.       入口数据:x1,y1   起始坐标
  132.                 x2,y2   终止坐标
  133.                 color   矩形的颜色
  134.       返回值:  无
  135. ******************************************************************************/
  136. void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color)
  137. {
  138.         LCD_DrawLine(x1,y1,x2,y1,color);
  139.         LCD_DrawLine(x1,y1,x1,y2,color);
  140.         LCD_DrawLine(x1,y2,x2,y2,color);
  141.         LCD_DrawLine(x2,y1,x2,y2,color);
  142. }


  143. /******************************************************************************
  144.       函数说明:画圆
  145.       入口数据:x0,y0   圆心坐标
  146.                 r       半径
  147.                 color   圆的颜色
  148.       返回值:  无
  149. ******************************************************************************/
  150. void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color)
  151. {
  152.         int a,b;
  153.         a=0;b=r;          
  154.         while(a<=b)
  155.         {
  156.                 LCD_DrawPoint(x0-b,y0-a,color);             //3           
  157.                 LCD_DrawPoint(x0+b,y0-a,color);             //0           
  158.                 LCD_DrawPoint(x0-a,y0+b,color);             //1               
  159.                 LCD_DrawPoint(x0-a,y0-b,color);             //2            
  160.                 LCD_DrawPoint(x0+b,y0+a,color);             //4               
  161.                 LCD_DrawPoint(x0+a,y0-b,color);             //5
  162.                 LCD_DrawPoint(x0+a,y0+b,color);             //6
  163.                 LCD_DrawPoint(x0-b,y0+a,color);             //7
  164.                 a++;
  165.                 if((a*a+b*b)>(r*r))//判断要画的点是否过远
  166.                 {
  167.                         b--;
  168.                 }
  169.         }
  170. }

  171. /******************************************************************************
  172.       函数说明:显示汉字串
  173.       入口数据:x,y显示坐标
  174.                 *s 要显示的汉字串
  175.                 fc 字的颜色
  176.                 bc 字的背景色
  177.                 sizey 字号 可选 16 24 32
  178.                 mode:  0非叠加模式  1叠加模式
  179.       返回值:  无
  180. ******************************************************************************/
  181. void LCD_ShowChinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
  182. {
  183.         while(*s!=0)
  184.         {
  185.                 if(sizey==16) LCD_ShowChinese16x16(x,y,s,fc,bc,sizey,mode);
  186.                 else if(sizey==24) LCD_ShowChinese24x24(x,y,s,fc,bc,sizey,mode);
  187.                 else if(sizey==32) LCD_ShowChinese32x32(x,y,s,fc,bc,sizey,mode);
  188.                 else return;
  189.                 s+=2;
  190.                 x+=sizey;
  191.         }
  192. }

  193. /******************************************************************************
  194.       函数说明:显示单个16x16汉字
  195.       入口数据:x,y显示坐标
  196.                 *s 要显示的汉字
  197.                 fc 字的颜色
  198.                 bc 字的背景色
  199.                 sizey 字号
  200.                 mode:  0非叠加模式  1叠加模式
  201.       返回值:  无
  202. ******************************************************************************/
  203. void LCD_ShowChinese16x16(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
  204. {
  205.         u8 i,j;
  206.         u16 k;
  207.         u16 HZnum;//汉字数目
  208.         u16 TypefaceNum;//一个字符所占字节大小
  209.         u16 x0=x;
  210.         TypefaceNum=sizey/8*sizey;//此算法只适用于字宽等于字高,且字高是8的倍数的字,
  211.                                   //也建议用户使用这样大小的字,否则显示容易出问题!
  212.         HZnum=sizeof(tfont16)/sizeof(typFNT_GB16);        //统计汉字数目
  213.         for(k=0;k<HZnum;k++)
  214.         {
  215.                 if ((tfont16[k].Index[0]==*(s))&&(tfont16[k].Index[1]==*(s+1)))
  216.                 {        
  217.                         LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
  218.                         for(i=0;i<TypefaceNum;i++)
  219.                         {
  220.                                 for(j=0;j<8;j++)
  221.                                 {       
  222.                                         if(!mode)//非叠加方式
  223.                                         {
  224.                                                 if(tfont16[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
  225.                                                 else LCD_WR_DATA(bc);
  226.                                         }
  227.                                         else//叠加方式
  228.                                         {
  229.                                                 if(tfont16[k].Msk[i]&(0x01<<j))        LCD_DrawPoint(x,y,fc);//画一个点
  230.                                                 x++;
  231.                                                 if((x-x0)==sizey)
  232.                                                 {
  233.                                                         x=x0;
  234.                                                         y++;
  235.                                                         break;
  236.                                                 }
  237.                                         }
  238.                                 }
  239.                         }
  240.                 }                                         
  241.                 continue;  //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
  242.         }
  243. }


  244. /******************************************************************************
  245.       函数说明:显示单个24x24汉字
  246.       入口数据:x,y显示坐标
  247.                 *s 要显示的汉字
  248.                 fc 字的颜色
  249.                 bc 字的背景色
  250.                 sizey 字号
  251.                 mode:  0非叠加模式  1叠加模式
  252.       返回值:  无
  253. ******************************************************************************/
  254. void LCD_ShowChinese24x24(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
  255. {
  256.         u8 i,j;
  257.         u16 k;
  258.         u16 HZnum;//汉字数目
  259.         u16 TypefaceNum;//一个字符所占字节大小
  260.         u16 x0=x;
  261.         TypefaceNum=sizey/8*sizey;//此算法只适用于字宽等于字高,且字高是8的倍数的字,
  262.                                   //也建议用户使用这样大小的字,否则显示容易出问题!
  263.         HZnum=sizeof(tfont24)/sizeof(typFNT_GB24);        //统计汉字数目
  264.         for(k=0;k<HZnum;k++)
  265.         {
  266.                 if ((tfont24[k].Index[0]==*(s))&&(tfont24[k].Index[1]==*(s+1)))
  267.                 {        
  268.                         LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
  269.                         for(i=0;i<TypefaceNum;i++)
  270.                         {
  271.                                 for(j=0;j<8;j++)
  272.                                 {       
  273.                                         if(!mode)//非叠加方式
  274.                                         {
  275.                                                 if(tfont24[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
  276.                                                 else LCD_WR_DATA(bc);
  277.                                         }
  278.                                         else//叠加方式
  279.                                         {
  280.                                                 if(tfont24[k].Msk[i]&(0x01<<j))        LCD_DrawPoint(x,y,fc);//画一个点
  281.                                                 x++;
  282.                                                 if((x-x0)==sizey)
  283.                                                 {
  284.                                                         x=x0;
  285.                                                         y++;
  286.                                                         break;
  287.                                                 }
  288.                                         }
  289.                                 }
  290.                         }
  291.                 }                                         
  292.                 continue;  //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
  293.         }
  294. }

  295. /******************************************************************************
  296.       函数说明:显示单个32x32汉字
  297.       入口数据:x,y显示坐标
  298.                 *s 要显示的汉字
  299.                 fc 字的颜色
  300.                 bc 字的背景色
  301.                 sizey 字号
  302.                 mode:  0非叠加模式  1叠加模式
  303.       返回值:  无
  304. ******************************************************************************/
  305. void LCD_ShowChinese32x32(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode)
  306. {
  307.         u8 i,j;
  308.         u16 k;
  309.         u16 HZnum;//汉字数目
  310.         u16 TypefaceNum;//一个字符所占字节大小
  311.         u16 x0=x;
  312.         TypefaceNum=sizey/8*sizey;//此算法只适用于字宽等于字高,且字高是8的倍数的字,
  313.                                   //也建议用户使用这样大小的字,否则显示容易出问题!
  314.         HZnum=sizeof(tfont32)/sizeof(typFNT_GB32);        //统计汉字数目
  315.         for(k=0;k<HZnum;k++)
  316.         {
  317.                 if ((tfont32[k].Index[0]==*(s))&&(tfont32[k].Index[1]==*(s+1)))
  318.                 {        
  319.                         LCD_Address_Set(x,y,x+sizey-1,y+sizey-1);
  320.                         for(i=0;i<TypefaceNum;i++)
  321.                         {
  322.                                 for(j=0;j<8;j++)
  323.                                 {       
  324.                                         if(!mode)//非叠加方式
  325.                                         {
  326.                                                 if(tfont32[k].Msk[i]&(0x01<<j))LCD_WR_DATA(fc);
  327.                                                 else LCD_WR_DATA(bc);
  328.                                         }
  329.                                         else//叠加方式
  330.                                         {
  331.                                                 if(tfont32[k].Msk[i]&(0x01<<j))        LCD_DrawPoint(x,y,fc);//画一个点
  332.                                                 x++;
  333.                                                 if((x-x0)==sizey)
  334.                                                 {
  335.                                                         x=x0;
  336.                                                         y++;
  337.                                                         break;
  338.                                                 }
  339.                                         }
  340.                                 }
  341.                         }
  342.                 }                                         
  343.                 continue;  //查找到对应点阵字库立即退出,防止多个汉字重复取模带来影响
  344.         }
  345. }


  346. /******************************************************************************
  347.       函数说明:显示单个字符
  348.       入口数据:x,y显示坐标
  349.                 num 要显示的字符
  350.                 fc 字的颜色
  351.                 bc 字的背景色
  352.                 sizey 字号
  353.                 mode:  0非叠加模式  1叠加模式
  354.       返回值:  无
  355. ******************************************************************************/
  356. void LCD_ShowChar(u16 x,u16 y,u8 num,u16 fc,u16 bc,u8 sizey,u8 mode)
  357. {
  358.         u8 temp,sizex,t;
  359.         u16 i,TypefaceNum;//一个字符所占字节大小
  360.         u16 x0=x;
  361.         sizex=sizey/2;
  362.         TypefaceNum=sizex/8*sizey;
  363.         num=num-' ';    //得到偏移后的值
  364.         LCD_Address_Set(x,y,x+sizex-1,y+sizey-1);  //设置光标位置
  365.         for(i=0;i<TypefaceNum;i++)
  366.         {
  367.                 if(sizey==16)temp=ascii_1608[num][i];                       //调用8x16字体
  368.                 else if(sizey==32)temp=ascii_3216[num][i];                 //调用16x32字体
  369.                 else return;
  370.                 for(t=0;t<8;t++)
  371.                 {
  372.                         if(!mode)//非叠加模式
  373.                         {
  374.                                 if(temp&(0x01<<t))LCD_WR_DATA(fc);
  375.                                 else LCD_WR_DATA(bc);
  376.                         }
  377.                         else//叠加模式
  378.                         {
  379.                                 if(temp&(0x01<<t))LCD_DrawPoint(x,y,fc);//画一个点
  380.                                 x++;
  381.                                 if((x-x0)==sizex)
  382.                                 {
  383.                                         x=x0;
  384.                                         y++;
  385.                                         break;
  386.                                 }
  387.                         }
  388.                 }
  389.         }                      
  390. }


  391. /******************************************************************************
  392.       函数说明:显示字符串
  393.       入口数据:x,y显示坐标
  394.                 *p 要显示的字符串
  395.                 fc 字的颜色
  396.                 bc 字的背景色
  397.                 sizey 字号
  398.                 mode:  0非叠加模式  1叠加模式
  399.       返回值:  无
  400. ******************************************************************************/
  401. void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 fc,u16 bc,u8 sizey,u8 mode)
  402. {         
  403.         while(*p!='\0')
  404.         {      
  405.                 LCD_ShowChar(x,y,*p,fc,bc,sizey,mode);
  406.                 x+=sizey/2;
  407.                 p++;
  408.         }  
  409. }


  410. /******************************************************************************
  411.       函数说明:显示数字
  412.       入口数据:m底数,n指数
  413.       返回值:  无
  414. ******************************************************************************/
  415. u32 mypow(u8 m,u8 n)
  416. {
  417.         u32 result=1;         
  418.         while(n--)result*=m;
  419.         return result;
  420. }


  421. /******************************************************************************
  422.       函数说明:显示整数变量
  423.       入口数据:x,y显示坐标
  424.                 num 要显示整数变量
  425.                 len 要显示的位数
  426.                 fc 字的颜色
  427.                 bc 字的背景色
  428.                 sizey 字号
  429.       返回值:  无
  430. ******************************************************************************/
  431. void LCD_ShowIntNum(u16 x,u16 y,u16 num,u8 len,u16 fc,u16 bc,u8 sizey)
  432. {                
  433.         u8 t,temp;
  434.         u8 enshow=0;
  435.         u8 sizex=sizey/2;
  436.         for(t=0;t<len;t++)
  437.         {
  438.                 temp=(num/mypow(10,len-t-1))%10;
  439.                 if(enshow==0&&t<(len-1))
  440.                 {
  441.                         if(temp==0)
  442.                         {
  443.                                 LCD_ShowChar(x+t*sizex,y,' ',fc,bc,sizey,0);
  444.                                 continue;
  445.                         }else enshow=1;
  446.                           
  447.                 }
  448.                  LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0);
  449.         }
  450. }


  451. /******************************************************************************
  452.       函数说明:显示两位小数变量
  453.       入口数据:x,y显示坐标
  454.                 num 要显示小数变量
  455.                 len 要显示的位数
  456.                 fc 字的颜色
  457.                 bc 字的背景色
  458.                 sizey 字号
  459.       返回值:  无
  460. ******************************************************************************/
  461. void LCD_ShowFloatNum1(u16 x,u16 y,float num,u8 len,u16 fc,u16 bc,u8 sizey)
  462. {                
  463.         u8 t,temp,sizex;
  464.         u16 num1;
  465.         sizex=sizey/2;
  466.         num1=num*100;
  467.         for(t=0;t<len;t++)
  468.         {
  469.                 temp=(num1/mypow(10,len-t-1))%10;
  470.                 if(t==(len-2))
  471.                 {
  472.                         LCD_ShowChar(x+(len-2)*sizex,y,'.',fc,bc,sizey,0);
  473.                         t++;
  474.                         len+=1;
  475.                 }
  476.                  LCD_ShowChar(x+t*sizex,y,temp+48,fc,bc,sizey,0);
  477.         }
  478. }


  479. /******************************************************************************
  480.       函数说明:显示图片
  481.       入口数据:x,y起点坐标
  482.                 length 图片长度
  483.                 width  图片宽度
  484.                 pic[]  图片数组   
  485.       返回值:  无
  486. ******************************************************************************/
  487. void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[])
  488. {
  489.         u8 t=1;
  490.         u32 num=length*width*2,num1;
  491.         LCD_Address_Set(x,y,x+length-1,y+width-1);
  492.         while(t)
  493.         {
  494.           if(num>65534)
  495.                 {
  496.                         num-=65534;
  497.                         num1=65534;
  498.                 }
  499.                 else
  500.                 {
  501.                         t=0;
  502.                         num1=num;
  503.                 }
  504.                 MYDMA_Config(DMA_CH3,(u32)&SPI1->DAT,(u32)pic,num1);
  505.     //MYDMA_Config(DMA_CH3,(u32)SPI_MASTER_DR_Base,(u32)pic,num1);
  506.                 SPI_I2S_EnableDma(SPI1,SPI_I2S_DMA_TX,ENABLE);
  507.     //SPI_Enable(SPI1, ENABLE);
  508.                 MYDMA_Enable(DMA_CH3);
  509.                 while(1)
  510.                 {
  511.                         if(DMA_GetFlagStatus(DMA_FLAG_TC3,DMA)!=RESET)//等待通道3传输完成
  512.                         {
  513.                                 DMA_ClearFlag(DMA_FLAG_TC3,DMA);//清除通道3传输完成标志
  514.                                 break;
  515.                         }
  516.                 }
  517.                 pic+=65534;
  518.         }
  519. }


其中LCD_Fill 为单一颜色刷屏,用到DMA配置1,其余均用DMA默认配置。
我们在MAIN函数里面调用:
  1. int main(void)
  2. {
  3.   u8 t,i,j;
  4.         u8 len;       
  5.         u16 times=0;  
  6.   float m=0;
  7.   /*SystemInit() function has been called by startup file startup_n32g43x.s*/
  8.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
  9.         delay_init(108);                //延时初始化
  10.         DBG_UART_Init(115200);        //串口初始化波特率为115200
  11.         LED_Init();                                  //初始化与LED连接的硬件接口  
  12.   
  13.   LCD_Init();//LCD初始化
  14.         LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  15.   LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  16.   LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  17. //  while(1){}
  18.   while(1)
  19.         {
  20. //                LCD_ShowPicture(0,0,240,240,gImage_2);
  21. //                delay_ms(1000);
  22.                 LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  23.                 LCD_ShowChinese(0,0,"国民技术论坛",RED,WHITE,32,0);
  24.                 LCD_ShowString(0,40,"LCD_W:",RED,WHITE,16,0);
  25.                 LCD_ShowIntNum(48,40,LCD_W,3,RED,WHITE,16);
  26.                 LCD_ShowString(80,40,"LCD_H:",RED,WHITE,16,0);
  27.                 LCD_ShowIntNum(128,40,LCD_H,3,RED,WHITE,16);
  28.                 LCD_ShowString(80,40,"LCD_H:",RED,WHITE,16,0);
  29.                 LCD_ShowString(0,70,"Increaseing Nun:",RED,WHITE,16,0);
  30.                 LCD_ShowFloatNum1(128,70,m,4,RED,WHITE,16);
  31.                 m+=0.11;
  32.                 for(j=0;j<3;j++)
  33.                 {
  34.                         for(i=0;i<6;i++)
  35.                         {
  36.                                 LCD_ShowPicture(40*i,120+j*40,40,40,gImage_1);
  37.                         }
  38.                 }
  39.                 delay_ms(5000);
  40.                 LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  41. //    delay_ms(200);
  42. //    LCD_Fill(0,0,LCD_W,LCD_H,WHITE);
  43.         }
显示如下:
M7.jpg
总体而言,是实现了过程,但是对于SPI速度不能调慢,慢了就不正常,我也是没弄明白~还有就是LCD_FILL这个函数莫名的奇怪。
必须调用2次,才正常。看了代码,也没看成那边有问题~这个代码可是在STM32上完全验证通过的。

评论

屏幕的驱动芯片对于spi速率有要求,不能太低。  发表于 2024-9-18 10:18
kyzhd 发表于 2022-1-21 14:13 来自手机 | 显示全部楼层
可以咨询一下国民的技术。
沧桑小草 发表于 2022-1-24 11:30 | 显示全部楼层
可以将源工程也附上,让大家看看找找原因
mutable 发表于 2022-2-8 17:19 | 显示全部楼层
看注释,还以为是I2C,原来是SPI
RAYINGPX 发表于 2024-5-31 17:08 | 显示全部楼层
利害!顶
结合国际经验 发表于 2024-8-31 20:11 | 显示全部楼层
开发设计和调试
您需要登录后才可以回帖 登录 | 注册

本版积分规则

111

主题

627

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部