立创·天空星 GD32F407VET6 使用SPI DMA驱动ST7735屏幕

一、硬件初始化

本次例程使用的外设是SPI0, DMA1-CH5,IO口使用情况如下:

IO定义
PA5SPI0-SCK 时钟
PA7SPI0-MOSI SPI发送
PB10LCD-RES 复位,低电平使能
PB11LCD-DC 数据/命令选择,低电平命令,高电平数据
PB12LCD-CS 片选,低电平使能
PB13LCD-BK 背光,悬空/高电平使能, 接地关闭

代码如下:

GPIO初始化

static void GPIO_Config(void)
{
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    /* configure SPI0 GPIO */
    gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_5 | GPIO_PIN_7);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_7);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
		
	/* set BackUp RES CS DC as GPIO*/
    gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_14 | GPIO_PIN_11 | GPIO_PIN_10 | GPIO_PIN_12);
    gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14 | GPIO_PIN_11 | GPIO_PIN_10 | GPIO_PIN_12);
		
	gpio_bit_set(GPIOB,GPIO_PIN_12);
}

DMA配置初始化

static void DMA_Config(void)
{
	rcu_periph_clock_enable(RCU_DMA1);
    dma_single_data_parameter_struct dma_init_struct;

    /* configure SPI0 transmit DMA */
    dma_deinit(DMA1, DMA_CH5);
	
    dma_init_struct.periph_addr  		= (uint32_t)&SPI_DATA(SPI0);
    dma_init_struct.memory0_addr 		= (uint32_t)spi0_send_array; // 自己定义一个数组,用来储存发往LCD的数据
    dma_init_struct.direction    		= DMA_MEMORY_TO_PERIPH;
    dma_init_struct.priority            = DMA_PRIORITY_HIGH;
    dma_init_struct.number              = LCD_BUFFER_SIZE;
    dma_init_struct.periph_inc          = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_inc          = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.circular_mode       = DMA_CIRCULAR_MODE_DISABLE;
	dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;

	dma_single_data_mode_init(DMA1, DMA_CH5, &dma_init_struct);
	dma_channel_subperipheral_select(DMA1, DMA_CH5, DMA_SUBPERI3);
}

SPI外设配置初始化

static void SPI_Config(void)
{
	rcu_periph_clock_enable(RCU_SPI0);
    spi_parameter_struct spi_init_struct;

    /* configure SPI0 parameter */
    spi_init_struct.trans_mode  		 = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode 		 = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss         		 = SPI_NSS_HARD;
    spi_init_struct.prescale             = SPI_PSC_2;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI0, &spi_init_struct);

	spi_enable(SPI0);
    spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
}

调用这个函数初始化GPIO,SPI和DMA

void SPI_Init(void)
{
		GPIO_Config();
		DMA_Config();
		SPI_Config();
}

注意事项

有以下几点配置时需要注意下,
GD324Fxx用户手册 DMA1外设请求
这里对应以下这段代码,DMA_SUBPERI3 对应表格左侧的通道3,截自GD32F4xx 用户手册 v3.0

dma_channel_subperipheral_select(DMA1, DMA_CH5, DMA_SUBPERI3);

在这里插入图片描述
IO的复用设置,对应上图,截自GD32F407xx 数据手册 v2.8

gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_5 | GPIO_PIN_7);

二、屏幕初始化

屏幕使用的是 “1.44寸TFT彩屏 不焊针” 这一项,购买链接如下
TFT-LCD 1.44寸 ST7735 淘宝链接

使用时还要注意连接屏幕的杜邦线不要太长,否则对SPI信号有影响导致通信不稳定。

屏幕复位

这里移植了RT-Thread,使用的是系统延时

void ST7735_Reset(void)
{
  LCD_RES_L;
  rt_thread_mdelay(150);
  LCD_RES_H;
  rt_thread_mdelay(150);
}

向屏幕发送数据

void st7735_dma_write(uint8_t *data,uint16_t len)
{
		dma_flag_clear(DMA1, DMA_CH5, DMA_FLAG_FTF);
		dma_channel_disable(DMA1, DMA_CH5);
		
		dma_memory_address_config(DMA1, DMA_CH5,DMA_MEMORY_0,(rt_uint32_t)data);
		dma_transfer_number_config(DMA1, DMA_CH5,len);
		
		dma_channel_enable(DMA1, DMA_CH5);
		while(dma_flag_get(DMA1, DMA_CH5, DMA_FLAG_FTF) == RESET)	{}
}

void st7735_write(uint8_t data)
{
	while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE))	{}
	spi_i2s_data_transmit(SPI0, data);
	while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE))	{}
	spi_i2s_data_receive(SPI0);
}

