一共有2组SPI,频率范围为f bus /512到f bus /2,以默认的48M的fbus频率为例,SPI的CLK频率范围为96K到24M。AC78xx的SPIM CS可以由硬件控制,这点和STM32不同。
1. 相应的SPIM的IO配置 SPI1的IO如下: #define SPI1_CS_PIN (GPIO_PA0) #define SPI1_SCK_PIN (GPIO_PA1) #define SPI1_MISO_PIN (GPIO_PA2) #define SPI1_MOSI_PIN (GPIO_PA3) GPIO_SetFunc(SPI1_CS_PIN, GPIO_FUNC_1); GPIO_SetFunc(SPI1_SCK_PIN, GPIO_FUNC_1); GPIO_SetFunc(SPI1_MISO_PIN, GPIO_FUNC_1); GPIO_SetFunc(SPI1_MOSI_PIN, GPIO_FUNC_1);
SPI2的IO如下: #define SPI2_CS_PIN (GPIO_PB11) #define SPI2_SCK_PIN (GPIO_PB12) #define SPI2_MISO_PIN (GPIO_PB13) #define SPI2_MOSI_PIN (GPIO_PB14) GPIO_SetFunc(SPI2_CS_PIN, GPIO_FUNC_1); GPIO_SetFunc(SPI2_SCK_PIN, GPIO_FUNC_1); GPIO_SetFunc(SPI2_MISO_PIN, GPIO_FUNC_1); GPIO_SetFunc(SPI2_MOSI_PIN, GPIO_FUNC_1);
2. 初始化SPI 初始化函数原型: int32_t SPI_Initialize(SPI_Type *SPIx,SPI_ConfigType *config) SPIx对应SPI1,SPI2,参数config的结构原型如下: typedef struct { SPI_SettingType setting; uint8_t sckHigh; uint8_t sckLow; uint8_t csHold; uint8_t csSetup; } SPI_ConfigType, *SPI_ConfigPtr; sckHigh和sckLow分别定义SCK的高低电平用几个总线时钟,0表示一个时钟,同时也能得到SPI的频率,计算公式如下: f SCL =f bclk /(SCK_LOW+1+SCK_HIGH+1) csHold是通信结束后CS维持的时间(以总线时钟为单位)。 csSetup是通信开始时(CS拉低后)多久时间(以总线时钟为单位)后SCK才开始变化。 参数setting的结构原型如下: typedef struct { uint32_t csIdle :8; ///< CS Idle Time uint32_t txEmptyIntEn :1; uint32_t rxFullIntEn :1; uint32_t txUnderflowIntEn :1; uint32_t rxOverflowIntEn :1; uint32_t master :1; ///< 1:Master0:Slave uint32_t modeErrorIntEn :1; uint32_t dmaTxEn :1; uint32_t dmaRxEn :1; uint32_t cpol :1; uint32_t cpha :1; uint32_t txMsbFirst :1; ///< TX MSB First uint32_t rxMsbFirst :1; ///< RX MSB First uint32_t frmSize :4; uint32_t csOutputEn :1; ///< CS HardwareOutput Enable uint32_t modeFaultEn :1; uint32_t continuousCSEn :1; ///< CS Continuous Output Enable uint32_t wakeUpEn :1; uint32_t spiEn :1; uint32_t swReset :1; uint32_t intEn :1; uint32_t :1; }SPI_SettingType; csIdle是通信结束后(CS拉高)保持高电平的时间。 sckHigh/sckLow/csHold/csSetup/csIdle的关系可以参考下图理解 Master表示SPI是使用Master还是Slave; dmaTxEn表示DMA发送使能; dmaRxEn表示DMA接收使能; cpol和cpoh组合表示SPI的模式; txMsbFirst表示发送是高位在前还是低位在前; rxMsbFirst表示接收是高位在前还是低位在前;
frmSize设置帧大小,支持4-16bits,值的范围为0-15,一般为7; csOutputEn使能CS是否由硬件控制输出; modeFaultEn使能主机模式故障检测,1: 使能多主机检测功能; continuousCSEn 使能CS 连续输出,1: CS 输出连续; wakeUpEn使能从机唤醒功能; spiEn初始化SPI; intEn使能SPI中断; txEmptyIntEn/ rxFullIntEn/ txUnderflowIntEn/rxOverflowIntEn/ modeErrorIntEn/ swReset 未使用;
一组初始化例程: spiConfig.sckHigh= 0; //24MHz spiConfig.sckLow = 0; spiConfig.setting.master= ENABLE; //Master spiConfig.setting.intEn =DISABLE; //Disable interrupt spiConfig.setting.txMsbFirst = ENABLE; //tx MSB spiConfig.setting.rxMsbFirst = ENABLE; //rx MSB spiConfig.setting.csOutputEn = DISABLE; //Disable CS hardware output spiConfig.setting.continuousCSEn = DISABLE; //No need if use GPIOcontrols CS spiConfig.setting.frmSize = 7; //frame size is 8bit spiConfig.setting.cpha = 0; //SPI Mode 0 spiConfig.setting.cpol = 0; spiConfig.setting.spiEn = ENABLE;
SPI_Initialize(SPIx, &spiConfig); 3. 读写函数 SPI是全双工的,对应的API函数是SPI_TransferPoll,其函数原型为: int32_t SPI_TransferPoll(SPI_Type *SPIx,uint16_t *rdBuff, uint16_t *wrBuff, uint32_t length) 返回值0表示成功,其他则表示错误。
这个函数要求rdBuff和wrBuff都不能为NULL,而实际上一般应用只会把SPI当作半双工,所以修改一下库函数,另外,这个API函数的rdbuf和wrbuf都是16bit的,一般是用8bit的,需要改成8 bit的方式,干脆不调用库函数,根据库函数自己建立一个API函数 void spimTransferBytes(uint8_t port,uint8_t* wrBuf, uint8_t* rdBuf, uint16_t len) { //SPI_TransferPoll(spimGroup[port], rdBuf, wrBuf, (uint32_t)len); SPI_Type* SPIx = spimGroup[port]; uint32_t i = 0;
if (!len) { return; }
for (i = 0; i < len; i++) { while (!SPI_IsTxEF(SPIx)); if(wrBuf == NULL) SPI_WriteDataReg(SPIx, 0xff); else SPI_WriteDataReg(SPIx, wrBuf); while (!SPI_IsRxFF(SPIx)); if(rdBuf == NULL) SPI_ReadDataReg(SPIx); else rdBuf = SPI_ReadDataReg(SPIx); } while ((SPI_IsBusy(SPIx))); SPI_CSRelease(SPIx); } 使用SPI接口的彩屏驱动芯片验证SPI Master功能已经能正常显示。 SPI使用SPI2,频率为24MHz,CS脚用软件控制(即初始化csOutputEn= DISABLE) 利用驱动芯片内部1MB的RAM测试一下SPI的通信效率,测试代码如下: uint8_t buffer[1024 * 8] = {0}; uint16_t len = 1024 * 8, i; uint32_t offset = 0; for(i = 0; i < len; i++) { buffer = (i& 0xff); } gTimerDelayCount = 1000; //10 seconds for(i = 0; i < (1024 * 1024) / len; i++) { ft8xxWrMemBuf(RAM_G+ offset, buffer, len); offset += len; } Printf("SPI write time:%d(ms)\n",((1000 - gTimerDelayCount) * TIMER_MS));
gTimerDelayCount是在一个10ms的时间中断中不停的减一。验证结果如下:
|