大家好,最近我用HC32F460驱动一块2.8 寸的SPI TFT,分辨率320x240, 但是发现TFT刷屏的速度非常非常慢!慢到大概5到6s才会刷完一屏!后来我开了DMA,也是几乎没有任何改善!我的SPI配置用的是官方例程,SPI时钟2分频,系统时钟使用的MCU内部自带时钟,我不知道自带时钟的PCLK1是多少,但PCLK1 在2分频后总不能5,6s才刷完一次屏吧,我之前用过其他的国产MCU,仿STM32F030,主频只有48M,我拿来刷3.5寸480x320的屏都比这个快啊,调了好几天都找不到原因,后来打算换到外部时钟看看,结果换到外部时钟后,又出现新的问题了,下面是我使用这颗芯片的所有问题汇总,希望能得到专家的指点:
1,刚开始,使用的是MCU自带内部时钟,用SPI+DMA驱动屏幕,SPI+DMA的配置用的是官方例程,SPI时钟2分频,屏幕可以正常初始化和点亮,但就是速度非常非常慢,如上所提到的,5,6s刷屏一次,简直无法忍受,检查了很多地方,实在是没辙了,因为从没遇到过这样的问题,之前用过的国产32位的单片机也很多,从未遇到这样的问题,感觉很奇怪。
2,为了验证是不是内部时钟问题,我使用了外部时钟,在外面焊了一颗16M的晶振,然后用官方例程切换到外部时钟,奇怪的是切换过去之后,整个系统启动变得非常缓慢,大概2s左右,之前用内部时钟都是秒启动。而且使用这颗外部16M时钟还影响了我的timer0定时器,我明明是设置的1ms进一次中断,可是使用外部时钟后,变成大概6s进一次中断(期间也换过既可16M晶振验证,无改善)。外部时钟的时钟的配置按照官方例程,也检查了和修改了很多次,没有发现哪里有问题,所以我不知道到底外部时钟配置成功没有,从现象上看确实应该是配置成功了,但是没有按照预期运行,还影响了定时器时基。当然驱动屏幕的结果也是和使用内部时钟一样的,奇慢无比。
以下是我的部分代码:
SPI+DMA部分:
void LCD_SPI_INIT(void)
{
stc_spi_init_t stcSpiInit; //定义结构变量
MEM_ZERO_STRUCT(stcSpiInit); //清零结构变量
PWC_Fcg1PeriphClockCmd(PWC_FCG1_PERIPH_SPI3, Enable); //配置外设时钟
//配置SPI IO口
PORT_SetFunc(PortB, Pin14, Func_Spi3_Sck, Disable); //配置PB14为SPI SCK
PORT_SetFunc(PortB, Pin15, Func_Gpio, Disable); //配置PB15为普通GPIO,初始化已在GPIO里完成
PORT_SetFunc(PortB, Pin12, Func_Spi3_Mosi, Disable); //配置PB12为SPI MOSI
PORT_SetFunc(PortB, Pin13, Func_Spi3_Miso, Disable); //配置PB13为SPI MISO
//配置SPI结构参数
stcSpiInit.enClkDiv = SpiClkDiv2; //SPI频率为PCLK1的2分频,PCLK1为100MHz
stcSpiInit.enFrameNumber = SpiFrameNumber1; //SPI发送的数据帧,这里为1帧数据,8bit
stcSpiInit.enDataLength = SpiDataLengthBit8; //SPI发送数据长度为8bit
stcSpiInit.enFirstBitPosition = SpiFirstBitPositionMSB; //数据方向为MSB在前
stcSpiInit.enSckPolarity = SpiSckIdleLevelLow; //
stcSpiInit.enSckPhase = SpiSckOddSampleEvenChange; //
stcSpiInit.enReadBufferObject = SpiReadReceiverBuffer; //
stcSpiInit.enWorkMode = SpiWorkMode4Line; //配置为4线SPI
stcSpiInit.enTransMode = SpiTransFullDuplex; //配置为全双工模式
stcSpiInit.enCommAutoSuspendEn = Disable;
stcSpiInit.enModeFaultErrorDetectEn = Disable;
stcSpiInit.enParitySelfDetectEn = Disable;
stcSpiInit.enParityEn = Disable;
stcSpiInit.enParity = SpiParityEven;
//SPI MASTER模式配置,因为只是给LCD发送数据,所以只需要master模式
stcSpiInit.enMasterSlaveMode = SpiModeMaster;
stcSpiInit.stcDelayConfig.enSsSetupDelayOption = SpiSsSetupDelayCustomValue;
stcSpiInit.stcDelayConfig.enSsSetupDelayTime = SpiSsSetupDelaySck1;
stcSpiInit.stcDelayConfig.enSsHoldDelayOption = SpiSsHoldDelayCustomValue;
stcSpiInit.stcDelayConfig.enSsHoldDelayTime = SpiSsHoldDelaySck1;
stcSpiInit.stcDelayConfig.enSsIntervalTimeOption = SpiSsIntervalCustomValue;
stcSpiInit.stcDelayConfig.enSsIntervalTime = SpiSsIntervalSck6PlusPck2;
stcSpiInit.stcSsConfig.enSsValidBit = SpiSsValidChannel0;
stcSpiInit.stcSsConfig.enSs0Polarity = SpiSsLowValid;
SPI_Init(M4_SPI3, &stcSpiInit); //以上参数配置入SPI结构体初始化
LCD_DMA_INIT();
}
void LCD_DMA_INIT(void)
{
stc_dma_config_t stcDmaCfg;
MEM_ZERO_STRUCT(stcDmaCfg);
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_DMA1, Enable);
PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS, Enable);
//配置DMA发送
stcDmaCfg.u16BlockSize = 1u; //传输的数据块数量为1,最小为1,最多为1024
stcDmaCfg.u16TransferCnt = (uint16_t)(1); //传输的次数,每传输一次数据块,寄存器减1,直到减到0为止,如果一开始就-配置为0,则表示无限次传输
stcDmaCfg.u32SrcAddr = (uint32_t)(0); //配置DMA发送的源地址,就是DMA取数据的地址
stcDmaCfg.u32DesAddr = (uint32_t)(&M4_SPI3->DR); //配置DMA发送的目标地址
stcDmaCfg.stcDmaChCfg.enSrcInc = AddressIncrease; //源地址自增
stcDmaCfg.stcDmaChCfg.enDesInc = AddressFix; //目标地址固定
stcDmaCfg.stcDmaChCfg.enTrnWidth = Dma8Bit; //传输数据为8bit,和SPI数据宽度一致
stcDmaCfg.stcDmaChCfg.enIntEn = Disable; //禁止DMA中断
DMA_InitChannel(M4_DMA1, DmaCh1, &stcDmaCfg);
DMA_SetTriggerSrc(M4_DMA1, DmaCh1, EVT_SPI3_SPTI); //配置DMA发送触发源
DMA_Cmd(M4_DMA1, Enable); //使能DMA
}
//LCD 指令和数据发发送
void LCD_SendCmd8_SPI_DMA(uint8_t cmd8) //发送8bit指令
{
SendDataBuff8[0]=cmd8;
DMA_SetSrcAddress(M4_DMA1, DmaCh1, (uint32_t)(&SendDataBuff8[0])); // 重新设置源地址和发送的数据
DMA_SetTransferCnt(M4_DMA1, DmaCh1, (uint16_t)(1)); //设置传输次数,传输一次,计数器减到0停止
LCD_RS_WR_REG;
LCD_CS_SET_LOW;
DMA_ChannelCmd(M4_DMA1, DmaCh1, Enable); //使能DMA1通道
SPI_Cmd(M4_SPI3, Enable); //使能SPI请求DMA传输
while (Reset == DMA_GetIrqFlag(M4_DMA1, DmaCh1, TrnCpltIrq)) ; //等待DMA传输完成
DMA_ClearIrqFlag(M4_DMA1, DmaCh1, TrnCpltIrq); //清除标志位
LCD_CS_SET_HIGH; //拉高片选,结束一次传输
SPI_Cmd(M4_SPI3, Disable); //失能SPI
DMA_ChannelCmd(M4_DMA1, DmaCh1, Disable); //失能DMA1通道
}
void LCD_SendData8_SPI_DMA(uint8_t data8) //发送8bit数据
{
SendDataBuff8[0]=data8;
DMA_SetSrcAddress(M4_DMA1, DmaCh1, (uint32_t)(&SendDataBuff8[0])); // 重新设置源地址和发送的数据
DMA_SetTransferCnt(M4_DMA1, DmaCh1, (uint16_t)(1)); //设置传输次数,传输一次,计数器减到0停止
LCD_RS_WR_DATA;
LCD_CS_SET_LOW;
DMA_ChannelCmd(M4_DMA1, DmaCh1, Enable); //使能DMA1通道
SPI_Cmd(M4_SPI3, Enable); //使能SPI请求DMA传输
while (Reset == DMA_GetIrqFlag(M4_DMA1, DmaCh1, TrnCpltIrq)) ;//等待DMA传输完成
DMA_ClearIrqFlag(M4_DMA1, DmaCh1, TrnCpltIrq); //清除标志位
LCD_CS_SET_HIGH; //拉高片选,结束一次传输
SPI_Cmd(M4_SPI3, Disable); //失能SPI
DMA_ChannelCmd(M4_DMA1, DmaCh1, Disable); //失能DMA1通道
}
//更换到外部16M时钟后,系统启动变得非常慢,而且影响了timer0的时基,配置的是1ms进一次中断,现在变成大概6s。
而驱动TFT屏的速度依然很慢,没有任何改善,估计是没有配置成功,但是对了很多遍代码,实在没觉得哪里有问题。
//下面是我配置外部16M时钟的代码(完全参考官方库函数)
同时我在system_hc32f460jeta.h中也做了如下配置
#define XTAL_VALUE ((uint32_t)16000000) //定义一个16000000晶振
#if !defined (XTAL_VALUE)
#define XTAL_VALUE ((uint32_t)8000000) /*!< External high speed OSC freq. */
#endif
void Set_SysCLK_to_External(void)
{
stc_clk_sysclk_cfg_t stcSysClkCfg;
MEM_ZERO_STRUCT(stcSysClkCfg);
//设置系统时钟分频, MPLL=200M/分频系数,注意:这个分频要满足规定的比例关系,否则不工作,具体参考规格书
stcSysClkCfg.enHclkDiv = ClkSysclkDiv1; //HCLK时钟:200M, 最大200M,用于CPU,SRAM,DMA等
stcSysClkCfg.enExclkDiv = ClkSysclkDiv4; //EXCLK时钟:50M,最大100M,用于SDIO
stcSysClkCfg.enPclk0Div = ClkSysclkDiv2; //PCLK0时钟:100M,最大200M,用于timer6计数时钟
stcSysClkCfg.enPclk1Div = ClkSysclkDiv2; //PCLK1时钟:100M,最大100M,用于SPI, timer0等
stcSysClkCfg.enPclk2Div = ClkSysclkDiv4; //PCLK2时钟:50M,最大60M,用于ADC
stcSysClkCfg.enPclk3Div = ClkSysclkDiv4; //PCLK3时钟:50M,最大50M,用于RTC,I2C等
stcSysClkCfg.enPclk4Div = ClkSysclkDiv8; //PCLK4时钟:25M,最大100M,用于ADC, TRNG等
CLK_SysClkConfig(&stcSysClkCfg);
/*****************************外部振荡器初始化***********************************/
stc_clk_xtal_cfg_t stcXtalCfg; //使用高速外部振荡器
MEM_ZERO_STRUCT(stcXtalCfg); //外部时钟初始化,清零
stcXtalCfg.enMode = ClkXtalModeOsc; //配置外部振荡器作为时钟源,注意振荡器和有源晶振的区别
stcXtalCfg.enDrv = ClkXtalMidDrv; //16M晶振属于中驱动能力
stcXtalCfg.enFastStartup = Enable; //使能快速启动
CLK_XtalConfig(&stcXtalCfg); //配置参数
CLK_XtalCmd(Enable); //使能振荡器
/*****************************外部振荡器初始化***********************************/
/**************************MPLL和SRAM时钟配置***********************************/
stc_clk_mpll_cfg_t stcMpllCfg;
stc_sram_config_t stcSramConfig;
MEM_ZERO_STRUCT(stcMpllCfg);
MEM_ZERO_STRUCT(stcSramConfig);
//SRAM初始化,包括读写/等待周期
stcSramConfig.u8SramIdx = Sram12Idx | Sram3Idx | SramHsIdx | SramRetIdx;
stcSramConfig.enSramRC = SramCycle2;
stcSramConfig.enSramWC = SramCycle2;
stcSramConfig.enSramEccMode = EccMode3; //校验模式选择
stcSramConfig.enSramEccOp = SramNmi; //ECC校验失败不产生NMI中断
stcSramConfig.enSramPyOp = SramNmi; //奇偶校验出错后不产生NMI中断
SRAM_Init(&stcSramConfig); //SRAM初始化
//FLASH读写等待周期设置
EFM_Unlock();
EFM_SetLatency(EFM_LATENCY_5); //配置延迟周期,这个参数和HCLK频率有关系,参考规格书
EFM_Lock();
//MPLL配置:(XTAL / pllmDiv * plln / PllpDiv = 200M) //注意得到的系统PLL频率为PPLq/P/R, 不是400M
stcMpllCfg.pllmDiv = 2ul; //XTAL分频系数:外部是16M晶振,2分频后是8M
stcMpllCfg.plln =50ul; //MPLL倍频系数:50倍频,倍频后为:外部8M x 50=400M
stcMpllCfg.PllpDiv = 2ul; //分频系数p:400M/2=200M
stcMpllCfg.PllqDiv = 2ul; //分频系数q:400M/2=200M
stcMpllCfg.PllrDiv = 2ul; //分频系数r:400M/2=200M
CLK_SetPllSource(ClkPllSrcXTAL); //配置时钟源:外部高速时钟
CLK_MpllConfig(&stcMpllCfg); //配置上述PLL参数
CLK_MpllCmd(Enable); //使能MPLL
/**************************MPLL和SRAM时钟配置***********************************/
PWC_HS2HP();
while(Set != CLK_GetFlagStatus(ClkFlagMPLLRdy)) ;
CLK_SetSysClkSource(CLKSysSrcMPLL); //设置MPLL作为系统时钟源
}
//如下是Timer0定时器,受到外部时钟的影响了,时间变的不对了。配置的是1ms一次中断,用外部时钟后,变成了大概6s了
void TIMER02_INT(void) //通用定时器
{
stc_tim0_base_init_t stcTimerCfg;
stc_irq_regi_conf_t stcIrqRegiConf;
uint32_t u32Pclk1;
stc_clk_freq_t stcClkTmp;
MEM_ZERO_STRUCT(stcTimerCfg);
MEM_ZERO_STRUCT(stcIrqRegiConf);
//获取PCLK1时钟
CLK_GetClockFreq(&stcClkTmp);
u32Pclk1 = stcClkTmp.pclk1Freq;
//使能Timer0的外设时钟线
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIM02, Enable);
//配置Timer02的B通道寄存器参数,同步模式:时基1ms
stcTimerCfg.Tim0_CounterMode = Tim0_Sync;
stcTimerCfg.Tim0_SyncClockSource = Tim0_Pclk1; //PCLK1为100M
stcTimerCfg.Tim0_ClockDivision = Tim0_ClkDiv4; // 4分频是25M,定时器1s运行25M次,1ms运行25000次
stcTimerCfg.Tim0_CmpValue = (uint16_t)(u32Pclk1/4ul/1000ul- 1ul); //1ms
TIMER0_BaseInit(M4_TMR02,Tim0_ChannelB,&stcTimerCfg);
TIMER0_IntCmd(M4_TMR02,Tim0_ChannelB,Enable); //启动通道B中断
stcIrqRegiConf.enIRQn = Int003_IRQn; //中断编号
stcIrqRegiConf.enIntSrc = INT_TMR02_GCMB;
stcIrqRegiConf.pfnCallback = &Timer0B_CallBack; //中断回调函数
enIrqRegistration(&stcIrqRegiConf);
NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIORITY_15);
NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
//启动Timer0
// TIMER0_Cmd(M4_TMR02,Tim0_ChannelA,Enable);
TIMER0_Cmd(M4_TMR02,Tim0_ChannelB,Enable);
}
//如下是main函数
int32_t main(void)
{
//Set_SysCLK_to_External(); //使用外部高速时钟,16Mhz,如果使用外部时钟,系统启动变的非常慢,如果把这个注释掉,系统启动很快
GLOBAL_GPIO_INIT(); //全局GPIO初始化
TIMER02_INT(); //启动TIMER0定时器中断,开启上面的Set_SysCLK_to_External(); 后,定期器中断不对了
LCD_SPI_INIT(); //硬件SPI+DMA初始化
LCD_Init(); // LCD初始化
MAIN_DISPLAY_FIXED(); //这是刷屏函数,不管用外部时钟还是内部时钟,不管用模拟SPI,硬件SPI,还是硬件SPI+DMA,
刷屏的速度都慢的无法忍受,大概5,6s左右
while(1)
{
;
}
}
|
|