void Lcd_WriteIndex(uint8_t cmd)
{
    LCD_DC_L;
    LCD_CS_L;
	st7735_write(cmd);
	LCD_CS_H;
}

void Lcd_WriteData(uint8_t data)
{
	LCD_DC_H;
	LCD_CS_L;
	st7735_write(data);
	LCD_CS_H;
}

uint8_t LCD_DMA_WriteData_16Bit(uint8_t *data,uint16_t len)
{
	LCD_DC_H;
	LCD_CS_L;
	st7735_dma_write(data,len);
	LCD_CS_H;
}

屏幕初始化

void ST7735_Init(void)
{
	//Reset before LCD Init.
	ST7735_Reset(); 

	//LCD Init For 1.44Inch LCD Panel with ST7735R.
	Lcd_WriteIndex(0x11);//Sleep exit 
	rt_thread_mdelay(120);
		
	//ST7735R Frame Rate
	Lcd_WriteIndex(0xB1); 
	Lcd_WriteData(0x01); 
	Lcd_WriteData(0x2C); 
	Lcd_WriteData(0x2D); 

	Lcd_WriteIndex(0xB2); 
	Lcd_WriteData(0x01); 
	Lcd_WriteData(0x2C); 
	Lcd_WriteData(0x2D); 

	Lcd_WriteIndex(0xB3); 
	Lcd_WriteData(0x01); 
	Lcd_WriteData(0x2C); 
	Lcd_WriteData(0x2D); 
	Lcd_WriteData(0x01); 
	Lcd_WriteData(0x2C); 
	Lcd_WriteData(0x2D); 
	
	Lcd_WriteIndex(0xB4); //Column inversion 
	Lcd_WriteData(0x07); 
	
	//ST7735R Power Sequence
	Lcd_WriteIndex(0xC0); 
	Lcd_WriteData(0xA2); 
	Lcd_WriteData(0x02); 
	Lcd_WriteData(0x84); 
	Lcd_WriteIndex(0xC1); 
	Lcd_WriteData(0xC5); 

	Lcd_WriteIndex(0xC2); 
	Lcd_WriteData(0x0A); 
	Lcd_WriteData(0x00); 

	Lcd_WriteIndex(0xC3); 
	Lcd_WriteData(0x8A); 
	Lcd_WriteData(0x2A); 
	Lcd_WriteIndex(0xC4); 
	Lcd_WriteData(0x8A); 
	Lcd_WriteData(0xEE); 
	
	Lcd_WriteIndex(0xC5); //VCOM 
	Lcd_WriteData(0x0E); 
	
	Lcd_WriteIndex(0x36); //MX, MY, RGB mode 
	Lcd_WriteData(0xC8); 
	
	//ST7735R Gamma Sequence
	Lcd_WriteIndex(0xe0); 
	Lcd_WriteData(0x0f); 
	Lcd_WriteData(0x1a); 
	Lcd_WriteData(0x0f); 
	Lcd_WriteData(0x18); 
	Lcd_WriteData(0x2f); 
	Lcd_WriteData(0x28); 
	Lcd_WriteData(0x20); 
	Lcd_WriteData(0x22); 
	Lcd_WriteData(0x1f); 
	Lcd_WriteData(0x1b); 
	Lcd_WriteData(0x23); 
	Lcd_WriteData(0x37); 
	Lcd_WriteData(0x00); 	
	Lcd_WriteData(0x07); 
	Lcd_WriteData(0x02); 
	Lcd_WriteData(0x10); 

	Lcd_WriteIndex(0xe1); 
	Lcd_WriteData(0x0f); 
	Lcd_WriteData(0x1b); 
	Lcd_WriteData(0x0f); 
	Lcd_WriteData(0x17); 
	Lcd_WriteData(0x33); 
	Lcd_WriteData(0x2c); 
	Lcd_WriteData(0x29); 
	Lcd_WriteData(0x2e); 
	Lcd_WriteData(0x30); 
	Lcd_WriteData(0x30); 
	Lcd_WriteData(0x39); 
	Lcd_WriteData(0x3f); 
	Lcd_WriteData(0x00); 
	Lcd_WriteData(0x07); 
	Lcd_WriteData(0x03); 
	Lcd_WriteData(0x10);  
	
	Lcd_WriteIndex(0x2a);
	Lcd_WriteData(0x00);
	Lcd_WriteData(0x00);
	Lcd_WriteData(0x00);
	Lcd_WriteData(0x7f);

	Lcd_WriteIndex(0x2b);
	Lcd_WriteData(0x00);
	Lcd_WriteData(0x00);
	Lcd_WriteData(0x00);
	Lcd_WriteData(0x9f);
	
	Lcd_WriteIndex(0xF0); //Enable test command  
	Lcd_WriteData(0x01); 
	Lcd_WriteIndex(0xF6); //Disable ram power save mode 
	Lcd_WriteData(0x00); 
	
	Lcd_WriteIndex(0x3A); //65k mode 
	Lcd_WriteData(0x05); 
	
	Lcd_WriteIndex(0x29);//Display on
	LCD_BK_H;
}

