目录
3.5 比较读出的SPI FLASH的数据和写入的SPI FLASH的数据
1.主函数流程
此程序的作用是实现SPI FLASH设备的数据读写功能,此设备使用SPI1总线CS0片选。主函数流程图如下:
主函数如下:
int main(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()如下:
void PSCInit(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模块设置为使能态,即使能SPI1。SPI1模块由PSC1管理,其LPSC的ID为10,如图所示:
(指南P163)
PSC1的寄存器(regs)基地址为0x01E2 7000,如图所示:
(手册P97)
PSCModuleControl函数具体执行细节可以参考这篇博文:
2.2 UART初始化
第二步,初始化串口终端,使用串口2。函数UARTStdioInit();该函数在demo\StarterWare\Source\StarterWare\Utils路径下工程文件里的uartStdio.c程序中,函数具体细节可以参考这篇博文。
2.3 管脚复用配置
第三步,将GPIO引脚功能设为复用功能,GPIOBankPinMuxSet();函数如下:
void GPIOBankPinMuxSet(void)
{
SPIPinMuxSetup(1);
SPI1CSPinMuxSetup(0);
}
SPIPinMuxSetup函数如下:
void SPIPinMuxSetup(unsigned int instanceNum)
{
unsigned int 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);
}
else if(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,则要设置的是PINMUX3的15_0位;如果是SPI1,则要设置的是PINMUX5的23_8位。所有的PINMUX寄存器都在系统配置模块0(SYSCFG0)中,因此需要以SYSCFG0模块的地址为基地址,再加上偏移地址找到PINMUXn寄存器。
这里启用的是SPI1通道,因此设置的是PINMUX5。函数先将与SPI1相关的字段,即23_8位清零,同时保存其它无关的位,然后设置23_8位为SPI1四个引脚SPI1_CLK、SPI1_SIMO、SPI1_SOMI、SPI1_ENA对应的值。
SPI1CSPinMuxSetup函数如下:
void SPI1CSPinMuxSetup(unsigned int csPinNum)
{
unsigned int spi1CSPinMux = 0;
unsigned int savePinMux = 0;
switch(csPinNum)
{
case 0:
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;
case 1:
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;
case 2:
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;
case 3:
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;
case 4:
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;
case 5:
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;
case 6:
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;
case 7:
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函数如下:
void InterruptInit(void)
{
// 初始化 DSP 中断控制器
IntDSPINTCInit();
// 使能 DSP 全局中断
IntGlobalEnable();
}
DSP中断初始化函数中,先初始化DSP中断控制器,再使能DSP全局中断。DSP中断控制器初始化函数IntDSPINTCInit在StarterWare的interrupt.c文件中,路径为\demo\StarterWare\Source\StarterWare\SystemConfig,IntDSPINTCInit函数如下:
void IntDSPINTCInit (void)
{
unsigned int 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 = (unsigned int)_intcVectorTable;
#else
ISTP = (unsigned int)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)
函数将ISTP(Interrupt Service Table Pointer)指向intcVectorTable,intcVectorTable是一张向量表,记录了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
NOP 2
NOP
NOP
.endm
;**********************************************************
; Interrupt Vector Table
;**********************************************************
.align 1024
_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__
interrupt void _c674x_nmi_isr (void)
#else
interrupt void c674x_nmi_isr (void)
#endif
{
c674xISRtbl[1]();
}
#ifdef __TI_EABI__
interrupt void _c674x_rsvd_int2_isr (void)
#else
interrupt void c674x_rsvd_int2_isr (void)
#endif
{
c674xISRtbl[2]();
}
#ifdef __TI_EABI__
interrupt void _c674x_rsvd_int3_isr (void)
#else
interrupt void c674x_rsvd_int3_isr (void)
#endif
{
c674xISRtbl[3]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int4_isr (void)
#else
interrupt void c674x_mask_int4_isr (void)
#endif
{
c674xISRtbl[4]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int5_isr (void)
#else
interrupt void c674x_mask_int5_isr (void)
#endif
{
c674xISRtbl[5]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int6_isr (void)
#else
interrupt void c674x_mask_int6_isr (void)
#endif
{
c674xISRtbl[6]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int7_isr (void)
#else
interrupt void c674x_mask_int7_isr (void)
#endif
{
c674xISRtbl[7]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int8_isr (void)
#else
interrupt void c674x_mask_int8_isr (void)
#endif
{
c674xISRtbl[8]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int9_isr (void)
#else
interrupt void c674x_mask_int9_isr (void)
#endif
{
c674xISRtbl[9]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int10_isr (void)
#else
interrupt void c674x_mask_int10_isr (void)
#endif
{
c674xISRtbl[10]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int11_isr (void)
#else
interrupt void c674x_mask_int11_isr (void)
#endif
{
c674xISRtbl[11]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int12_isr (void)
#else
interrupt void c674x_mask_int12_isr (void)
#endif
{
c674xISRtbl[12]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int13_isr (void)
#else
interrupt void c674x_mask_int13_isr (void)
#endif
{
c674xISRtbl[13]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int14_isr (void)
#else
interrupt void c674x_mask_int14_isr (void)
#endif
{
c674xISRtbl[14]();
}
#ifdef __TI_EABI__
interrupt void _c674x_mask_int15_isr (void)
#else
interrupt void c674x_mask_int15_isr (void)
#endif
{
c674xISRtbl[15]();
}
当发送CPU中断INTn时,函数从中断向量表intcVectorTable中找到对应的中断服务程序入口地址,然后又在该中断服务函数中调用c674xISRtbl[n]函数。
初始化DSP中断控制器后,IntDSPINTCInit函数使能DSP全局中断,IntDSPINTCInit函数如下:
void IntGlobalEnable (void)
{
_enable_interrupts();
}
_enable_interrupts函数为Intrinsic函数,函数使能全局中断。Intrinsic函数为C/C++语言提供了一个调用汇编语言的接口机制,通过Intrinsic函数,C/C++函数中可以调用汇编函数。
2.5 SPI中断初始化
第五步,对SPI进行中断初始化。SPIInterruptInit函数如下:
void SPIInterruptInit(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如下:
void SPIInit(void)
{
unsigned char cs = 0x01;
unsigned char dcs = 0x01;
unsigned int 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);
}
2.6.1.SPI复位
先将SPI复位,SPIReset函数如下:
void SPIReset(unsigned int baseAdd)
{
HWREG(baseAdd + SPI_SPIGCR0) = ~(SPI_SPIGCR0_RESET);
}
函数设置SPIGCR0寄存器(SPI Global Control Register 0)的RESET位为1,复位SPI。然后设置使SPIGCR0的RESET位为0,使SPI out of reset状态,SPIOutOfReset函数如下:
void SPIOutOfReset(unsigned int baseAdd)
{
HWREG(baseAdd + SPI_SPIGCR0) = SPI_SPIGCR0_RESET;
}
2.6.2.设置SPI模式
接着,设置SPIGCR1的CLKMOD,MASTER字段为3,将SPI设为Master模式,SPIModeConfigure函数如下:
void SPIModeConfigure(unsigned int baseAdd, unsigned int flag)
{
HWREG(baseAdd + SPI_SPIGCR1) |= flag;
}
2.6.3.设置SPI的时钟
下一步,设置SPI的时钟,SPIClkConfigure函数如下:
void SPIClkConfigure(unsigned int baseAdd, unsigned int moduleClk,
unsigned int spiClk, unsigned int dataFormat)
{
unsigned int prescale;
prescale = (moduleClk/spiClk) - 1;
HWREG(baseAdd + SPI_SPIFMT(dataFormat)) = \
(SPI_SPIFMT_PRESCALE & (prescale << SPI_SPIFMT_PRESCALE_SHIFT));
}
SPI时钟计算公式为:spiclk=moduleClk/(prescale+1),Spi1模块时钟moduleclk为PLL1_SYSCLK2,PLL1_SYSCLK2为PLL1_SYSCLK1的一半,PLL1_SYSCLK1这里直连晶振,为456MHz,因此moduleclk为228000000Hz(228MHz)。
(指南P130~P131)
要设置spiclk为20000000Hz(20MHz),则prescale=(moduleclk/spiclk)-1。每个SPI模块有四个格式寄存器(Format register),可以存储四种数据字格式(data word format)。这里只用了一种格式,用了SPIFMT0,设置SPIFMT0(SPI Data Format Register 0)的PRESCALE字段的值为prescale,则可得spiclk。
(指南P1330)
(指南P1380)
2.6.4.设置SPI引脚控制寄存器
下一步,设置SPI引脚控制寄存器(SPI Pin Control Registers),每个SPI模块有6个SPIPC寄存器,为SPIPC0~SPIPC5,SPIPinControl函数如下:
void SPIPinControl(unsigned int baseAdd, unsigned int idx,
unsigned int flag, unsigned int *val)
{
if (0 == flag)
{
HWREG(baseAdd + SPI_SPIPC(idx)) = *val;
}
else
{
*val = HWREG(baseAdd + SPI_SPIPC(idx));
}
}
Idx为SPIPC的标号,为0~5,flag表示读(flag=1)SPIPC还是写(flag=0)SPIPC。这里是写SPIPC0,SPIPC0配置SPI模块的引脚功能(function)是GPIO还是SPI功能引脚,这里设置SOMIFUN、SIMOFUN、CLKFUN、SCS0FUN[0]字段为1,即对应的四个引脚为SPI功能引脚(SPI functional pin)。
(指南P1364)
2.6.5.设置SPIDEF寄存器
接着,设置SPIDEF寄存器(SPI default chip select register),设置CS脚的缺省值,无数据传输时,CS电平为缺省值,SPIDefaultCSSet函数如下:
void SPIDefaultCSSet(unsigned int baseAdd, unsigned char dcsval)
{
HWREG(baseAdd + SPI_SPIDEF) = dcsval;
}
这里设置为1,则SPIx_SCS[0]脚缺省值为高。
(指南P1378)
2.6.6.设置SPI数据格式
然后,配置SPI数据格式为DATA_FORMAT0寄存器里的格式值,SPIDataFormatConfig函数如下:
void SPIDataFormatConfig(unsigned int 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函数如下:
void SPIConfigClkFormat(unsigned int baseAdd, unsigned int flag,
unsigned int dataFormat)
{
HWREG(baseAdd + SPI_SPIFMT(dataFormat)) |= flag;
}
函数设置SPIFMT0的POLARITY和PHASE为1和0,空闲时spiclk线为高电平,spiclk不延迟。
(指南P1380)
SPIShiftMsbFirst函数如下:
void SPIShiftMsbFirst(unsigned int baseAdd, unsigned int dataFormat)
{
HWREG(baseAdd + SPI_SPIFMT(dataFormat)) &= ~SPI_SPIFMT_SHIFTDIR;
}
函数设置SPIFMT0的SHIFTDIR位,这里设为0,先输出MSB位。
(指南P1379)
SPICharLengthSet函数如下:
void SPICharLengthSet(unsigned int baseAdd, unsigned int numOfChar,
unsigned int dataFormat)
{
HWREG(baseAdd + SPI_SPIFMT(dataFormat)) &= ~SPI_SPIFMT_CHARLEN;
HWREG(baseAdd + SPI_SPIFMT(dataFormat)) |= numOfChar;
}
函数设置SPIFMT的CHARLEN字段,设置SPI数据字的长度,这里设置的是SPIFMT0,数据字长度为8,即一个字符8位。
(指南P1380)
2.6.7.设置SPI数据格式及片选信号
下一步,配置SPI数据格式及片选信号,SPIDat1Config函数如下:
void SPIDat1Config(unsigned int baseAdd, unsigned int flag, unsigned char cs)
{
unsigned char *ptr = (unsigned char*)(baseAdd + SPI_SPIDAT1);
unsigned char 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装载入新的控制信息和数据,才deactivate。DFSEL为0,选择SPIFMT0的数据字格式,即Data word format 0。CS与SPIDEF的CSDEF异或,然后将结果设置SPIDAT1的CSNR[n]位,则SPI1_SCS[0]脚在数据传输时电平为0。这里设置了之后,SPI FLASH对应的CS脚SPI1_SCS[0]脚将为有效,即CS脚被拉低。
(指南P1371)
(指南P1378)
2.6.8.设置中断映射
下一步,映射中断到INT1。SPIIntLevelSet函数如下:
void SPIIntLevelSet(unsigned int baseAdd, unsigned int flag)
{
HWREG(baseAdd + SPI_SPILVL) |= flag;
}
函数映射中断到INT1,这里将接收中断和发送中断映射到SPINT1。
(指南P1329)
(指南P1361)
3.读写SPI外设
3.1 SPI FLASH写使能
到这一步,已经完成了所有的初始化工作,可以对SPI外设读写了。在写SPI设备之前,需要先对SPI写使能。WriteEnable函数如下:
void WriteEnable(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_EN(0x06),对spi flash写使能,
(W25Q32FV手册P31)
发送的数据长度为1,接收的长度也为1。然后对数据的格式进行配置,SPIDat1Config函数见2.6SPI初始化的第七步,这里的配置是一样的,配置SPI数据格式及片选信号,细节见上。然后开始进行SPI数据传输,SpiTransfer函数如下:
void SpiTransfer(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函数如下:
void SPIIntEnable(unsigned int baseAdd, unsigned int flag)
{
HWREG(baseAdd + SPI_SPIINT0) |= flag;
}
该函数设置SPIINT0的TXINTENA和RXINTENA位,使能发送中断和接收中断。由于此时SPIDAT0和SPIDAT1的TXDATA部分都没有写入过数据,因此TXBUF都为空,因此SPIFLG的TXINTFLG中断标志位被置为1,一旦使能了SPI发送中断和接收中断,就会产生SPI1中断,就会转入SPIisr中断服务子程序。
(SPIIFG 指南P1362)
(SPIINT0 指南P1359)
SPIisr程序如下:
void SPIIsr(void)
{
unsigned int 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中断向量码为0x14。SPIInterruptVectorGet函数如下:
unsigned int SPIInterruptVectorGet(unsigned int baseAdd)
{
unsigned int intVectorCode;
intVectorCode = HWREG(baseAdd + SPI_INTVEC1);
return (intVectorCode >> 1);
}
然后判断中断码intcode是否为空,不为空则判断是发送中断还是接收中断。如果是发送中断,则发送p_tx指向的数组,发送的数据个数为tx_len,当发送完毕后,禁止发送中断。发送函数SPITransmitData1如下:
void SPITransmitData1(unsigned int baseAdd, unsigned int data)
{
HWREG(baseAdd + SPI_SPIDAT1) = \
(HWREG(baseAdd + SPI_SPIDAT1) & ~ SPI_SPIDAT1_TXDATA) | data;
}
往SPIDAT1的TXDATA部分写数据,就可以启动SPI数据传输。
(指南P1335)
禁止发送中断函数SPIIntDisable如下:
void SPIIntDisable(unsigned int baseAdd, unsigned int flag)
{
HWREG(baseAdd + SPI_SPIINT0) &= ~(flag);
}
函数清除SPIINT0的TXINTENA位,禁止发送中断。
(指南P1359)
如果是接收中断,则读取SPIBUF的RXDATA位(SPIBUF[15:0]),读取接收
到的数据。读取函数SPIDataReceive如下:
unsigned int SPIDataReceive(unsigned int baseAdd)
{
return (HWREG(baseAdd + SPI_SPIBUF) & SPI_SPIBUF_RXDATA);
}
读取rx_len个数据后,flag清零,表示已读取完SPI从设备发回来的数据,然后清除SPIINT0的RXINTENA位,禁用接收中断。
(指南P1359)
处理完发送或接收中断后,再读取中断码intcode,如果为0,说明没有中断发生了,就退出中断服务子程序。回到SpiTransfer函数,重新将flag设为1,设置SPIDAT1的数据格式为Data Format 0。写使能就执行完了。
主函数继续执行,UARTPuts执行细节参考这篇博文。
UARTGetc函数在uartStdio.c文件中,uartStdio.c文件在Utils文件夹下,该文件夹路径为\TL_TMS6748\demo\StarterWare\Source\StarterWare\Utils,函数如下:
unsigned char UARTGetc(void)
{
return (UARTConsoleGetc());
}
UARTConsoleGetc函数在UARTConsole.c文件中,UARTConsole.c文件在Platform文件夹下,该文件夹路径为
TL_TMS6748\demo\StarterWare\Application\Platform,函数如下:
unsigned char UARTConsoleGetc(void)
{
return ((unsigned char)UARTCharGet(UART_CONSOLE_BASE));
}
UARTCharGet函数在uart.c文件中,uart.c在Drivers文件夹下,Drivers文件夹路径为TL_TMS6748\demo\StarterWare\Source\StarterWare\Drivers,函数如下:
int UARTCharGet(unsigned int 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;
}
函数读取UART的LSR寄存器的DR位,判断是否接收到数据,如果有则读取RBR,否则继续等待。
(UART LSR寄存器指南P1442)
(UART RBR寄存器指南P1431)
3.2 擦除SPI FLASH
SPIflash写使能后,要写spi flash,还要先擦除flash。SectorErase函数如下:
void SectorErase(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_ERASE(0xD8),SPI_FLASH_ADDR_MSB1(0x0A),
SPI_FLASH_ADDR_MSB0(0x00),SPI_FLASH_ADDR_LSB(0x00)。后面三个字节组成一个24位地址,0x0A0000,该地址是要擦除的64KB块的地址。因为前面2.6spi初始化的第6步SPICharLengthSet函数设置了SPIFMT0的数据字长度为8,所以每往TXDATA写一个16位数据,都是只发送低8位。
(指南P)
(W25Q32FV手册P56)
(W25Q32FV手册P28)
启动SPI传输,将擦除指令和擦除地址发送给SPI flash设备,然后等待flash擦除完成,IsFlashBusy函数如下:
void IsFlashBusy(void)
{
do{
StatusGet();
}while(rx_data[1] & WRITE_IN_PROGRESS);
}
StatusGet函数如下:
void StatusGet(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_RX(0x05),WRITE_IN_PROGRESS(0x01),读取状态寄存器1,时序如下:
(W25Q32FV手册P32)
(状态寄存器 W25Q32FV手册P18)
一直读取BUSY位,直到BUSY位不为1,则转换已完成。
3.3 写SPI FLASH
擦除完spi flash,就可以写spi flash了,写之前再使能一下spi flash写使能,调用WriteEnable。然后写flash,WritetoFlash函数如下:
void WritetoFlash(void)
{
unsigned int 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_WRITE(0x02),SPI_FLASH_ADDR_MSB1(0x0A),SPI_FLASH_ADDR_MSB0(0x00),SPI_FLASH_ADDR_LSB(0x00)。页编程允许1到256字节数据写到之前擦除过的存储位置。写spi flash页,时序是,先发送写指令02h,然后发送24位的页地址,然后发送256字节要写的数据。这里24位页地址和前面擦除过的地址是一样的,都是0x0A0000。时序图如下:
(W25Q32FV手册P51)
3.4 读SPI FLASH
写完spi flash后,读spi flash,读取刚刚写入的那一页,对比写入的数据和读出的数据是否一样。读spi flash函数ReadFromFlash如下:
void ReadFromFlash(void)
{
unsigned int 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_READ(0x03),SPI_FLASH_ADDR_MSB1(0x0A),SPI_FLASH_ADDR_MSB0(0x00),SPI_FLASH_ADDR_LSB(0x00)。读数据的时序是,先发送读指令03h,然后发送24位的页地址,然后DO线就会输出该页的数据,此时就可以读取页的数据了。时序图如下:
(W25Q32FV手册P36)
发送了读指令03h,24位地址后,SPI模块的RXBUF将会收到数据,此时会同时发生Receive buffer full interrupt和Transmit 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如下:
int VerifyData(void)
{
unsigned int 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);
return 0;
}
}
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);
return 1;
}
return 0;
}
函数逐个比较读出的数据和写入的数据,如果有一个不一样,则校验失败,否则成功。