本帖最后由 panxiaoyi 于 2025-4-20 11:24 编辑
CH32V103 的 SPI+DMA 的状态寄存器请教:
我本来是要驱动 ST7789 彩屏的,采用硬件 16位SPI+16位DMA 传送显示的数据,指令数据由 8位SPI 查询死循环等待传送。使用 CH32V103R8T6 和 CH32V203G8R6 都出现了同样的问题。
我现在做了代码精简,只有一个 main.c 文件,不再驱动显示,只看 SPI 输出的 (时钟、数据) 的波形
现在的代码逻辑是:
1:使用 SPI/DMA 输出 38400 个 16 位数据。
2:等待 SPI 空闲,然后,单纯使用 8位SPI 输出几个数据,死循环查询等待结束。
3:返回循环。
代码如下:
- #include "debug.h"
- #define SPI1_DMA_RX_CH DMA1_Channel2
- #define SPI1_DMA_TX_CH DMA1_Channel3
- #define SPI1_DMA_TC_FLAG DMA1_FLAG_TC3
- #define SPI1_DMA_RC_FLAG DMA1_FLAG_TC2
- uint16_t i = 0;
- void SPI1_GPIO_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure = {0}; // GPIO配置结构体
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // 选择SPI的SCK和MOSI引脚
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出模式(用于SPI主设备输出)
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; // 速度
- GPIO_Init(GPIOA, &GPIO_InitStructure); // 使用上述参数初始化GPIOx
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // SPI的MISO引脚
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- }
- //==============================================================================
- void SPI1_Init(void)
- {
- SPI_InitTypeDef SPI_InitStructure = {0}; // SPI配置结构体
- SPI_Cmd(SPI1, DISABLE); // 在配置SPI模式前,需要清除SPE位
- SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; // 单线单向发送模式(仅使用MOSI)
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 配置为主机模式
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8位数据帧格式
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 时钟极性:空闲时高电平
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 时钟相位:第二个边沿采样(CPOL=1 + CPHA=1 即SPI模式3)
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制NSS(片选信号由用户代码控制)
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // 波特率预分频系数
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位先发送(MSB First)
- SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC多项式值(此处设置但未启用CRC功能)
- SPI_Init(SPI1, &SPI_InitStructure); // 使用上述参数初始化SPIx寄存器
- SPI_Cmd(SPI1, ENABLE); // 启动SPIx工作(配置完成后激活外设)
- }
- //==============================================================================
- void SPI1_Write(uint8_t data)
- {
- while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // 等待SPI发送缓冲区为空
- SPI_I2S_SendData(SPI1, data); // 发送数据
- while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY)); // 等待SPI空闲
- }
- //==============================================================================
- void DISPLAY_WR_REG(uint8_t data)
- {
- SPI1_Write(data);
- }
- //==============================================================================
- void DISPLAY_WR_DAT(uint8_t data)
- {
- SPI1_Write(data);
- }
- //==============================================================================
- void SPI1_DMA_Init_16B(uint8_t Flag)
- {
- SPI_InitTypeDef SPI_InitStructure = {0};
- SPI_Cmd(SPI1, DISABLE);
- SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
- SPI_InitStructure.SPI_CRCPolynomial = 7;
- SPI_Init(SPI1, &SPI_InitStructure);
- SPI_Cmd(SPI1, ENABLE);
-
- // 下面是DMA配置
- DMA_InitTypeDef DMA_InitStructure = {0}; // DMA初始化结构体
- DMA_DeInit(SPI1_DMA_TX_CH); // 复位SPIx发送通道的DMA寄存器到默认值
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DATAR; // 设置外设地址为SPIx数据寄存器
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)NULL; // 暂设内存地址为NULL(实际使用前需配置有效地址)
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // 传输方向:内存到外设(存储器作为源)
- DMA_InitStructure.DMA_BufferSize = 0; // 传输数据量初始为0(实际使用需配置)
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址固定(SPI数据寄存器不需要递增)
- if(!Flag) DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增(用于传输数组数据)
- else DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // 内存地址固定(用于传输相同的数据)
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 外设数据宽度:16位
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 内存数据宽度:16位
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // 普通模式(传输完成后停止)
- DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 高通道优先级
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 禁用内存到内存模式(使用外设模式)
- DMA_Init(SPI1_DMA_TX_CH, &DMA_InitStructure); // 使用上述参数初始化DMA通道
- SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); // 使能SPI1的TX DMA请求(当SPI需要发送数据时会自动触发DMA传输)
- }
- //==============================================================================
- void SPI1_DMA_Write16B(uint16_t *data, uint16_t len)
- {
- DMA_Cmd(SPI1_DMA_TX_CH, DISABLE); // 禁用DMA通道防止配置过程中意外触发
- DMA_ClearFlag(SPI1_DMA_TC_FLAG); // 清除该通道的传输完成标志(具体标志需根据通道选择)
- SPI1_DMA_TX_CH->MADDR = (uint32_t)data; // 设置内存源地址(待发送数据缓冲区)
- SPI1_DMA_TX_CH->CNTR = len; // 设置传输数据长度(单位:数据项个数)
- DMA_Cmd(SPI1_DMA_TX_CH, ENABLE); // 使能DMA通道,开始数据传输
- }
- //==============================================================================
- void DisplaySetWindows(uint16_t xStar, uint16_t yStar, uint16_t xEnd, uint16_t yEnd)
- {
- __asm__ volatile("NOP");
- __asm__ volatile("NOP");
- // Delay_Ms(1000);
- while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY)); // 等待SPI空闲
- SPI1_Init();
-
- DISPLAY_WR_REG(0x2a);
- DISPLAY_WR_DAT(xStar >> 8);
- DISPLAY_WR_DAT(0x00FF & xStar);
- DISPLAY_WR_DAT(xEnd >> 8);
- DISPLAY_WR_DAT(0x00FF & xEnd);
- DISPLAY_WR_REG(0x2B);
- DISPLAY_WR_DAT(yStar >> 8);
- DISPLAY_WR_DAT(0x00FF & yStar);
- DISPLAY_WR_DAT(yEnd >> 8);
- DISPLAY_WR_DAT(0x00FF & yEnd);
- DISPLAY_WR_REG(0x2C);
- }
- //==============================================================================
- void DisplayFillColor(uint16_t Color, uint16_t len)
- {
- while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY)); // 等待SPI空闲
- SPI1_DMA_Init_16B(1);
- SPI1_DMA_Write16B(&Color,len);
- }
- //==============================================================================
- int main(void)
- {
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
- SystemCoreClockUpdate();
- Delay_Init();
- USART_Printf_Init(115200);
- printf("SystemClk:%d\r\n", SystemCoreClock);
- printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
- printf("This is printf example\r\n");
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 必须开启GPIOB时钟才能操作其引脚
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
- SPI1_GPIO_Init();
- SPI1_Init();
- while(1)
- {
- i+=1000; //颜色变化
- DisplayFillColor(i,38400);//38400); //显示填充(颜色,像数点的数量)
- Delay_Ms(100); //延时,以便观察SPI/DMA输出的波形
- DisplaySetWindows(0, 0, 240-1, 160-1); //设置显示屏的显示窗口
- }
- }
- /*
- 现在遇到的问题是:
- 1:预期正常时:SPI/DMA 输出的时钟波形 = 数据波形(数据波形不应该出现 0 )
- 2:如果在 188 行设置断点,那面 SPI/DMA 输出的时钟正常,数据正常
- 3:如果在 188 行的函数的里面的开头设置断点,那么,进入函数后,SPI/DMA 的数据就输出0,时钟还是正常的
- 4:如果在 188 行里面增加大量延时,故障还是会出现
- */
|