显示函数

// 设置lcd显示区域,在此区域写点数据自动换行
void Lcd_SetRegion(uint16_t x_start,uint16_t y_start,uint16_t x_end,uint16_t y_end)
{
	Lcd_WriteIndex(0x2a);
	Lcd_WriteData(0x00);
	Lcd_WriteData(x_start+2);
	Lcd_WriteData(0x00);
	Lcd_WriteData(x_end+2);

	Lcd_WriteIndex(0x2b);
	Lcd_WriteData(0x00);
	Lcd_WriteData(y_start+3);
	Lcd_WriteData(0x00);
	Lcd_WriteData(y_end+3);
	
	Lcd_WriteIndex(0x2c);
}

// 设置lcd显示起始点
void Lcd_SetXY(uint16_t x,uint16_t y)
{
  	Lcd_SetRegion(x,y,x,y);
}

// 画一个点
void Gui_DrawPoint(uint16_t x,uint16_t y,uint16_t Data)
{
	uint8_t data[2];
	data[0] = Data>>8;
	data[1] = Data&0xFF;
	Lcd_SetRegion(x,y,x + 1,y + 1);
	LCD_DMA_WriteData_16Bit(data,2);
}

// 全屏清屏函数,填充颜色Color
void Lcd_Clear(uint16_t Color)               
{	
	unsigned int i,m;
	Lcd_SetRegion(0,0,ST7735_WIDTH - 1,ST7735_HEIGHT - 1);
	Lcd_WriteIndex(0x2C);
	for(i = 0;i < LCD_BUFFER_SIZE;i = i + 2)
	{
		spi0_send_array[i] = Color>>8;
		spi0_send_array[i+1] = Color&0xFF;
	}
	LCD_DMA_WriteData_16Bit(spi0_send_array,LCD_BUFFER_SIZE);
}

参数定义

// 屏幕参数定义
#define ST7735_WIDTH  128
#define ST7735_HEIGHT 128
#define LCD_BUFFER_SIZE (ST7735_WIDTH * ST7735_HEIGHT * 2) // RGB565格式

// 颜色宏定义
#define ST7735_BLACK   0x0000
#define ST7735_BLUE    0x001F
#define ST7735_RED     0xF800
#define ST7735_GREEN   0x07E0
#define ST7735_CYAN    0x07FF
#define ST7735_MAGENTA 0xF81F
#define ST7735_YELLOW  0xFFE0
#define ST7735_WHITE   0xFFFF

#define SPI0_SCK_PIN	GPIO_PIN_5	// SPI时钟线
#define SPI0_MOSI_PIN	GPIO_PIN_7	// SPI发送

#define LCD_RES_PIN		GPIO_PIN_10	// 复位,低电平使能
#define LCD_DC_PIN		GPIO_PIN_11	// 数据/命令选择,低电平命令,高电平数据
#define LCD_CS_PIN		GPIO_PIN_12	// 片选,低电平使能
#define LCD_BK_PIN 		GPIO_PIN_14 // 背光,悬空使能, 接地关闭,默认上拉至3.3V

#define LCD_PORT	GPIOB
#define SPI0_PORT	GPIOA

// GPIO输出高低电平宏定义
#define SPI_SCK_H	gpio_bit_set(SPI0_PORT,SPI0_SCK_PIN)
#define SPI_SCK_L	gpio_bit_reset(SPI0_PORT,SPI0_SCK_PIN)

#define SPI_SDA_H	gpio_bit_set(SPI0_PORT,SPI0_MOSI_PIN)
#define SPI_SDA_L	gpio_bit_reset(SPI0_PORT,SPI0_MOSI_PIN)

#define LCD_RES_H	gpio_bit_set(LCD_PORT,LCD_RES_PIN)
#define LCD_RES_L	gpio_bit_reset(LCD_PORT,LCD_RES_PIN)


#define LCD_RES_H	gpio_bit_set(LCD_PORT,LCD_RES_PIN)
#define LCD_RES_L	gpio_bit_reset(LCD_PORT,LCD_RES_PIN)

