C6748_SPI_FLASH

来源:互联网 发布:千元显示器推荐知乎 编辑:程序博客网 时间:2024/06/05 12:42

1.主函数流程

此程序的作用是实现SPI FLASH设备的数据读写功能,此设备使用SPI1总线CS0片选。主函数流程图如下:

主函数如下:

intmain(void)

{

    PSCInit();

 

    GPIOBankPinMuxSet();

 

    char choice;

 

//初始化串口终端使用串口2

UARTStdioInit();

 

UARTPuts("Tronlong SPI Flash Application......\r\n",-1);

 

//管脚复用配置

GPIOBankPinMuxSet();

 

// DSP中断初始化

InterruptInit();

 

// SPI中断初始化

SPIInterruptInit();

 

// SPI初始化

SPIInit();

 

//写使能

WriteEnable();

     

    UARTPuts("Do you want to erase a sector of the flash before writing to it ?.", -1);

UARTPuts("\r\nInput y(Y)/n(N) to proceed.\r\n",-1);

 

choice=UARTGetc();

UARTPutc(choice);

 

if(('y'== choice) || ('Y'== choice))

{

//擦除 Flash

SectorErase();

}

 

WriteEnable();

// Flash

WritetoFlash();

// Flash

ReadFromFlash();

 

//数据校验

VerifyData();

 

for(;;)

{

 

}

}

主函数中,先对各相关外设作初始化,然后再执行具体操作。

2.初始化

2.1 PSC初始化

首先进行PSC初始化,在PSC1中使能SPI1模块。PSC初始化函数PSCInit()如下:

voidPSCInit(void)

{

    //对相应外设模块的使能也可以在 BootLoader中完成

//使能 SPI模块

PSCModuleControl(SOC_PSC_1_REGS, HW_PSC_SPI1, PSC_POWERDOMAIN_ALWAYS_ON, PSC_MDCTL_NEXT_ENABLE);

}

该函数中调用了PSCModuleControl函数,PSCModuleControl函数作用是将所要请求的模块(requested module)设置到所需要的状态(required state),这里是把SPI1模块设置为使能态,即使能SPI1SPI1模块由PSC1管理,其LPSCID10,如图所示:

(指南P163

PSC1的寄存器(regs)基地址为0x01E2 7000,如图所示:

(手册P97

PSCModuleControl函数具体执行细节可以参考这篇博文:

C6748_UART_EDMA

2.2 UART初始化

第二步,初始化串口终端,使用串口2。函数UARTStdioInit();该函数在demo\StarterWare\Source\StarterWare\Utils路径下工程文件里的uartStdio.c程序中,函数具体细节可以参考这篇博文。

C6748_UART_EDMA

2.3 管脚复用配置

第三步,将GPIO引脚功能设为复用功能,GPIOBankPinMuxSet();函数如下:

voidGPIOBankPinMuxSet(void)

{

    SPIPinMuxSetup(1);

    SPI1CSPinMuxSetup(0);

}

SPIPinMuxSetup函数如下:

voidSPIPinMuxSetup(unsignedint instanceNum)

{

unsignedint savePinMux=0;

 

if(0== instanceNum)

{

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(3))&\

~(SYSCFG_PINMUX3_PINMUX3_3_0|\

SYSCFG_PINMUX3_PINMUX3_15_12|\

SYSCFG_PINMUX3_PINMUX3_11_8|\

SYSCFG_PINMUX3_PINMUX3_7_4);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(3))=\

(PINMUX3_SPI0_CLK_ENABLE| PINMUX3_SPI0_SIMO_ENABLE|\

PINMUX3_SPI0_SOMI_ENABLE| PINMUX3_SPI0_ENA_ENABLE|\

savePinMux);

 

}

elseif(1== instanceNum)

{

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5))&\

~(SYSCFG_PINMUX5_PINMUX5_11_8|\

SYSCFG_PINMUX5_PINMUX5_23_20|\

SYSCFG_PINMUX5_PINMUX5_19_16|\

SYSCFG_PINMUX5_PINMUX5_15_12);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5))=\

(PINMUX5_SPI1_CLK_ENABLE| PINMUX5_SPI1_SIMO_ENABLE|\

PINMUX5_SPI1_SOMI_ENABLE| PINMUX5_SPI1_ENA_ENABLE|\

savePinMux);

}

else

{

 

}

 

}

该函数根据要启用的是SPI0还是SPI1通道,分别设置不同的PINMUX寄存器。如果是SPI0,则要设置的是PINMUX315_0位;如果是SPI1,则要设置的是PINMUX523_8位。所有的PINMUX寄存器都在系统配置模块0SYSCFG0)中,因此需要以SYSCFG0模块的地址为基地址,再加上偏移地址找到PINMUXn寄存器。

