STM32F446RET6 SPI1 DMA驱动Nokia5110 LCD失效求助
SPI1 DMA传输Nokia5110异常问题排查
问题现象
使用STM32F446RET6的SPI1 DMA向Nokia5110 LCD发送数据时无法正常工作,非DMA方式可正常运行。
关键代码片段
static void initSPI1(void) { // PA5, PA6, PA7 AS AF7 RCC->AHB1ENR |= 1<<0; GPIOA->MODER &= ~(1<<10 | 1<<12 | 1<<14); GPIOA->MODER |= 1<<11 | 1<<13 | 1<<15; GPIOA->AFR[0] |= 1<<20 | 1<<22 | 1<<24 | 1<<26 | 1<<28 | 1<<30; // SPI1 CONFIG RCC->APB2ENR |= 1<<12; SPI1->CR1 |= 1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<8 | 1<<9; SPI1->CR2 |= 1<<1; // ENABLE TX DMA SPI1->CR1 |= 1<<6; // ENABLE SPI1 // DMA2 CONFIG RCC->AHB1ENR |= 1<<22; DMA2_Stream3->CR |= 1<<4 | 1<<6 | 1<<8 | 1<<10 | 1<<25 | 1<<26; } static void sendChar(char character) { GPIOA->ODR &= ~(1<<0); // CS_PIN_LOW DMA2_Stream3->NDTR = 1; DMA2_Stream3->PAR = (uint32_t) &SPI1->DR; DMA2_Stream3->M0AR = (uint32_t) character; DMA2_Stream3->CR |= 1<<0; while(!(DMA2->LISR & 1<<27)); DMA2_Stream3->CR &= ~(1<<0); DMA2->LIFCR |= 1<<27; // send char without dma // SPI1->DR = character; // while(!(SPI1->SR & 1<<1)); // while(SPI1->SR & 1<<7); // // (void) SPI1->DR; // (void) SPI1->SR; GPIOA->ODR |= 1<<0; // CS_PIN_HIGH }
程序错误点排查
1. DMA内存地址传递错误
在sendChar函数中,DMA2_Stream3->M0AR = (uint32_t) character 是错误的。DMA需要读取的是变量的内存地址,而非变量的值。正确写法应为:
DMA2_Stream3->M0AR = (uint32_t)&character;
当前写法会将字符的ASCII值当作内存地址,导致DMA从非法地址读取数据,传输内容完全错误。
2. DMA通道选择错误
SPI1_TX对应的DMA通道为DMA2 Stream3 Channel 3(参考STM32F446手册),但代码中配置了1<<25 | 1<<26(CHSEL[2:0] = 110,对应通道6),通道不匹配会导致DMA无法响应SPI的传输请求。正确的通道配置应为:
DMA2_Stream3->CR |= (3 << 25); // CHSEL[2:0] = 011,选择通道3
3. DMA数据宽度不匹配
SPI1配置为8位数据传输,但代码中DMA的PSIZE(外设数据宽度)和MSIZE(内存数据宽度)被设置为16位(1<<8和1<<10),数据宽度不匹配会导致传输数据被截断或错位。正确配置应为8位宽度:
// 清除原有16位配置,设置为8位 DMA2_Stream3->CR &= ~((3 << 8) | (3 << 10));
4. DMA Stream初始化不完整
- 未开启内存增量模式(
MINC位):虽然单次传输1字节时不影响,但如果后续扩展多字节传输,必须开启该位,建议补充:DMA2_Stream3->CR |= 1<<11; // 开启内存增量模式 - 建议先清除DMA Stream的CR寄存器再配置,避免默认值干扰:
DMA2_Stream3->CR = 0; // 先清空配置寄存器 // 再重新写入所需配置 DMA2_Stream3->CR |= 1<<4 | 1<<6 | (3 << 25) | 1<<11;
修正后的关键代码示例
修正后的initSPI1函数
static void initSPI1(void) { // PA5, PA6, PA7 AS AF7 RCC->AHB1ENR |= 1<<0; GPIOA->MODER &= ~(1<<10 | 1<<12 | 1<<14); GPIOA->MODER |= 1<<11 | 1<<13 | 1<<15; GPIOA->AFR[0] |= 1<<20 | 1<<22 | 1<<24 | 1<<26 | 1<<28 | 1<<30; // SPI1 CONFIG RCC->APB2ENR |= 1<<12; SPI1->CR1 |= 1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<8 | 1<<9; SPI1->CR2 |= 1<<1; // ENABLE TX DMA SPI1->CR1 |= 1<<6; // ENABLE SPI1 // DMA2 CONFIG RCC->AHB1ENR |= 1<<22; DMA2_Stream3->CR = 0; // 清空寄存器 // DIR=1(内存到外设), MINC=1(内存增量), PINC=0(外设非增量) // PSIZE=00(8位), MSIZE=00(8位), CHSEL=011(通道3), CIRC=0(非循环) DMA2_Stream3->CR |= (1<<4) | (1<<11) | (3<<25); }
修正后的sendChar函数
static void sendChar(char character) { GPIOA->ODR &= ~(1<<0); // CS_PIN_LOW DMA2_Stream3->NDTR = 1; DMA2_Stream3->PAR = (uint32_t)&SPI1->DR; DMA2_Stream3->M0AR = (uint32_t)&character; // 修正为变量地址 DMA2_Stream3->CR |= 1<<0; // 启动DMA传输 while(!(DMA2->LISR & (1<<27))); // 等待传输完成 DMA2_Stream3->CR &= ~(1<<0); // 停止DMA DMA2->LIFCR |= (1<<24) | (1<<25) | (1<<26) | (1<<27); // 清除所有Stream3标志 GPIOA->ODR |= 1<<0; // CS_PIN_HIGH }
内容的提问来源于stack exchange,提问作者May Myat Thuzar
相关产品推荐
相关产品推荐

