关于设计,生活和电子的二三事

走进MSP430(8)——MSP430G2 硬件SPI使用

前面都只是根据TI MSPWare例程讲,感觉比较无聊,最后这两个结合实际使用来讲,贴出的代码在MSP430G2 LAUNCHPAD和对应芯片上测试通过。这一次是硬件SPI模块。一般像这种同步串口(不知道什么是同步串口和异步串口的请自行google)多数情况用GPIO模拟是最简单的,但随着对速度要求的提高,再用GPIO软件模拟的话就很难做到了,比如在STM32上实测用GPIO模拟SPI协议,速度最高也就不到2M,而硬件最高能达到36M,这时候就需要使用硬件SPI。硬件SPI另一个好处是数据收发由专用硬件模块实现,收发时候不占用CPU,如果结合DMA(直接内存访问)使用,在整个传输过程中都不需要CPU参与,复杂任务下可以大大节约CPU资源。不像IIC,SPI是一个约定俗成的协议,不需要专利费,所以也就没有各种隐藏bug(此段为段子,如有雷同纯属巧合)。但是也因为SPI没有确定的协议,因此有好几种工作模式,需要根据具体器件设置。实例中根据SPI FLASH设置SPI工作模式,如果用到别的器件上可能需要根据实际情况修改配置参数。
首先上一张SPI时序图,所有操作都以时序图为参考。图片来源是MSP430G2系列用户手册。

可见四种SPI模式是数据采样时间不同,为方便说明,UCxCLK的四种情况从上到下依次编号为1,2,3,4。对于数据移位和采样时间,在UCxCLK是情况1和4时,数据引脚电平(MOSI,MISO)在时钟的上升沿改变,在下降沿被采样,情况2和3是下降沿改变,上升沿采样。对于时钟电平空闲时的状态,在UCxCLK是情况1和3时是空闲状态时钟为低,情况2和4是空闲状态时钟为高。
对于FLASH支持的SPI模式可在FLASH手册中找到,多数FLASH都支持UCxCLK情况2和3,即数据引脚电平在下降沿改变,在上升沿采样,而对于时钟引脚空闲电平不敏感。

因此,程序中配置SPI为时钟空闲为高,上升沿采样,下降沿数据改变。SPI初始化代码如下:

    WDTCTL = WDTPW + WDTHOLD;                       // Stop watchdog timer
    P1OUT = 0x00;
    P1DIR |= BIT0 + BIT3 + BIT5;
    P1OUT |= BIT0 + BIT3 + BIT5;                    // Control pins assignment
    P1SEL = BIT1 + BIT2 + BIT4;
    P1SEL2 = BIT1 + BIT2 + BIT4;                    // SPI pins assignment
    UCA0CTL0 |= UCCKPL + UCMSB + UCMST + UCSYNC;    // 3-pin, 8-bit SPI master
    UCA0CTL1 |= UCSSEL_2;                           // SPI Clock source is SMCLK
    UCA0BR0 |= 0x02;                                // SPI clock = SMCLK / 2
    UCA0BR1 = 0;                                    //
    UCA0MCTL = 0;                                   // No modulation
    UCA0CTL1 &= ~UCSWRST;                           // **Initialize USCI state machine**
    IFG2 &= ~(UCA0TXIFG + UCA0RXIFG);               // Clear TX and RX interrupt flag
    P1OUT &= ~BIT5;                                 // Now with SPI signals initialized, select slave device
    IE2 |= UCA0TXIE;                                // Enable TX interrupt
    __bis_SR_register(GIE);                         // Enable global interrupts

在MSP430G2553手册里对于SPI引脚连接有说明。因为用到了SPI模块,所以只有特定引脚可以连接设备。

如图中,P1.1为SPI SOMI引脚,P1.2为SPI SIMO引脚,P1.4为SPI CLK引脚,这三个引脚要接到SPI FLASH的对应引脚上,SPI的CS引脚在有些芯片中提供硬件控制功能,有些没有。因为一个SPI总线上可以挂多个器件,通过CS引脚选择要进行操作的器件,在对器件进行操作之前需要先根据说明将CS引脚置于相应电平(一般是低电平表示选中对应器件),器件才会响应。不能同时有两个或以上器件被选中,否则数据读写会出错。在没有硬件控制CS引脚功能的芯片上,可以用GPIO控制。由于CS引脚在整个读写过程中可以一直保持有效,所以对读写速度影响不大。

图中FLASH芯片通过转接板连接到LAUNCHPAD,左边的连接线连接到别的模块上,程序中没有用到。在运行程序之前,先使用编程器读出FLASH内容如下:

然后运行程序,并同时用逻辑分析仪采集CLK,MOSI,MISO引脚的信号。程序图片右边能够读出FLASH内容,并且和编程器读出的内容一致,逻辑分析仪也能读出SPI传输的内容。

附上完整的读FLASH的程序。写FLASH的程序在此上面稍加修改即可。FLASH读写指令可以在芯片手册中查到。

#include <msp430.h>
#include <stdint.h>

#define SPI_CS  BIT5
#define SPI_WP  BIT3
#define SPI_HD  BIT0
uint8_t SPI_Char[128];
uint8_t SPI_CMD[4] = {0x03, 0x00, 0x00, 0x00};

int main(void)
{
    WDTCTL = WDTPW + WDTHOLD;                       // Stop watchdog timer
    P1OUT = 0x00;
    P1DIR |= BIT0 + BIT3 + BIT5;
    P1OUT |= BIT0 + BIT3 + BIT5;                    // Control pins assignment
    P1SEL = BIT1 + BIT2 + BIT4;
    P1SEL2 = BIT1 + BIT2 + BIT4;                    // SPI pins assignment
    UCA0CTL0 |= UCCKPL + UCMSB + UCMST + UCSYNC;    // 3-pin, 8-bit SPI master
    UCA0CTL1 |= UCSSEL_2;                           // SPI Clock source is SMCLK
    UCA0BR0 |= 0x02;                                // SPI clock = SMCLK / 2
    UCA0BR1 = 0;                                    //
    UCA0MCTL = 0;                                   // No modulation
    UCA0CTL1 &= ~UCSWRST;                           // **Initialize USCI state machine**
    IFG2 &= ~(UCA0TXIFG + UCA0RXIFG);               // Clear TX and RX interrupt flag
    P1OUT &= ~BIT5;                                 // Now with SPI signals initialized, select slave device
    IE2 |= UCA0TXIE;                                // Enable TX interrupt
    __bis_SR_register(GIE);                         // Enable global interrupts

    UCA0TXBUF = SPI_CMD[0];                         // Send 1st byte

    while(1);
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCIA0RX_ISR(void)
{
    static uint8_t i = 0;

    if(IFG2 & UCA0TXIFG)
    {
        if(i < 128)
            SPI_Char[i++] = UCA0RXBUF;  // Receive bytes
    }
}

#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCIA0TX_ISR(void)
{
    static uint8_t i = 1;

    if(IFG2 & UCA0TXIFG)
    {
        if(i < 4)
            UCA0TXBUF = SPI_CMD[i++];   // Transmit control bytes
        else
        {
            UCA0TXBUF = 0xFF;
            i++;                        // Dummy tx bytes, for continuity of spi clock
        }
        if(i == 5)
            IE2 |= UCA0RXIE;            // Transmit end, enable receive interrupt
        if(i >= 133)
        {
            P1OUT |= BIT5;              // Deselect SPI slave device
            IE2 &= ~UCA0RXIE;           // Clear TX and RX interrupt
            IE2 &= ~UCA0TXIE;
        }
    }
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据