这里启用的是SPI1通道,因此设置的是PINMUX5。函数先将与SPI1相关的字段,即23_8位清零,同时保存其它无关的位,然后设置23_8位为SPI1四个引脚SPI1_CLKSPI1_SIMOSPI1_SOMISPI1_ENA对应的值。

SPI1CSPinMuxSetup函数如下:

voidSPI1CSPinMuxSetup(unsignedint csPinNum)

{

unsignedint spi1CSPinMux=0;

unsignedint savePinMux=0;

 

switch(csPinNum)

{

case0:

spi1CSPinMux= (SYSCFG_PINMUX5_PINMUX5_7_4_NSPI1_SCS0<<\

SYSCFG_PINMUX5_PINMUX5_7_4_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5))&\

~(SYSCFG_PINMUX5_PINMUX5_7_4);

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5))=\

(spi1CSPinMux| savePinMux);

 

break;

 

case1:

spi1CSPinMux= (SYSCFG_PINMUX5_PINMUX5_3_0_NSPI1_SCS1<<\

SYSCFG_PINMUX5_PINMUX5_3_0_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5))&\

~(SYSCFG_PINMUX5_PINMUX5_3_0);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(5))=\

(spi1CSPinMux| savePinMux);

 

break;

 

case2:

spi1CSPinMux= (SYSCFG_PINMUX4_PINMUX4_31_28_NSPI1_SCS2<<\

SYSCFG_PINMUX4_PINMUX4_31_28_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))&

~(SYSCFG_PINMUX4_PINMUX4_31_28);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))=\

(spi1CSPinMux| savePinMux);

 

break;

 

case3:

spi1CSPinMux= (SYSCFG_PINMUX4_PINMUX4_27_24_NSPI1_SCS3<<\

SYSCFG_PINMUX4_PINMUX4_27_24_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))&\

~(SYSCFG_PINMUX4_PINMUX4_27_24);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))=\

(spi1CSPinMux| savePinMux);

 

break;

 

case4:

spi1CSPinMux= (SYSCFG_PINMUX4_PINMUX4_23_20_NSPI1_SCS4<<\

SYSCFG_PINMUX4_PINMUX4_23_20_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))&\

~(SYSCFG_PINMUX4_PINMUX4_23_20);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))=\

(spi1CSPinMux| savePinMux);

 

break;

 

case5:

spi1CSPinMux= (SYSCFG_PINMUX4_PINMUX4_19_16_NSPI1_SCS5<<\

SYSCFG_PINMUX4_PINMUX4_19_16_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))&\

~(SYSCFG_PINMUX4_PINMUX4_19_16);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))=\

(spi1CSPinMux| savePinMux);

 

break;

 

case6:

spi1CSPinMux= (SYSCFG_PINMUX4_PINMUX4_15_12_NSPI1_SCS6<<\

SYSCFG_PINMUX4_PINMUX4_15_12_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))&\

~(SYSCFG_PINMUX4_PINMUX4_15_12);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))=\

(spi1CSPinMux| savePinMux);

 

break;

 

case7:

spi1CSPinMux= (SYSCFG_PINMUX4_PINMUX4_11_8_NSPI1_SCS7<<\

SYSCFG_PINMUX4_PINMUX4_11_8_SHIFT);

 

savePinMux=HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))&\

~(SYSCFG_PINMUX4_PINMUX4_11_8);

 

HWREG(SOC_SYSCFG_0_REGS+SYSCFG0_PINMUX(4))=\

(spi1CSPinMux| savePinMux);

 

break;

 

default:

break;

 

}

 

}

该函数设置PINMUX寄存器相关字段为SPI1通道片选引脚SPI1_SCSn的值,SPI1通道可以外接多个SPI外设,每个SPI外设由一个片选信号SCSn控制,这里设置的SPI1_SCS0引脚。

PINMUX5寄存器内容如下:

