本帖最后由 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 行里面增加大量延时,故障还是会出现
*/
|