@安小芯
谢谢国民技术提供的N32G430开发板,使用官方提供的底层驱动库非常容易上手。俗话说,好马配好鞍。芯片设计得再好,也是需要配套的基础底层驱动软件来加快使用者上手时间的,经过几天的测试体验,我感觉国民技术已经做到了。本次提供的测试例程是使用硬件SPI2和DMA驱动中景园1.14寸135RGB*240 TFT LCD模组(我是淘宝渠道获取的),该模组的完整型号为:ZJY114S0800TG01。LCD模组与N32G430开发板硬件连接如下:
GND - GND
VCC - 3V3
RES - PB10 (复位,低电平有效)
DC - PB11 (数据/命令控制)
CS - PB12 (片选,低电平有效)
BLK - PB14 (背光,高电平有效)
SCL - PB13 (硬件SPI2_SCK)
SDA - PB15 (硬件SPI2_MOSI)
LCD驱动主要包含两个方面,第一个方面是按照厂家提供的规格书对相关寄存器进行初始化的流程,通过SPI依次将数据或者命令依次写入寄存器即可,SPI的速率快慢对初始化流程影响不大,这里可以采用传统的SPI传输方式即可;第二个方面是刷屏和图片显示,也就是要将显示区域内特定的字符或者点阵数据通过SPI接口发送到显示缓冲区,如果SPI速率过慢不仅会影响显示效果,也会占用过多的MCU处理时间,这部分大块的数据搬运就可以通过SPI+DMA的传输方式来完成,这样不仅释放了MCU在数据传输和存取上的压力,还大大提升SPI外设处理数据的效率。DMA控制器的原理是,和Cortex-M内核共享相同的系统数据总线,执行直接存储器数据传输。MCU初始化完外设的传输动作,传输动作本身是由 DMA 控制器来实施和完成的。
N32G430的DMA控制器使用非常简单,8个可独立配置的DMA通道,可以任意指定在某个外设寄存器地址和SRAM内存地址之间进行DMA 传输。外设和内存传输数据位宽支持字节、半字和字长度。DMA的配置也非常简单:
- //DMA通道配置
- //这里的传输形式是固定的,这点要根据不同的情况来修改
- //从存储器->外设模式/8位数据宽度/存储器增量模式
- //DMA_CHx:DMA通道CHx
- //addr_per:外设地址peripheral
- //addr_mem:存储器地址
- //cnt:数据传输量
- void DMA_Configuration(DMA_ChannelType* DMA_CHx, uint32_t addr_per, uint32_t addr_mem, uint32_t cnt)
- {
- DMA_InitType DMA_InitStructure;
- DMA_Reset(DMA_CHx);
- /* SPI_MASTER TX DMA config */
- DMA_InitStructure.MemAddr = (uint32_t)addr_mem;
- DMA_InitStructure.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; //内存数据宽度为8位
- DMA_InitStructure.MemoryInc = DMA_MEM_INC_MODE_ENABLE; //内存地址寄存器递增(内存地址自增,发送一个缓冲区内容)
- DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST; //数据传输方向,从内存读取发送到外设
- DMA_InitStructure.PeriphAddr = (uint32_t)addr_per;
- DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; //外设数据宽度为8位
- DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_MODE_DISABLE; //外设地址寄存器不变(外设地址不自增)
- DMA_InitStructure.BufSize = cnt;
- DMA_InitStructure.CircularMode = DMA_CIRCULAR_MODE_DISABLE; //禁止循环模式
- DMA_InitStructure.Mem2Mem = DMA_MEM2MEM_DISABLE;
- DMA_InitStructure.Priority = DMA_CH_PRIORITY_MEDIUM;
- DMA_Initializes(DMA_CHx, &DMA_InitStructure);
- DMA_Channel_Request_Remap(DMA_CHx, DMA_REMAP_SPI2_TX);
- }
通过SPI+DMA方式传输N个像素点的函数:
- void OLED_SPI_DMA_WriteNByte(uint16_t *Data, uint32_t len)
- {
- uint8_t *p = NULL;
- p = (uint8_t *)Data;
- OLED_CS_L();
-
- SPI_I2S_DMA_Transfer_Enable(SPI2, SPI_I2S_DMA_TX);
-
- DMA_Configuration(DMA_CH1, (u32)&SPI2->DAT, (u32)&p[0], len * 2); // 配置DMA 8位传输,内存到SPI2外设
- DMA_Channel_Enable(DMA_CH1); // 启动DMA传输
- /* Wait for data sending complete*/
- //while(DMA_Flag_Status_Get(DMA, DMA_CH1_TXCF) == RESET) // DMA查询模式,等待DMA传输完成
- // ;
-
- // 等待DMA完成标志置位
- while(1)
- {
- if(DMA_Flag_Status_Get(DMA, DMA_CH1_TXCF) != RESET)
- {
- DMA_Flag_Status_Clear(DMA, DMA_CH1_TXCF);
- break;
- }
- }
-
- // 解决了刷屏最后一部分刷屏不正常的问题
- while(SPI_I2S_Flag_Status_Get(SPI2, SPI_I2S_FLAG_BUSY) != RESET) // 等待SPI输完成
- ;
- OLED_CS_H();
- }
通过SPI+DMA方式刷屏和显示图片的处理函数:
- void LCD_Fill( u16 xsta, u16 ysta, u16 xend, u16 yend, u16 color )
- {
- u32 i;
- __align(4) u16 buf[BUF_SIZE];
- u16* tar = buf;
- uint32_t count = ( yend - ysta ) * ( xend - xsta );
- LCD_Address_Set( xsta, ysta, xend - 1, yend - 1 ); //设置显示范围
-
- while( count )
- {
- int to_write = MIN( BUF_SIZE, count );
- // swap the high byte and low byte
- for( i = 0; i < to_write; i++ )
- {
- tar[i] = ( ( color>> 8 ) & 0xff ) | ( color << 8 );
- }
- OLED_SPI_DMA_WriteNByte(tar, to_write );
- count -= to_write;
- }
- }
- void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[])
- {
- //u16 i,j;
- //u32 k=0;
- //u16 *p = (u16 *)&pic[0];
- //u16 temp;
- LCD_Address_Set(x,y,x+length-1,y+width-1);
- #if (0)
- for(i=0;i<length;i++)
- {
- for(j=0;j<width;j++)
- {
- //LCD_WR_DATA8(pic[k*2]);
- //LCD_WR_DATA8(pic[k*2+1]);
- //k++;
- temp = p[k++];
- LCD_WR_DATA(SWAP16(temp));
- }
- }
- #endif
- OLED_SPI_DMA_WriteNByte((u16 *)&pic[0], length * width);
- }
SPI2的初始化代码,采用了软件控制片选信号的方式,方便一次性传输多个字节。
- void OLED_InitSPI2(void)
- {
- GPIO_InitType GPIO_InitStructure;
- SPI_InitType SPI2_InitStructure;
- RCC_APB2_Peripheral_Clock_Enable(RCC_APB2_PERIPH_SPI2);
- RCC_AHB_Peripheral_Clock_Enable( RCC_AHB_PERIPH_GPIOB);
- GPIO_Structure_Initialize(&GPIO_InitStructure);
- GPIO_InitStructure.Pin = GPIO_PIN_12;
- GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
- GPIO_Peripheral_Initialize(GPIOB, &GPIO_InitStructure);
- OLED_CS_H();
- GPIO_Structure_Initialize(&GPIO_InitStructure);
- GPIO_InitStructure.Pin = GPIO_PIN_13 | GPIO_PIN_15;
- GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
- GPIO_InitStructure.GPIO_Alternate = GPIO_AF1_SPI2;
- GPIO_Peripheral_Initialize(GPIOB, &GPIO_InitStructure);
- SPI_I2S_Reset(SPI2);
- SPI_Initializes_Structure(&SPI2_InitStructure);
- SPI2_InitStructure.DataDirection = SPI_DIR_SINGLELINE_TX;
- SPI2_InitStructure.SpiMode = SPI_MODE_MASTER;
- SPI2_InitStructure.DataLen = SPI_DATA_SIZE_8BITS;
- SPI2_InitStructure.CLKPOL = SPI_CLKPOL_HIGH;
- SPI2_InitStructure.CLKPHA = SPI_CLKPHA_SECOND_EDGE;
- SPI2_InitStructure.NSS = SPI_NSS_SOFT;
- SPI2_InitStructure.BaudRatePres = SPI_BR_PRESCALER_2;
- SPI2_InitStructure.FirstBit = SPI_FB_MSB;
- SPI2_InitStructure.CRCPoly = 7;
- SPI_Initializes(SPI2, &SPI2_InitStructure);
- SPI_NSS_Config(SPI2, SPI_NSS_SOFT);
- SPI_Set_Nss_Level(SPI2, SPI_NSS_HIGH);
- SPI_ON(SPI2);
- }
最后,上传的附件包含了LCD显示效果视频和完整的工程代码,以及LCD模组的规格书及字符、汉字和图片取模教程。
|