#define LCD_CS_H	gpio_bit_set(LCD_PORT,LCD_CS_PIN)
#define LCD_CS_L	gpio_bit_reset(LCD_PORT,LCD_CS_PIN)

#define LCD_DC_H	gpio_bit_set(LCD_PORT,LCD_DC_PIN)
#define LCD_DC_L	gpio_bit_reset(LCD_PORT,LCD_DC_PIN)

#define LCD_BK_H	gpio_bit_set(LCD_PORT,LCD_BK_PIN)
#define LCD_BK_L	gpio_bit_reset(LCD_PORT,LCD_BK_PIN)


总结

本文介绍了GD32F407VET6单片机基于rt-thread nano如何驱动TFT-LCD屏幕 ST7735,GD32和STM32使用SPI时还是有不小的差别,希望这篇文章能给大家带来帮助,需要代码也可以留言。

### GD32 SPI DMA 发送 示例代码及教程 对于GD32系列微控制器,在使用SPI接口进行数据传输时,采用DMA可以显著提高效率并减少CPU占用率。下面展示一段基于GD32F303固件库的示例程序来说明如何配置和启动SPIDMA协同工作的发送过程[^1]。 #### 初始化设置 首先需要初始化SPI外设以及相应的DMA通道: ```c #include "gd32f30x.h" // 定义使用SPI端口及其对应的DMA请求ID #define SPIx SPI1 #define SPIx_CLK RCC_APB2Periph_SPI1 #define SPIx_TX_DMA_CHANNEL DMA1_Channel3 #define SPIx_RX_DMA_CHANNEL DMA1_Channel2 #define SPIx_DMA_IRQ DMA1_Channel2_3_IRQn void spi_dma_init(void){ /* 配置SPI */ spi_i2s_deinit(SPIx); // 设置SPI工作参数... } void dma_config_for_spi_send(uint8_t* buffer, uint32_t length){ nvic_irq_enable(SPIx_DMA_IRQ, 0, 0); dma_parameter_struct dma_initstruct; dma_deinit(SPIx_TX_DMA_CHANNEL); dma_initstruct.direction = DMA_MEMORY_TO_PERIPHERAL; // 数据流向是从内存到外设 dma_initstruct.memory_addr = (uint32_t)buffer; // 源地址为待发送的数据缓冲区首地址 dma_initstruct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; // 开启源地址自增模式 dma_initstruct.periph_addr = (uint32_t)&(SPIx->DR); // 目的地地址指向SPI寄存器中的数据接收/发送寄存器 dma_initstruct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; // 关闭目的地地址自动增加 dma_initstruct.priority = DMA_PRIORITY_HIGH; // 设定优先级高低 dma_initstruct.number_of_data = length; // 要传送的数据量大小 dma_init(SPIx_TX_DMA_CHANNEL,&dma_initstruct); } ``` 这段代码完成了基本的硬件资源分配,并设置了必要的中断使能位以便后续处理完成事件通知。 #### 启动DMA传输 当一切准备就绪之后就可以调用如下函数触发一次完整的DMA发送动作了: ```c void start_spi_dma_transfer(uint8_t *data_buffer,uint32_t size){ /* 清除可能存在的错误标志 */ while(dma_flag_get(DMA_FLAG_GIF3)!= RESET){}; /* 将要发送的数据加载入DMA */ dma_memory_to_memory_disable(DMA1_Channel3); dma_circulation_disable(DMA1_Channel3); dma_config_for_spi_send(data_buffer,size); /* 打开SPI-DMA连接开关 */ spi_i2s_dma_transmit_config(SPIx,SPI_I2S_DMA_TRANSMIT_ENABLE); /* 最后开启DMA流控 */ dma_channel_enable(SPIx_TX_DMA_CHANNEL); } ``` 上述方法会先清理任何残留的状态标记再按照给定参数重新装载新的任务列表;接着激活指定方向上的DMA链接最后才真正意义上开启了整个流程。 #### 中断服务例程ISR 为了能够在每次DMA事务结束后得到及时反馈还需要编写配套的服务例行程序(ISR),这里仅给出框架示意: ```c void DMA1_Channel2_3_IRQHandler(void){ if(dma_interrupt_flag_get(DMA1_Channnel3,DMA_INT_FTFIF)){ // 处理半包或全包结束后的逻辑... /* 清除已完成转移产生的中断*/ dma_interrupt_flag_clear(DMA1_Channel3,DMA_INT_FTFIF); // 如果有必要还可以在此处重启下一个周期的任务... } } ``` 以上就是利用DMA加速GD32平台上SPI通信的一个简单实例介绍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值