(指南P228

2.4 DSP中断初始化

第四步,对DSP中断初始化。InterruptInit函数如下:

voidInterruptInit(void)

{

 

    //初始化 DSP中断控制器

    IntDSPINTCInit();

 

    //使能 DSP全局中断

    IntGlobalEnable();

}

DSP中断初始化函数中,先初始化DSP中断控制器,再使能DSP全局中断。DSP中断控制器初始化函数IntDSPINTCInitStarterWareinterrupt.c文件中,路径为\demo\StarterWare\Source\StarterWare\SystemConfigIntDSPINTCInit函数如下:

voidIntDSPINTCInit (void)

{

unsignedint step=0;

 

/* Set ISRs to default "do-nothing" routine */

while(step!= C674X_INT_COUNT)

c674xISRtbl[step++]= IntDefaultHandler;

 

/* Set interrupt service table pointer to the vector table */

#ifdef__TI_EABI__

ISTP= (unsignedint)_intcVectorTable;

#else

ISTP= (unsignedint)intcVectorTable;

#endif

 

/* Clear pending CPU maskable interrupts (if any) */

ICR=0xFFF0;

 

/* Enable NMIE bit to allow CPU maskable interrupts */

IER= (1<< C674X_NMI);

}

IntDSPINTCInit函数设置DSP中断控制器(INTC),在使用DSP INTC前应该要先调用该API。该API清除所有可屏蔽中断标志位(INT4-INT15),

TMS320C674x DSP CPU and Instruction Set Reference Guide P41

然后设置IER寄存器的NMIE位,允许可屏蔽中断被使能。

TMS320C674x DSP CPU and Instruction Set Reference Guide P42

函数将ISTPInterrupt Service Table Pointer)指向intcVectorTableintcVectorTable是一张向量表,记录了CPU中断服务程序的入口地址,各向量对应一个CPU中断。该向量表在intvecs.asm文件中,路径同interrupt.c文件一样,内容如下:

    .global _intcVectorTable

    .global _c_int00

    .global _c674x_nmi_isr

    .global _c674x_rsvd_int2_isr

    .global _c674x_rsvd_int3_isr

    .global _c674x_mask_int4_isr

    .global _c674x_mask_int5_isr

    .global _c674x_mask_int6_isr

    .global _c674x_mask_int7_isr

    .global _c674x_mask_int8_isr

    .global _c674x_mask_int9_isr

    .global _c674x_mask_int10_isr

    .global _c674x_mask_int11_isr

    .global _c674x_mask_int12_isr

    .global _c674x_mask_int13_isr

    .global _c674x_mask_int14_isr

    .global _c674x_mask_int15_isr

 

;**********************************************************

;               Interrupt Fetch Packet

;**********************************************************

VEC_ENTRY .macro addr

    STW B0,*--B15

    MVKL addr,B0

    MVKH addr,B0

    B B0

    LDW*B15++,B0

    NOP2

    NOP

    NOP

    .endm

 

;**********************************************************

;               Interrupt Vector Table

;**********************************************************

    .align1024

_intcVectorTable:

    VEC_ENTRY _c_int00

    VEC_ENTRY _c674x_nmi_isr

    VEC_ENTRY _c674x_rsvd_int2_isr

    VEC_ENTRY _c674x_rsvd_int3_isr

    VEC_ENTRY _c674x_mask_int4_isr

    VEC_ENTRY _c674x_mask_int5_isr

    VEC_ENTRY _c674x_mask_int6_isr

    VEC_ENTRY _c674x_mask_int7_isr

    VEC_ENTRY _c674x_mask_int8_isr

    VEC_ENTRY _c674x_mask_int9_isr

    VEC_ENTRY _c674x_mask_int10_isr

    VEC_ENTRY _c674x_mask_int11_isr

    VEC_ENTRY _c674x_mask_int12_isr

    VEC_ENTRY _c674x_mask_int13_isr

    VEC_ENTRY _c674x_mask_int14_isr

    VEC_ENTRY _c674x_mask_int15_isr

intcVectorTable表所有向量的入口函数在interrupt.c文件中,如下:

#ifdef__TI_EABI__

interruptvoid_c674x_nmi_isr (void)

#else

interruptvoidc674x_nmi_isr (void)

#endif

