目录
1.主函数流程
2.外设初始化
2.1串口初始化
2.2 EDMA初始化
2.3 SPI初始化
3.请求EDMA通道
4.使能
4.1 使能SPI
4.2 写使能
4.1.设置EDMA发送的PaRAM寄存器
4.2.注册EDMA发送的回调函数
4.3.设置EDMA接收的PaRAM寄存器
4.4.注册EDMA接收的回调函数
4.5.assert对应SPI Flash的CSHOLD线
4.6.使能SPI控制器产生DMA事件
4.7.等待回调函数将flagTx或flagRx标志置位1
4.8. deassert对应SPI Flash的CSHOLD线
4.9. 等待SPI Flash允许写
5.读写操作
5.1 擦除SPI Flash
5.2 编程指定页码
5.3 读SPI FLASH
5.4 验证数据
6.参考文献
1.主函数流程
此程序的作用通过EDMA3实现SPI FLASH设备的数据读写功能,检验数据正确与否。主函数流程图如下:
主函数如下:
int main(void)
{
unsigned int retVal = 0;
unsigned char choice;
//初始化串口
UARTStdioInit();
UARTPuts("Welcome to SPI EDMA application.
", -1);
UARTPuts("Here the SPI controller on the SoC communicates with", -1);
UARTPuts(" the SPI Flash present on the SoM.
", -1);
//初始化EDMA
EDMA3Initialize();
//初始化SPI
SPIInitialize();
//请求EDMA通道
RequestEDMA3Channels();
//使能SPI
SPIEnable(SOC_SPI_1_REGS);
//写使能
WriteEnable();
UARTPuts("Do you want to erase a sector of the flash before writing to it ?.", -1);
UARTPuts("
Input y(Y)/n(N) to proceed.
", -1);
choice = UARTGetc();
UARTPutc(choice);
if(('y' == choice) || ('Y' == choice))
{
//擦除
FlashSectorErase();
}
WriteEnable();
//编程指定页码
FlashPageProgram(originalData);
//读
ReadFromFlash(readFlash);
//校验
retVal = verifyData(&originalData[4], &readFlash[4], 256);
if(TRUE == retVal)
{
UARTPuts("
The data in the Flash and the one written ", -1);
UARTPuts("to it are equal.
", -1);
}
else
{
UARTPuts("
The data in the Flash and the one written to it", -1);
UARTPuts(" are not equal.
", -1);
}
while(1);
}
2.外设初始化
外设初始化主要包括三部分:串口、EDMA3、SPI。
2.1串口初始化
串口初始化函数
UARTStdioInit具体细节参考这篇博文:
C6748_UART_EDMA
2.2 EDMA初始化
EDMA初始化函数
EDMA3Initialize如下:
void EDMA3Initialize(void)
{
/* Enabling the PSC for EDMA3CC_0).*/
PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_CC0, PSC_POWERDOMAIN_ALWAYS_ON,
PSC_MDCTL_NEXT_ENABLE);
/* Enabling the PSC for EDMA3TC_0.*/
PSCModuleControl(SOC_PSC_0_REGS, HW_PSC_TC0, PSC_POWERDOMAIN_ALWAYS_ON,
PSC_MDCTL_NEXT_ENABLE);
/* Initialization of EDMA3 */
EDMA3Init(SOC_EDMA30CC_0_REGS, evtQ);
/*
** Enable AINTC to handle interuppts. Also enable IRQ interuppt in
** ARM processor.
*/
SetupInt();
/* Register EDMA3 Interrupts */
ConfigureIntEDMA3();
}
函数先在PSC中使能EDMA3CC_0和EDMA3TC_0,然后对EDMA3进行初始化,PSCModuleControl函数和EDMA3Init函数细节见这篇博文:
C6748_UART_EDMA
然后设置DSP中断,
SetupInt函数如下:
void SetupInt(void)
{
// Initialize DSP interrupt controller
IntDSPINTCInit();
// Enable DSP interrupts globally
IntGlobalEnable();
}
IntDSPINTCInit函数和IntGlobalEnable函数细节见这篇博文:
C6748_SPI_FLASH
最后注册EDMA3中断,ConfigureIntEDMA3函数如下:
void ConfigureIntEDMA3(void)
{
IntRegister(C674X_MASK_INT4, Edma3ComplHandlerIsr);
IntRegister(C674X_MASK_INT5, Edma3CCErrHandlerIsr);
IntEventMap(C674X_MASK_INT4, SYS_INT_EDMA3_0_CC0_INT1);
IntEventMap(C674X_MASK_INT5, SYS_INT_EDMA3_0_CC0_ERRINT);
IntEnable(C674X_MASK_INT4);
IntEnable(C674X_MASK_INT5);
}
函数将Edma3ComplHandlerIsr函数注册为C674X_MASK_INT4的中断服务程序,将Edma3CCErrHandlerIsr函数注册为C674X_MASK_INT5的中断服务程序。然后将SYS_INT_EDMA3_0_CC0_INT1事件(#8)和SYS_INT_EDMA3_0_CC0_ERRINT事件(#56)分别映射到C674X_MASK_INT4和C674X_MASK_INT5。最后使能C674X_MASK_INT4和C674X_MASK_INT5。细节可以参考这篇博文的EDMA3InterruptInit函数:
C6748_UART_EDMA
2.3 SPI初始化
SPI初始化函数SPIInitialize如下:
void SPIInitialize(void)
{
PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_SPI1, PSC_POWERDOMAIN_ALWAYS_ON,
PSC_MDCTL_NEXT_ENABLE);
/* Using SPI1 instance. */
SPIPinMuxSetup(1);
/* Select CS0 of SPI1. The SPI Flash is connected to SPI1_SCS[0]. */
SPI1CSPinMuxSetup(0);
SPISetUp();
}
函数先在PSC中使能SPI,然后设置SPI1引脚为复用功能,选择CS0,SPIPinMuxSetup函数和SPI1CSPinMuxSetup函数细节见这篇博文:
C6748_SPI_FLASH
然后设置SPI,SPISetUp函数如下:
void SPISetUp(void)
{
unsigned char dcs = 0x01;
unsigned int val = SIMO_SOMI_CLK_CS;
/* Resets the SPI */
SPIReset(SOC_SPI_1_REGS);
/* Brings SPI Out-of-Reset */
SPIOutOfReset(SOC_SPI_1_REGS);
/* Configures SPI in Master Mode */
SPIModeConfigure(SOC_SPI_1_REGS, SPI_MASTER_MODE);
/* Sets SPI Controller for 4-pin Mode with Chip Select */
SPIPinControl(SOC_SPI_1_REGS, 0, 0, &val);
/* Configures the Prescale bit in Data Format register. */
SPIClkConfigure(SOC_SPI_1_REGS, 150000000, 10000000,
SPI_DATA_FORMAT0);
/* Chip Select Default Pattern is Set To 1 in SPIDEF Register*/
SPIDefaultCSSet(SOC_SPI_1_REGS, dcs);
/* Configures SPI Data Format Register */
SPIConfigDataFmtReg(SPI_DATA_FORMAT0);
}
设置步骤与C6748_SPI_Flash这篇博文里SPIInit函数基本一致,不同的是参数设置,这里设置预分频系数时,SPI1模块输入时钟为150000000Hz,要获取10000000Hz的SPIclk,(注:SPI1模块输入时钟为什么是150000000Hz,不应该是228000000Hz吗?)其它不再细述。SPIConfigDataFmtReg函数如下:
void SPIConfigDataFmtReg(unsigned long dataFormat)
{
/* Configures the polarity and phase of SPI 1lock */
SPIConfigClkFormat(SOC_SPI_1_REGS,
(SPI_CLK_POL_HIGH | SPI_CLK_INPHASE),
dataFormat);
/* Configures SPI to transmit MSB bit First during data transfer */
SPIShiftMsbFirst(SOC_SPI_1_REGS, dataFormat);
/* Sets the Charcter length */
SPICharLengthSet(SOC_SPI_1_REGS, CHAR_LENGTH, dataFormat);
}
SPIConfigDataFmtReg函数与C6748_SPI_Flash博文里的SPIDataFormatConfig函数是一样的,这里不再细述,可以参考该博文。
3.请求EDMA通道
到这里,就完成了对所有外设的初始化了。在使用EDMA通道传输数据前,需要先请求EDMA通道,
RequestEDMA3Channels函数如下:
void RequestEDMA3Channels(void)
{
/* Request DMA Channel and TCC for SPI Transmit*/
EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,
EDMA3_CHA_SPI1_TX, EDMA3_CHA_SPI1_TX, evtQ);
/* Request DMA Channel and TCC for SPI Receive*/
EDMA3RequestChannel(SOC_EDMA30CC_0_REGS, EDMA3_CHANNEL_TYPE_DMA,
EDMA3_CHA_SPI1_RX, EDMA3_CHA_SPI1_RX, evtQ);
}
EDMA3_CHA_SPI1_TX(19),EDMA3_CHA_SPI1_RX(18),evtQ(0)。函数请求#19和#18DMA通道,这两个DMA通道的事件源分别为SPI1_Transmit和SPI1_Receive,设置evtQ为0,则这两个DMA中断事件都进入队列0处理,设置对应的TCC为19和18,则完成DMA传输后,返回的TCC码分别为19和18,从而将IPR的第19和18位置1,只要DRAE1和IER的对应位置位了,就会产生SYS_INT_EDMA3_0_CC0_INT1(#8)中断。
RequestEDMA3Channels函数细节参考C6748_UART_EDMA博文。
EDMA3RequestChannel函数如下:
unsigned int EDMA3RequestChannel(unsigned int baseAdd,
unsigned int chType,
unsigned int chNum,
unsigned int tccNum,
unsigned int evtQNum)
{
unsigned int retVal = FALSE;
if (chNum < SOC_EDMA3_NUM_DMACH)
{
/* Enable the DMA channel in the enabled in the shadow region
* specific register
*/
EDMA3EnableChInShadowReg(baseAdd, chType, chNum);
EDMA3MapChToEvtQ( baseAdd, chType, chNum, evtQNum);
if (EDMA3_CHANNEL_TYPE_DMA == chType)
{
/* Interrupt channel nums are < 32 */
if (tccNum < SOC_EDMA3_NUM_DMACH)
{
/* Enable the Event Interrupt */
EDMA3EnableEvtIntr(baseAdd, chNum);
retVal = TRUE;
}
HWREG(baseAdd + EDMA3CC_OPT(chNum)) &= EDMA3CC_OPT_TCC_CLR;
HWREG(baseAdd + EDMA3CC_OPT(chNum)) |= EDMA3CC_OPT_TCC_SET(chNum);
}
else if (EDMA3_CHANNEL_TYPE_QDMA== chType)
{
/* Interrupt channel nums are < 8 */
if (tccNum < SOC_EDMA3_NUM_QDMACH)
{
/* Enable the Event Interrupt */
EDMA3EnableEvtIntr(baseAdd, chNum);
retVal = TRUE;
}
HWREG(baseAdd + EDMA3CC_OPT(chNum)) &= EDMA3CC_OPT_TCC_CLR;
HWREG(baseAdd + EDMA3CC_OPT(chNum)) |= EDMA3CC_OPT_TCC_SET(chNum);
}
}
return retVal;
}
该API请求一个逻辑通道(DMA/QDMA/Link),对DMA和QDMA通道,在分配通道的同时会分配TCC和PaRam Set,在分配完EDMA3资源后,会设置OPT PaRAM字的TCC字段为相应的TCC。该API还设置了申请的通道的事件队列(event queue)。SOC_EDMA3_NUM_DMACH(32),如果chNum小于32
,则为有效申请通道号,然后在影子区域使能所申请的DMA通道(通道号为chNum),通道类型chType有两种:DMA和QDMA。EDMA3EnableChInShadowReg函数
如下:
void EDMA3EnableChInShadowReg(unsigned int baseAdd,
unsigned int chType,
unsigned int chNum)
{
/* Allocate the DMA/QDMA channel */
if (EDMA3_CHANNEL_TYPE_DMA == chType)
{
/* FOR TYPE EDMA*/
if(chNum < 32)
{
/* Enable the DMA channel in the DRAE registers */
HWREG(baseAdd + EDMA3CC_DRAE(regionId)) |= (0x01u << chNum);
}
else
{
/* Enable the DMA channel in the DRAEH registers */
HWREG(baseAdd + EDMA3CC_DRAEH(regionId)) |= (0x01u << (chNum - 32));
}
}
else if (EDMA3_CHANNEL_TYPE_QDMA== chType)
{
/* FOR TYPE QDMA */
/* Enable the QDMA channel in the DRAE/DRAEH registers */
HWREG(baseAdd + EDMA3CC_QRAE(regionId)) |= 0x01u << chNum;
}
}
函数根据申请的通道类型是DMA还是QDMA,分别设置DRAE和QRAE。如果是DMA通道,如果通道号小于32,则设置DRAE,因为C6748只有SYS_INT_EDMA3_0_CC0_INT1和SYS_INT_EDMA3_1_CC0_INT1中断,对应的影子区域是1,因此regionID是1,在DRAE1中设置chNum对应的位为1,使能chNum号DMA通道的中断;如果通道号chNum大于32,则设置DRAE1的chNum-32位为1。如果是QDMA通道,则设置QRAE1的chNum位为1。细节参考C6748_UART_EDMA博文。
(指南P524)
(指南P577)
(指南P578)
在影子区域寄存器DRAE1中使能了chNum号DMA通道的中断后,为chNum通道分配事件队列,EDMA3MapChToEvtQ函数如下:
void EDMA3MapChToEvtQ(unsigned int baseAdd,
unsigned int chType,
unsigned int chNum,
unsigned int evtQNum)
{
if (EDMA3_CHANNEL_TYPE_DMA == chType)
{
/* Associate DMA Channel to Event Queue */
HWREG(baseAdd + EDMA3CC_DMAQNUM((chNum) >> 3u)) &=
EDMA3CC_DMAQNUM_CLR(chNum);
HWREG(baseAdd + EDMA3CC_DMAQNUM((chNum) >> 3u)) |=
EDMA3CC_DMAQNUM_SET((chNum), evtQNum);
}
else if (EDMA3_CHANNEL_TYPE_QDMA == chType)
{
/* Associate QDMA Channel to Event Queue */
HWREG(baseAdd + EDMA3CC_QDMAQNUM) |=
EDMA3CC_QDMAQNUM_SET(chNum, evtQNum);
}
}
该函数将chNum号DMA或QDMA通道映射到evtQNum号事件队列。如果是DMA通道,则设置DMAQNUMn的相应位,每个DMAQNUMn对应8个DMA通道事件,DMAQNUM0对应DMA通道0到DMA通道7中断事件,DMAQNUM3对应DMA通道24到DMA通道31中断事件。
(指南P568)
(指南P568)
为chNum通道分配事件队列后,使能事件中断。
EDMA3EnableEvtIntr函数如下:
void EDMA3EnableEvtIntr(unsigned int baseAdd,
unsigned int chNum)
{
if(chNum < 32)
{
/* Interrupt Enable Set Register (IESR) */
HWREG(baseAdd + EDMA3CC_S_IESR(regionId)) |= (0x01u << chNum);
}
else
{
/* Interrupt Enable Set Register (IESRH) */
HWREG(baseAdd + EDMA3CC_S_IESRH(regionId)) |= (0x01u << (chNum - 32));
}
}
函数往IESR的chNum位写1,则IER中的相应位会置1,使能chNum通道中断。
(指南P592)
当IPR中的chNum位为1时,只要IER和DRAE1的chNum位都为1,就会产生
SYS_INT_EDMA3_0_CC0_INT1中断。
在IER中使能了chNum通道中断后,设置PaRAM set的OPT参数的TCC位,每个PaRAM set对应一个DMA通道,前n个PaRAM set对应到前n个DMA通道,这里申请的是通道19和通道18,因此设置的是PaRAM set 19和PaRAM set 18。每个PaRAM set由8个32位的参数(entry)组成,一共占32字节。第一个32位参数是OPT。
(指南P555)
(指南P557)
(指南P501)
(指南P501)
如果chNum通道(DMA或QDMA)对应的PaRAM set(即第chNum个PaRAM set)的OPT参数(channel option parameter)里,TCINTEN和/或ITCINTEN位被置为1,则EDMA3TC(正常传输完成)或EDMA3CC(早完成,early completion)就会返回一个TCC码(completion code on transfer or intermediate transfer completion)。TCC码的值由chNum通道对应的PaRAM set的OPT参数里的TCC位所确定。当EDMA3CC(channel controller)检测到传输完成码TCC(TCC=n),则IPR的相应位(第n位,n=0到31)将会被置为1。
(指南P593)
4.使能
4.1 使能SPI
SPIEnable函数如下:
void SPIEnable(unsigned int baseAdd)
{
HWREG(baseAdd + SPI_SPIGCR1) |= SPI_SPIGCR1_ENABLE;
}
该函数设置SPI模块SPIGCR1(SPI GLOBAL Control Register 1)的ENABLE位为1,使能SPI传输。
(指南P1357 SPIGCR1)
4.2 写使能
使能DSP的SPI之后,就可以开始对spiflash进行操作了,在写spiflash之前,需要先擦除spi flash,擦除spi flash实质上是往扇区写数据,因此需要先发送spi flash写使能指令(0x06),参考这篇博文的3.1节(SPI FLASH写使能):
C6748_SPI_FLASH
WriteEnable函数如下:
void WriteEnable(void)
{
unsigned int buffLength = 1;
volatile char writeEn;
volatile char dummy;
writeEn = SPI_FLASH_WRITE_EN;
/* Configure the PaRAM registers in EDMA for Transmission. */
SpiTxEdmaParamSet(EDMA3_CHA_SPI1_TX, EDMA3_CHA_SPI1_TX, &writeEn, buffLength);
/* Registering Callback Function for Transmission. */
cb_Fxn[EDMA3_CHA_SPI1_TX] = &callback;
/* Configure the PaRAM registers in EDMA for Reception. */
SpiRxEdmaParamSet(EDMA3_CHA_SPI1_RX, EDMA3_CHA_SPI1_RX, &dummy, buffLength, FALSE);
/* Registering Callback Function for Reception. */
cb_Fxn[EDMA3_CHA_SPI1_RX] = &callback;
/* Assert the CSHOLD line corresponding to the SPI Flash. */
CSHoldAssert();
/* Enable SPI controller to generate DMA events */
SPIIntEnable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
/* Wait until both the flags are set to 1 in the callback function. */
while((0 == flagTx) || (0 == flagRx));
flagTx = 0;
flagRx = 0;
/* Deassert the CSHOLD line corresponding to the SPI Flash. */
CSHoldDeassert();
/* Wait until SPI Flash is enabled for writing. */
while (IsWriteEnabled() != TRUE);
}
4.1.设置EDMA发送的PaRAM寄存器
设置EDMA发送中断事件所对应的PaRAM set,该PaRAM set存储信息指明如何利用DMA发送数据。
SpiTxEdmaParamSet函数如下:
void SpiTxEdmaParamSet(unsigned int tccNum, unsigned int chNum,
volatile char *buffer, unsigned int buffLength)
{
EDMA3CCPaRAMEntry paramSet;
unsigned char *p = (unsigned char *)¶mSet;
unsigned int i = 0;
/* Clean-up the contents of structure variable. */
for (i = 0; i < sizeof(paramSet); i++)
{
p[i] = 0;
}
/* Fill the PaRAM Set with transfer specific information. */
/* srcAddr holds address of memory location buffer. */
paramSet.srcAddr = (unsigned int) buffer;
/* destAddr holds address of SPIDAT1 register. */
paramSet.destAddr = (unsigned int) (SOC_SPI_1_REGS + SPI_SPIDAT1);
/* aCnt holds the number of bytes in an array. */
paramSet.aCnt = (unsigned short) 1;
/* bCnt holds the number of such arrays to be transferred. */
paramSet.bCnt = (unsigned short) buffLength;
/* cCnt holds the number of frames of aCnt*bBcnt bytes to be transferred. */
paramSet.cCnt = (unsigned short) 1;
/*
** The srcBidx should be incremented by aCnt number of bytes since the
** source used here is memory.
*/
paramSet.srcBIdx = (short) 1;
/* A sync Transfer Mode is set in OPT.*/
/* srCIdx and destCIdx set to zero since ASYNC Mode is used. */
paramSet.srcCIdx = (short) 0;
/* Linking transfers in EDMA3 are not used. */
paramSet.linkAddr = (unsigned short)0xFFFF;
paramSet.bCntReload = (unsigned short)0;
paramSet.opt = 0x00000000;
/* SAM field in OPT is set to zero since source is memory and memory
pointer needs to be incremented. DAM field in OPT is set to zero
since destination is not a FIFO. */
/* Set TCC field in OPT with the tccNum. */
paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);
/* EDMA3 Interrupt is enabled and Intermediate Interrupt Disabled.*/
paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);
/* Now write the PaRam Set to EDMA3.*/
EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, ¶mSet);
/* EDMA3 Transfer is Enabled. */
EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);
}
函数先清除paramSet结构体,然后将DMA传输的具体信息填入其中。设置DMA数据传输源地址为buffer,buffer为要发送的char数组的源地址。设置目的地址为SPIDAT1,则DMA会将数据从buffer搬移到SPIDAT1中,进而通过SPI总线发送出去,这里因为发送的只是一个字节数据,即写使能指令writeEn,所以buffer数组只有一个元素。设置一次发送的DMA数据的三维,aCnt为DMA传输的一个数组里面的字节数,bCnt为DMA传输的数组数,cCnt为DMA传输的帧数。每传完一个数组,地址应该加aCnt,因此BIDX设为1。因为只传了一帧(frame)数据,因此CIDX设为0。设置OPT的TCC字段为tccNum,此为DMA传输完成后,返回给EDMA3CC的TCC码。设置OPT的TCINTEN位为1,使能transfer complete interrupt,则当DMA数据传输完成后,IPR的tccNum位就会置1。
(指南P556)
设置完PaRAM set后,调用EDMA3SetPaRAM函数将其写至EDMA3的PaRAM, 然后使能EDMA3传输,设置启动DMA传输的触发模式为事件触发,则当#19DMA通道对应的事件(SPI1_TX,当SPI1的TXBUF为空时发生)和#18DMA通道对应的事件(SPI1_RX,当SPI1的RXBUF非空时发生)对应的事件发生时,就会触发EDMA3传输。SPI中断如何产生参考这篇博文的
SPIIntEnable函数:
C6748_SPI_FLASH
(手册P102)
(指南P1362)
(指南P1359)
4.2.注册EDMA发送的回调函数
注册EDMA发送的回调函数为callback函数,
cb_Fxn[]为回调函数指针数组,回调函数指针
cb_Fxn[EDMA3_CHA_SPI1_TX]指向callback函数,回调函数
callback在EDMA3传输完成中断(
SYS_INT_EDMA3_0_CC0_INT1)处理程序Edma3ComlHandlerIsr中调用,当DMA数据发送完成后,产生
SYS_INT_EDMA3_0_CC0_INT1中断,然后程序进入该中断的服务程序Edma3ComlHandlerIsr中,在该中断服务程序中调用回调函数,此时cb_Fxn[index]指向的是callback,因此调用的是callback函数。细节参考这篇博文的
Edma3ComplHandlerIsr函数:
C6748_UART_EDMA
Callback函数如下:
void callback(unsigned int tccNum, unsigned int status)
{
if(tccNum == 19)
{
flagTx = 1;
/* Disable SPI-EDMA Communication. */
SPIIntDisable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
}
else if(tccNum == 18)
{
flagRx = 1;
/* Disable SPI-EDMA Communication. */
SPIIntDisable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
}
}
在回调函数中,根据是EDMA发送还是EDMA接收分别设置flagTx标志和flagRx标志,然后设置SPIINT0的DMAREQEN位(16位)为0,禁用SPI的DMA请求,则SPI1模块将不会向EDMA发送EDMA请求。
(指南P1359)
4.3.设置EDMA接收的PaRAM寄存器
设置EDMA接收中断事件所对应的PaRAM set,该PaRAM set存储信息指明如何利用DMA发送数据。
SpiRxEdmaParamSet函数如下:
void SpiRxEdmaParamSet(unsigned int tccNum, unsigned int chNum,
volatile char *buffer, unsigned int length,
unsigned int destBidxFlag)
{
EDMA3CCPaRAMEntry paramSet;
unsigned char *p = (unsigned char *)¶mSet;
unsigned int i = 0;
/* Clean-up the contents of structure variable. */
for (i = 0; i < sizeof(paramSet); i++)
{
p[i] = 0;
}
/* Fill the PaRAM Set with Receive specific information. */
/* srcAddr holds address of SPI Rx FIFO. */
paramSet.srcAddr = (unsigned int) (SOC_SPI_1_REGS + SPI_SPIBUF);
/* destAddr is address of memory location named buffer. */
paramSet.destAddr = (unsigned int) buffer;
/* aCnt holds the number of bytes in an array. */
paramSet.aCnt = (unsigned short) 1;
/* bCnt holds the number of such arrays to be transferred. */
paramSet.bCnt = (unsigned short) length;
/* cCnt holds the number of frames of aCnt*bBcnt bytes to be transferred. */
paramSet.cCnt = (unsigned short) 1;
/* The srcBidx should not be incremented since it is a h/w register. */
paramSet.srcBIdx = 0;
if(TRUE == destBidxFlag)
{
/* The destBidx should be incremented for every byte. */
paramSet.destBIdx = 1;
}
else
{
/* The destBidx should not be incremented. */
paramSet.destBIdx = 0;
}
/* A sync Transfer Mode. */
/* srCIdx and destCIdx set to zero since ASYNC Mode is used. */
paramSet.srcCIdx = 0;
paramSet.destCIdx = 0;
/* Linking transfers in EDMA3 are not used. */
paramSet.linkAddr = (unsigned short)0xFFFF;
paramSet.bCntReload = 0;
paramSet.opt = 0x00000000;
/* Set TCC field in OPT with the tccNum. */
paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);
/* EDMA3 Interrupt is enabled and Intermediate Interrupt Disabled.*/
paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);
/* Now write the PaRam Set to EDMA3.*/
EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, ¶mSet);
/* EDMA3 Transfer is Enabled. */
EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);
}
函数将源地址设为SPIBUF(SPI的RX FIFO),目标地址设为内存里的buffer数组地址,EDMA将SPI模块所接收到的SPI flash返回的字节数据写到buffer数组(dummy变量,从名字可以判断返回的字节没有用)中。各维度的设置与EDMA发送的设置函数一样,设置
destBidxFlag为FALSE,则BIdx不增加,即传输的源地址一直为SPIBUF。
4.4.注册EDMA接收的回调函数
注册EDMA接收的回调函数为callback函数,当EDMA接收中断发生后,在中断服务程序
Edma3ComplHandlerIsr中将会调用该函数。
4.5.assert对应SPI Flash的CSHOLD线
CSHoldAssert()函数如下:
void CSHoldAssert(void)
{
SPIDat1Config(SOC_SPI_1_REGS, (SPI_CSHOLD | SPI_DATA_FORMAT0), 0x01);
}
函数设置SPIFMT0的CSHOLD位为1,DFSEL位为SPI_DATA_FORMAT0(0),
SPIDat1Config函数参考这篇博文的2.6第七步。该函数
(指南P1351)
4.6.使能SPI控制器产生DMA事件
SPIIntEnable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT)函数如下:
void SPIIntEnable(unsigned int baseAdd, unsigned int flag)
{
HWREG(baseAdd + SPI_SPIINT0) |= flag;
}
函数设置SPIINT0的DMAREQEN位(16位)为1,使能SPI的DMA请求。
4.7.等待回调函数将flagTx或flagRx标志置位1
while((0 == flagTx) || (0 == flagRx));
flagTx = 0;
flagRx = 0;
4.8. deassert对应SPI Flash的CSHOLD线
CSHoldDeassert函数如下:
void CSHoldDeassert(void)
{
SPIDat1Config(SOC_SPI_1_REGS, SPI_DATA_FORMAT0, 0x01);
}
设置SPIDAT1的CSHOLD位为0,则CS线被deactivate。
4.9. 等待SPI Flash允许写
while (IsWriteEnabled() != TRUE);
;IsWriteEnabled函数如下:
unsigned int IsWriteEnabled(void)
{
volatile unsigned char temp = 0;
unsigned int retVal = FALSE;
/* Reading the Status Register of SPI Flash. */
temp = FlashStatusRead();
if (temp & WRITE_ENABLE_LATCH)
{
retVal = TRUE;
}
return retVal;
}
该函数中读取SPI Flash的状态寄存器,然后判断是否为0x02,是则说明允许锁存(即允许写),就返回TURE,否则返回FLASE。
FlashStatusRead函数如下:
unsigned char FlashStatusRead(void)
{
volatile char writeFlash[2] = {0};
volatile char readFlash[2] = {0};
unsigned int buffLength = 0;
writeFlash[0] = SPI_FLASH_STATUS_RX;
writeFlash[1] = 0;
buffLength = 2;
/* Configure the PaRAM registers in EDMA for Transmission.*/
SpiTxEdmaParamSet(EDMA3_CHA_SPI1_TX, EDMA3_CHA_SPI1_TX, writeFlash, buffLength);
/* Registering Callback Function for Transmission. */
cb_Fxn[EDMA3_CHA_SPI1_TX] = &callback;
/* Configure the PaRAM registers in EDMA for Reception. */
SpiRxEdmaParamSet(EDMA3_CHA_SPI1_RX, EDMA3_CHA_SPI1_RX, readFlash, buffLength, TRUE);
/* Registering Callback Function for Reception. */
cb_Fxn[EDMA3_CHA_SPI1_RX] = &callback;
/* Assert the CSHOLD line corresponding to the SPI Flash. */
CSHoldAssert();
/* Enable SPI controller to generate DMA events */
SPIIntEnable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
/* Wait until both the flags are set to 1 in the callback function. */
while((0 == flagTx) || (0 == flagRx));
flagTx = 0;
flagRx = 0;
/* Deassert the CSHOLD line corresponding to the SPI Flash. */
CSHoldDeassert();
return ((unsigned char)readFlash[1]);
}
步骤与写使能是一样的,不过最后一步返回的是读取到的第二字节,详细可参考这篇博文的3.2擦除SPI FLASH的StatusGet函数。
C6748_SPI_FLASH
5.读写操作
5.1 擦除SPI Flash
FlashSectorErase函数如下:
void FlashSectorErase(void)
{
volatile char sectorErase[4] = {0};
unsigned int buffLength = 4;
volatile char dummy;
sectorErase[0] = SPI_FLASH_SECTOR_ERASE;
sectorErase[1] = SPI_FLASH_ADDR_MSB1;
sectorErase[2] = SPI_FLASH_ADDR_MSB0;
sectorErase[3] = SPI_FLASH_ADDR_LSB;
/* Configure the PaRAM registers in EDMA for Transmission. */
SpiTxEdmaParamSet(EDMA3_CHA_SPI1_TX, EDMA3_CHA_SPI1_TX, sectorErase, buffLength);
/* Registering Callback Function for Transmission. */
cb_Fxn[EDMA3_CHA_SPI1_TX] = &callback;
/* Configure the PaRAM registers in EDMA for Reception. */
SpiRxEdmaParamSet(EDMA3_CHA_SPI1_RX, EDMA3_CHA_SPI1_RX, &dummy, buffLength, FALSE);
/* Registering Callback Function for Reception. */
cb_Fxn[EDMA3_CHA_SPI1_RX] = &callback;
/* Assert the CSHOLD line corresponding to the SPI Flash. */
CSHoldAssert();
/* Enable SPI controller to generate DMA events */
SPIIntEnable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
/* Wait until both the flags are set to 1 in the callback function. */
while((0 == flagTx) || (0 == flagRx));
flagTx = 0;
flagRx = 0;
/* Deassert the CSHOLD line corresponding to the SPI Flash. */
CSHoldDeassert();
/* Wait until the previous write to the SPI Flash if completed. */
IsWriteInProgress();
}
前面的设置步骤与4.2写使能基本一致,只是EDMA发送的数组稍有区别,这次是4个字节(一字节指令+三字节地址),可以参考C6748_SPI_Flash的3.2节擦除SPI FLASH。当把4个字节写往SPI Flash后,等待该SPI Flash写操作完成,函数
IsWriteInProgress如下:
void IsWriteInProgress(void)
{
volatile unsigned char temp = 0;
do
{
temp = FlashStatusRead();
}while(temp & WRITE_IN_PROGRESS);
}
函数读取spi flash的status寄存器,判断是否为WRITE_IN_PROGRESS(0x01),如果是,说明还在写操作中,则继续读,直到不是。
5.2 编程指定页码
函数
FlashPageProgram(originalData)(orginalData为260个volatile char元素的数组,为要写往SPI FLASH的数组)如下:
void FlashPageProgram(volatile char *pVrf_Data)
{
volatile char pageProgram[260] = {0};
unsigned int buffLength = 0;
unsigned int index = 0;
volatile char dummy;
pageProgram[0] = SPI_FLASH_PAGE_WRITE;
pageProgram[1] = SPI_FLASH_ADDR_MSB1;
pageProgram[2] = SPI_FLASH_ADDR_MSB0;
pageProgram[3] = SPI_FLASH_ADDR_LSB;
for(index = 4; index < 260; index++)
{
pageProgram[index] = 259 - index;
pVrf_Data[index] = 259 - index;
}
buffLength = index;
/* Configure the PaRAM registers in EDMA for Transmission.*/
SpiTxEdmaParamSet(EDMA3_CHA_SPI1_TX, EDMA3_CHA_SPI1_TX, pageProgram, buffLength);
/* Registering Callback Function for Transmission. */
cb_Fxn[EDMA3_CHA_SPI1_TX] = &callback;
/* Configure the PaRAM registers in EDMA for Reception.*/
SpiRxEdmaParamSet(EDMA3_CHA_SPI1_RX, EDMA3_CHA_SPI1_RX, &dummy, buffLength, FALSE);
/* Registering Callback Function for Reception. */
cb_Fxn[EDMA3_CHA_SPI1_RX] = &callback;
/* Assert the CSHOLD line corresponding to the SPI Flash. */
CSHoldAssert();
/* Enable SPI controller to generate DMA events */
SPIIntEnable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
/* Wait until both the flags are set to 1 in the callback function. */
while((0 == flagTx) || (0 == flagRx));
flagTx = 0;
flagRx = 0;
/* Deassert the CSHOLD line corresponding to the SPI Flash. */
CSHoldDeassert();
/* Wait until the previous write to the SPI Flash if completed. */
IsWriteInProgress();
}
写spi flash可以参考C6748_SPI_Flash的3.3节写SPI flash。
5.3 读SPI FLASH
读SPI FLASH函数
ReadFromFlash(readFlash)(readFlash为260字节volatile char元素数组,为读取到的260字节spi flash数据所存储的数组)如下:
void ReadFromFlash(volatile char *pReadFlash)
{
volatile char writeFlash[260] = {0};
unsigned int buffLength = 0;
unsigned int index = 0;
writeFlash[0] = SPI_FLASH_READ;
writeFlash[1] = SPI_FLASH_ADDR_MSB1;
writeFlash[2] = SPI_FLASH_ADDR_MSB0;
writeFlash[3] = SPI_FLASH_ADDR_LSB;
for(index = 4; index < 260; index++)
{
writeFlash[index] = 0;
}
buffLength = index;
/* Configure the PaRAM registers in EDMA for Transmission.*/
SpiTxEdmaParamSet(EDMA3_CHA_SPI1_TX, EDMA3_CHA_SPI1_TX, writeFlash, buffLength);
/* Registering Callback Function for Transmission. */
cb_Fxn[EDMA3_CHA_SPI1_TX] = &callback;
/* Configure the PaRAM registers in EDMA for Reception.*/
SpiRxEdmaParamSet(EDMA3_CHA_SPI1_RX, EDMA3_CHA_SPI1_RX, pReadFlash, buffLength, TRUE);
/* Registering Callback Function for Reception. */
cb_Fxn[EDMA3_CHA_SPI1_RX] = &callback;
/* Assert the CSHOLD line corresponding to the SPI Flash. */
CSHoldAssert();
/* Enable SPI controller to generate DMA events */
SPIIntEnable(SOC_SPI_1_REGS, SPI_DMA_REQUEST_ENA_INT);
/* Wait until both the flags are set to 1 in the callback function. */
while((0 == flagTx) || (0 == flagRx));
flagTx = 0;
flagRx = 0;
/* Deassert the CSHOLD line corresponding to the SPI Flash. */
CSHoldDeassert();
}
5.4 验证数据
验证所写的256字节数据和读出的256字节是否一样,分别打印提示信息。
6.参考文献
[1]
SPI协议(上)—— 基础介绍
[2]
SPI协议(中)—— 基础介绍
[3]
SPI协议(下)—— 基础介绍