TMS320C6748_SPI_FLASH

目录

1.主函数流程

2.初始化

2.1 PSC初始化

2.2 UART初始化

2.3 管脚复用配置

2.4 DSP中断初始化

2.5 SPI中断初始化

2.6 SPI初始化

2.6.1.SPI复位

2.6.2.设置SPI模式

2.6.3.设置SPI的时钟

2.6.4.设置SPI引脚控制寄存器

2.6.5.设置SPIDEF寄存器

2.6.6.设置SPI数据格式

2.6.7.设置SPI数据格式及片选信号

2.6.8.设置中断映射

3.读写SPI外设

3.1 SPI FLASH写使能

3.2 擦除SPI FLASH

3.3 写SPI FLASH

3.4 读SPI FLASH

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函数具体执行细节可以参考这篇博文:

C6748_UART_EDMA

2.2 UART初始化

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

C6748_UART_EDMA

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执行细节参考这篇博文。

C6748_I2C

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;
}

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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值