{

c674xISRtbl[1]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_rsvd_int2_isr (void)

#else

interruptvoidc674x_rsvd_int2_isr (void)

#endif

{

c674xISRtbl[2]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_rsvd_int3_isr (void)

#else

interruptvoidc674x_rsvd_int3_isr (void)

#endif

{

c674xISRtbl[3]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int4_isr (void)

#else

interruptvoidc674x_mask_int4_isr (void)

#endif

{

c674xISRtbl[4]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int5_isr (void)

#else

interruptvoidc674x_mask_int5_isr (void)

#endif

{

c674xISRtbl[5]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int6_isr (void)

#else

interruptvoidc674x_mask_int6_isr (void)

#endif

{

c674xISRtbl[6]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int7_isr (void)

#else

interruptvoidc674x_mask_int7_isr (void)

#endif

{

c674xISRtbl[7]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int8_isr (void)

#else

interruptvoidc674x_mask_int8_isr (void)

#endif

{

c674xISRtbl[8]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int9_isr (void)

#else

interruptvoidc674x_mask_int9_isr (void)

#endif

{

c674xISRtbl[9]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int10_isr (void)

#else

interruptvoidc674x_mask_int10_isr (void)

#endif

{

c674xISRtbl[10]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int11_isr (void)

#else

interruptvoidc674x_mask_int11_isr (void)

#endif

{

c674xISRtbl[11]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int12_isr (void)

#else

interruptvoidc674x_mask_int12_isr (void)

#endif

{

c674xISRtbl[12]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int13_isr (void)

#else

interruptvoidc674x_mask_int13_isr (void)

#endif

{

c674xISRtbl[13]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int14_isr (void)

#else

interruptvoidc674x_mask_int14_isr (void)

#endif

{

c674xISRtbl[14]();

}

 

#ifdef__TI_EABI__

interruptvoid_c674x_mask_int15_isr (void)

#else

interruptvoidc674x_mask_int15_isr (void)

#endif

{

c674xISRtbl[15]();

}

当发送CPU中断INTn时,函数从中断向量表intcVectorTable中找到对应的中断服务程序入口地址,然后又在该中断服务函数中调用c674xISRtbl[n]函数。

初始化DSP中断控制器后,IntDSPINTCInit函数使能DSP全局中断,IntDSPINTCInit函数如下:

voidIntGlobalEnable (void)

{

_enable_interrupts();

}

_enable_interrupts函数为Intrinsic函数,函数使能全局中断。Intrinsic函数为C/C++语言提供了一个调用汇编语言的接口机制,通过Intrinsic函数,C/C++函数中可以调用汇编函数。

2.5 SPI中断初始化

    第五步,对SPI进行中断初始化。SPIInterruptInit函数如下:

voidSPIInterruptInit(void)

{

    //注册中断服务函数

    IntRegister(C674X_MASK_INT4, SPIIsr);

 

    //映射中断事件

    IntEventMap(C674X_MASK_INT4, SYS_INT_SPI1_INT);

 

    //使能可屏蔽中断

    IntEnable(C674X_MASK_INT4);

}

先注册INT4的中断函数为SPIIsr,将c674xISRtbl[4]指向SPIIsr,然后将SPI1中断源映射到INT4,最后使能INT4

2.6 SPI初始化

第六步,SPI初始化。函数SPIInit如下:

voidSPIInit(void)

{

unsignedchar cs=0x01;

unsignedchar dcs=0x01;

unsignedint val= SIMO_SOMI_CLK_CS;

 

SPIReset(SOC_SPI_1_REGS);

 

SPIOutOfReset(SOC_SPI_1_REGS);

 

SPIModeConfigure(SOC_SPI_1_REGS, SPI_MASTER_MODE);

 

SPIClkConfigure(SOC_SPI_1_REGS,228000000, 20000000, SPI_DATA_FORMAT0);

 

SPIPinControl(SOC_SPI_1_REGS,0, 0, &val);

 

SPIDefaultCSSet(SOC_SPI_1_REGS, dcs);

 

//配置 SPI数据格式

SPIDataFormatConfig(SPI_DATA_FORMAT0);

 

//配置 SPI数据格式及片选信号

SPIDat1Config(SOC_SPI_1_REGS, (SPI_CSHOLD| SPI_DATA_FORMAT0), cs);

 

    //映射中断到 INT1

SPIIntLevelSet(SOC_SPI_1_REGS, SPI_RECV_INTLVL| SPI_TRANSMIT_INTLVL);

 

//使能 SPI

SPIEnable(SOC_SPI_1_REGS);

}

1.

先将SPI复位,SPIReset函数如下:

voidSPIReset(unsignedint baseAdd)

{

HWREG(baseAdd+ SPI_SPIGCR0) =~(SPI_SPIGCR0_RESET);

}

函数设置SPIGCR0寄存器(SPI Global Control Register 0)的RESET位为1,复位SPI。然后设置使SPIGCR0RESET位为0,使SPI out of reset状态,SPIOutOfReset函数如下:

voidSPIOutOfReset(unsignedint baseAdd)

{

HWREG(baseAdd+ SPI_SPIGCR0) = SPI_SPIGCR0_RESET;

}

2.

接着,设置SPIGCR1CLKMODMASTER字段为3,将SPI设为Master模式,SPIModeConfigure函数如下:

voidSPIModeConfigure(unsignedint baseAdd, unsignedint flag)

{

HWREG(baseAdd+ SPI_SPIGCR1) |= flag;

}

3.

下一步,设置SPI的时钟,SPIClkConfigure函数如下:

voidSPIClkConfigure(unsignedint baseAdd, unsignedint moduleClk,

unsignedint spiClk,unsignedint dataFormat)

{

unsignedint prescale;

 

prescale= (moduleClk/spiClk)-1;

 

HWREG(baseAdd+SPI_SPIFMT(dataFormat))=\

(SPI_SPIFMT_PRESCALE& (prescale << SPI_SPIFMT_PRESCALE_SHIFT));

}

    SPI时钟计算公式为:spiclk=moduleClk/(prescale+1)Spi1模块时钟moduleclkPLL1_SYSCLK2PLL1_SYSCLK2PLL1_SYSCLK1的一半,PLL1_SYSCLK1这里直连晶振,为456MHz,因此moduleclk228000000Hz228MHz)。

(指南P130~P131

要设置spiclk20000000Hz(20MHz),则prescale=(moduleclk/spiclk)-1。每个SPI模块有四个格式寄存器(Format register),可以存储四种数据字格式(data word format)。这里只用了一种格式,用了SPIFMT0,设置SPIFMT0SPI Data Format Register 0)的PRESCALE字段的值为prescale,则可得spiclk

(指南P1330

(指南P1380

    

4.

下一步,设置SPI引脚控制寄存器(SPI Pin Control Registers),每个SPI模块有6SPIPC寄存器,为SPIPC0~SPIPC5SPIPinControl函数如下:

voidSPIPinControl(unsignedint baseAdd, unsignedint idx,

unsignedint flag,unsignedint*val)

{

if (0== flag)

{

HWREG(baseAdd+SPI_SPIPC(idx))=*val;

}

else

{

*val=HWREG(baseAdd+SPI_SPIPC(idx));

}

}

    IdxSPIPC的标号,为0~5flag表示读(flag=1SPIPC还是写(flag=0SPIPC。这里是写SPIPC0SPIPC0配置SPI模块的引脚功能(function)是GPIO还是SPI功能引脚,这里设置SOMIFUNSIMOFUNCLKFUNSCS0FUN[0]字段为1,即对应的四个引脚为SPI功能引脚(SPI functional pin)。

(指南P1364

5.

接着,设置SPIDEF寄存器(SPI default chip select register),设置CS脚的缺省值,无数据传输时,CS电平为缺省值,SPIDefaultCSSet函数如下:

voidSPIDefaultCSSet(unsignedint baseAdd, unsignedchar dcsval)

{

HWREG(baseAdd+ SPI_SPIDEF) = dcsval;

}

这里设置为1,则SPIx_SCS[0]脚缺省值为高。

(指南P1378

6.

然后,配置SPI数据格式为DATA_FORMAT0寄存器里的格式值,SPIDataFormatConfig函数如下:

voidSPIDataFormatConfig(unsignedint dataFormat)

{

//配置 SPI时钟

SPIConfigClkFormat(SOC_SPI_1_REGS,

(SPI_CLK_POL_HIGH| SPI_CLK_INPHASE),

dataFormat);

 

//配置 SPI发送时 MSB优先

SPIShiftMsbFirst(SOC_SPI_1_REGS, dataFormat);

 

//设置字符长度

SPICharLengthSet(SOC_SPI_1_REGS, CHAR_LENGTH, dataFormat);

}

SPIConfigClkFormat函数如下:

voidSPIConfigClkFormat(unsignedint baseAdd, unsignedint flag,

unsignedint dataFormat)

{

HWREG(baseAdd+SPI_SPIFMT(dataFormat))|= flag;

}

函数设置SPIFMT0POLARITYPHASE10,空闲时spiclk线为高电平,spiclk不延迟。

(指南P1380

SPIShiftMsbFirst函数如下:

voidSPIShiftMsbFirst(unsignedint baseAdd, unsignedint dataFormat)

{

HWREG(baseAdd+SPI_SPIFMT(dataFormat))&=~SPI_SPIFMT_SHIFTDIR;

}

函数设置SPIFMT0SHIFTDIR位,这里设为0,先输出MSB位。

(指南P1379

SPICharLengthSet函数如下:

voidSPICharLengthSet(unsignedint baseAdd, unsignedint numOfChar,

unsignedint dataFormat)

{

HWREG(baseAdd+SPI_SPIFMT(dataFormat))&=~SPI_SPIFMT_CHARLEN;

HWREG(baseAdd+SPI_SPIFMT(dataFormat))|= numOfChar;

}

函数设置SPIFMTCHARLEN字段,设置SPI数据字的长度,这里设置的是SPIFMT0,数据字长度为8,即一个字符8位。

(指南P1380

7.

下一步,配置SPI数据格式及片选信号,SPIDat1Config函数如下:

voidSPIDat1Config(unsignedint baseAdd, unsignedint flag,unsignedchar cs)

{

unsignedchar*ptr= (unsignedchar*)(baseAdd+ SPI_SPIDAT1);

unsignedchar dcs;

 

*(ptr+3)= (char)((flag>>24)| (flag & (SPI_SPIDAT1_DFSEL >>

SPI_SPIDAT1_DFSEL_SHIFT)));

 

dcs=HWREG(baseAdd+ SPI_SPIDEF ) & (SPI_SPIDEF_CSDEF);

 

*(ptr+2)= cs ^ dcs;

}

该函数先设置SPIDAT1的最高8位(ptr+3),这里设置CSHOLD位为1,则CS引脚在数据传输完成后仍然保持有效(active),直到SPIDAT1装载入新的控制信息和数据,才deactivateDFSEL0,选择SPIFMT0的数据字格式,即Data word format 0CSSPIDEFCSDEF异或,然后将结果设置SPIDAT1CSNR[n]位,则SPI1_SCS[0]脚在数据传输时电平为0。这里设置了之后,SPI FLASH对应的CSSPI1_SCS[0]脚将为有效,即CS脚被拉低。

(指南P1371

(指南P1378

8.

下一步,映射中断到INT1SPIIntLevelSet    函数如下:

voidSPIIntLevelSet(unsignedint baseAdd, unsignedint flag)

{

HWREG(baseAdd+ SPI_SPILVL) |= flag;

}

函数映射中断到INT1,这里将接收中断和发送中断映射到SPINT1

(指南P1329

(指南P1361

3.读写SPI外设

3.1 SPI FLASH写使能

到这一步,已经完成了所有的初始化工作,可以对SPI外设读写了。在写SPI设备之前,需要先对SPI写使能。WriteEnable函数如下:

voidWriteEnable(void)

{

tx_data[0]= SPI_FLASH_WRITE_EN;

tx_len= rx_len =1;

SPIDat1Config(SOC_SPI_1_REGS, (SPI_CSHOLD| SPI_DATA_FORMAT0), 0x1);

SpiTransfer();

}

设置要发送的数据为SPI_FLASH_WRITE_EN0x06),对spi flash写使能,

W25Q32FV手册P31

发送的数据长度为1,接收的长度也为1。然后对数据的格式进行配置,SPIDat1Config函数见2.6SPI初始化的第七步,这里的配置是一样的,配置SPI数据格式及片选信号,细节见上。然后开始进行SPI数据传输,SpiTransfer函数如下:

voidSpiTransfer(void)

{

p_tx=&tx_data[0];

p_rx=&rx_data[0];

SPIIntEnable(SOC_SPI_1_REGS, (SPI_RECV_INT| SPI_TRANSMIT_INT));

while(flag);

flag=1;

 

SPIDat1Config(SOC_SPI_1_REGS, SPI_DATA_FORMAT0,0x1);

}

SPIIntEnable函数如下:

voidSPIIntEnable(unsignedint baseAdd, unsignedint flag)

{

HWREG(baseAdd+ SPI_SPIINT0) |= flag;

}

该函数设置SPIINT0TXINTENARXINTENA位,使能发送中断和接收中断。由于此时SPIDAT0SPIDAT1TXDATA部分都没有写入过数据,因此TXBUF都为空,因此SPIFLGTXINTFLG中断标志位被置为1,一旦使能了SPI发送中断和接收中断,就会产生SPI1中断,就会转入SPIisr中断服务子程序

SPIIFG指南P1362

SPIINT0指南P1359

SPIisr程序如下:

voidSPIIsr(void)

{

unsignedint intCode=0;

IntEventClear(SYS_INT_SPI1_INT);

intCode=SPIInterruptVectorGet(SOC_SPI_1_REGS);

 

while (intCode)

{

if(intCode== SPI_TX_BUF_EMPTY)

{

tx_len--;

SPITransmitData1(SOC_SPI_1_REGS,*p_tx);

p_tx++;

if (!tx_len)

{

SPIIntDisable(SOC_SPI_1_REGS, SPI_TRANSMIT_INT);

}

}

 

if(intCode== SPI_RECV_FULL)

{

rx_len--;

*p_rx= (char)SPIDataReceive(SOC_SPI_1_REGS);

p_rx++;

if (!rx_len)

{

flag=0;

SPIIntDisable(SOC_SPI_1_REGS, SPI_RECV_INT);

}

}

 

intCode=SPIInterruptVectorGet(SOC_SPI_1_REGS);

}

}

该函数中,先是清除CPU中断标志,IntEventClear函数细节见这篇博文。C6748_EDMA_GPIO中断学习笔记

然后读取INTVEC1,确定什么事件引起的SPI中断,这里是发送缓冲区空引起的SPI中断,因此SPI中断向量码为0x14SPIInterruptVectorGet函数如下:

unsignedintSPIInterruptVectorGet(unsignedint baseAdd)

{

unsignedint intVectorCode;

intVectorCode=HWREG(baseAdd+ SPI_INTVEC1);

 

return (intVectorCode>>1);

}

然后判断中断码intcode是否为空,不为空则判断是发送中断还是接收中断。如

过是发送中断,则发送p_tx指向的数组,发送的数据个数为tx_len,当发送完

毕后,禁止发送中断。发送函数SPITransmitData1如下:

voidSPITransmitData1(unsignedint baseAdd, unsignedint data)

{

HWREG(baseAdd+ SPI_SPIDAT1) =\

(HWREG(baseAdd+ SPI_SPIDAT1) &~ SPI_SPIDAT1_TXDATA)| data;

}

SPIDAT1TXDATA部分写数据,就可以启动SPI数据传输。

(指南P1335

禁止发送中断函数SPIIntDisable如下:

voidSPIIntDisable(unsignedint baseAdd, unsignedint flag)

{

HWREG(baseAdd+ SPI_SPIINT0) &=~(flag);

}

函数清除SPIINT0TXINTENA位,禁止发送中断。

(指南P1359

如果是接收中断,则读取SPIBUFRXDATA位(SPIBUF[15:0]),读取接收

到的数据。读取函数SPIDataReceive如下:

unsignedintSPIDataReceive(unsignedint baseAdd)

{

return (HWREG(baseAdd+ SPI_SPIBUF) & SPI_SPIBUF_RXDATA);

}

读取rx_len个数据后,flag清零,表示已读取完SPI从设备发回来的数据,然

后清除SPIINT0RXINTENA位,禁用接收中断。

(指南P1359

处理完发送或接收中断后,再读取中断码intcode,如果为0,说明没有中断发

生了,就退出中断服务子程序。

回到SpiTransfer函数,重新将flag设为1,设置SPIDAT1的数据格式为Data Format 0。写使能就执行完了。

主函数继续执行,UARTPuts执行细节参考这篇博文。

C6748_I2C

UARTGetc函数在uartStdio.c文件中,uartStdio.c文件在Utils文件夹下,该文件夹路径为\TL_TMS6748\demo\StarterWare\Source\StarterWare\Utils,函数如下:

unsignedcharUARTGetc(void)

{

return (UARTConsoleGetc());

}

UARTConsoleGetc函数在UARTConsole.c文件中,UARTConsole.c文件在Platform文件夹下,该文件夹路径为

TL_TMS6748\demo\StarterWare\Application\Platform,函数如下:

unsignedcharUARTConsoleGetc(void)

{

return ((unsignedchar)UARTCharGet(UART_CONSOLE_BASE));

}

UARTCharGet函数在uart.c文件中,uart.cDrivers文件夹下,Drivers文件夹路径为TL_TMS6748\demo\StarterWare\Source\StarterWare\Drivers,函数如下:

intUARTCharGet(unsignedint baseAdd)

{

int data=0;

 

/*

** Busy check if data is available in receiver FIFO(RBR regsiter in non-FIFO

** mode) so that data could be read from the RBR register.

*/

while ((HWREG(baseAdd+ UART_LSR) & UART_DATA_READY) ==0);

 

data= (int)HWREG(baseAdd+ UART_RBR);

 

return data;

}

函数读取UARTLSR寄存器的DR位,判断是否接收到数据,如果有则读取RBR,否则继续等待。

UART LSR寄存器指南P1442

UART RBR寄存器指南P1431

3.2 擦除SPI FLASH

SPIflash写使能后,要写spi flash,还要先擦除flashSectorErase函数如下:

voidSectorErase(void)

{

tx_data[0]= SPI_FLASH_SECTOR_ERASE;

tx_data[1]= SPI_FLASH_ADDR_MSB1;

tx_data[2]= SPI_FLASH_ADDR_MSB0;

tx_data[3]= SPI_FLASH_ADDR_LSB;

 

tx_len= rx_len =4;

SPIDat1Config(SOC_SPI_1_REGS, (SPI_CSHOLD| SPI_DATA_FORMAT0), 0x1);

SpiTransfer();

 

IsFlashBusy();

}

SPI_FLASH_SECTOR_ERASE0xD8),SPI_FLASH_ADDR_MSB10x0A),

SPI_FLASH_ADDR_MSB00x00),SPI_FLASH_ADDR_LSB0x00)。后面三个字节组成一个24位地址,0x0A0000,该地址是要擦除的64KB块的地址。因为前面2.6spi初始化的第6SPICharLengthSet函数设置了SPIFMT0的数据字长度为8,所以每往TXDATA写一个16位数据,都是只发送低8位。

(指南P

W25Q32FV手册P56

W25Q32FV手册P28

启动SPI传输,将擦除指令和擦除地址发送给SPI flash设备,然后等待flash擦除完成,IsFlashBusy函数如下:

voidIsFlashBusy(void)

{

do{

StatusGet();

 

}while(rx_data[1]& WRITE_IN_PROGRESS);

}

StatusGet函数如下:

voidStatusGet(void)

{

tx_data[0]= SPI_FLASH_STATUS_RX;

tx_len= rx_len =2;

SPIDat1Config(SOC_SPI_1_REGS, (SPI_CSHOLD| SPI_DATA_FORMAT0), 0x1);

SpiTransfer();

}

SPI_FLASH_STATUS_RX0x05),WRITE_IN_PROGRESS0x01),读取状态寄存器1,时序如下:

W25Q32FV手册P32

(状态寄存器 W25Q32FV手册P18

一直读取BUSY位,直到BUSY位不为1,则转换已完成。

3.3 写SPI FLASH

擦除完spi flash,就可以写spi flash了,写之前再使能一下spi flash写使能,调用WriteEnable。然后写flashWritetoFlash函数如下:

voidWritetoFlash(void)

{

unsignedint index;

 

tx_data[0]= SPI_FLASH_PAGE_WRITE;

tx_data[1]= SPI_FLASH_ADDR_MSB1;

tx_data[2]= SPI_FLASH_ADDR_MSB0;

tx_data[3]= SPI_FLASH_ADDR_LSB;

 

//准备要写入的数据

for (index=4; index<260; index++)

{

tx_data[index]= index;

}

 

for(index=4; index<260; index++)

{

vrf_data[index]= index;

}

 

tx_len= rx_len = index;

SPIDat1Config(SOC_SPI_1_REGS, (SPI_CSHOLD| SPI_DATA_FORMAT0), 0x1);

SpiTransfer();

 

IsFlashBusy();

}

SPI_FLASH_PAGE_WRITE0x02),SPI_FLASH_ADDR_MSB10x0A),SPI_FLASH_ADDR_MSB00x00),SPI_FLASH_ADDR_LSB0x00)。页编程允许1256字节数据写到之前擦除过的存储位置。写spi flash页,时序是,先发送写指令02h,然后发送24位的页地址,然后发送256字节要写的数据。这里24位页地址和前面擦除过的地址是一样的,都是0x0A0000。时序图如下:

W25Q32FV手册P51

3.4 读SPI FLASH

写完spi flash后,读spi flash,读取刚刚写入的那一页,对比写入的数据和读出的数据是否一样。读spi flash函数ReadFromFlash如下:

voidReadFromFlash(void)

{

unsignedint index;

 

tx_data[0]= SPI_FLASH_READ;

tx_data[1]= SPI_FLASH_ADDR_MSB1;

tx_data[2]= SPI_FLASH_ADDR_MSB0;

tx_data[3]= SPI_FLASH_ADDR_LSB;

 

//情况变量

for (index=4; index<260; index++)

{

tx_data[index]=0;

}

 

tx_len= rx_len = index;

SPIDat1Config(SOC_SPI_1_REGS, (SPI_CSHOLD| SPI_DATA_FORMAT0), 0x1);

SpiTransfer();

}

SPI_FLASH_READ0x03),SPI_FLASH_ADDR_MSB10x0A),SPI_FLASH_ADDR_MSB00x00),SPI_FLASH_ADDR_LSB0x00)。读数据的时序是,先发送读指令03h,然后发送24位的页地址,然后DO线就会输出该页的数据,此时就可以读取页的数据了。时序图如下:

W25Q32FV手册P36

发送了读指令03h24位地址后,SPI模块的RXBUF将会收到数据,此时会同时发生Receive buffer full interruptTransmit buffer empty interrupt,但是因为Receive buffer full interrupt的优先级比Transmit buffer empty interrupt要高,所以INTVECT1里的中断码是Receive buffer full interrupt的中断码,在SPIIsr程序中,程序会读取index个数据到rx_data数组中,然后再将没写完的index-4个数据写到spi总线。

(指南P1381

3.5 比较读出的SPI FLASH的数据和写入的SPI FLASH的数据

函数VerifyData如下:

intVerifyData(void)

{

unsignedint index;

 

for(index=4; index<260; index++)

{

if(vrf_data[index]!= rx_data[index])

{

UARTPuts("\r\n",-1);

UARTPuts("VerifyData: Comparing the data written to and read",-1);

UARTPuts(" from Flash.\r\nThe two data blocks are unequal.", -1);

UARTPuts(" Mismatch found at index ",-1);

UARTPutNum((int)index-3);

UARTPuts("\r\n",-1);

            UARTPuts("Verify Failed.\r\n",-1);

return0;

}

}

 

if (index==260)

{

UARTPuts("\r\nThe data in the Flash and the one written ", -1);

UARTPuts("to it are equal.\r\n",-1);

        UARTPuts("Verify successfully.\r\n",-1);

return1;

}

 

return0;

}

函数逐个比较读出的数据和写入的数据,如果有一个不一样,则校验失败,否则成功。

原创粉丝点击