历史上的今天
返回首页

历史上的今天

今天是:2024年10月10日(星期四)

正在发生

2019年10月10日 | MSP430程序库<五>SPI同步串行通信

2019-10-10 来源:eefocus

SPI总线系统是一种同步串行外设接口;是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。 许多的芯片都用这种协议通信:EEPROM、Flash、实时时钟、AD转换器、数字信号处理器等:MSP430的USART模块不仅能够实现异步模式(见:MSP430程序库<二>UART异步串口),而且支持同步串行通信(即SPI模式);其SPI支持3线、4线操作,支持主机模式和从机模式,字符长度可以7位或8位等。由于要用AD7708芯片完成AD采样,AD7708是通过SPI与其它设备通信的;本程序比较简化,只完成了主机模式的初始化。


硬件介绍:

SPI:SPI是Motorola首先在其MC68HCXX系列处理器上定义的,它是一种同步的高速串行通信协议,有关SPI协议的详细内容,参考:SPI_互动百科。


MSP430对SPI的支持:当msp430USART模块控制器UxCTL的位SYNC置位时,USART模块工作于同步模式,对于149即工作于SPI模式,若是169,USART0可以支持I2C,可以通过另一控制位I2C控制,I2C位0则工作于SPI。在SPI模式下,允许单片机以确定的速率发送和接收7位或8位数据。


同步通信与异步通信类似;同步通信和异步通信寄存器资源一致,具体寄存器的不同位之间的功能存在差异;具体寄存器内容参见TI提供的用户指南。


USART模块的SPI操作可以是3线和4线,其信号如下: 

SIMO:从进主出,主机模式下,数据输出;从机模式下,数据输入。 

SOMI:从出主进,主机模式下,数据输入;从机模式下,数据输出。 

UCLK:USART SPI模式时钟,信号有主机输出,从机输入。 

STE:从机模式发送接收允许控制脚,用于4线模式,控制多主从系统中多个从机,避免发生冲突。具体方式如下(图截自 用户指南):

image

image四线主机模式:STE为高电平,SIMO和UCLK操作正常;STE为低电平,SIMO和UCLK被置为输入方向,主机控制权让出。 

四线从机模式:STE为高电平,从机的发送和接收无效,且把SOMI置为输入方向;STE为低电平,发送接收正常,SOMI也为正常输出。


USART模块串行时钟极性和相位设置:


USART的时钟UCLK的极性和相位由位于UxTCTL寄存器的CKPH和CKPL位控制,具体如下图:在程序中,我分别称之为,时钟模式0、时钟模式1、时钟模式2、时钟模式3。


image


USART的波特率产生,SPI不同于异步通信:异步通信由UxBR1UxBR0UxMCTL三个寄存器控制,以产生标准频率;而同步模式,主从设备用同一个时钟,不再需要产生标准时钟,故而不再用UxMCTL寄存器,设其值为0.


其他的,与异步通信基本一致,这里不再细说。具体参考用户指南。


程序实现:

程序和异步通信方式类似:首先是初始化函数,然后是读取数据、写入数据函数。此程序采用和我之前的UART程序库类似的结构,写入数据后进入低功耗等待中断,判断标志位进行写入数据和读取数据。


这里函数只实现430的主机模式,如需从机模式可以仿照我的程序,进行简化实现。


由于,我即将使用的SPI设备(AD7708)不是字符型设备,这里不再实现写入字符串函数,也不再移植printf和scanf函数,如若需要可以自己添加,printf和scanf的移植参考:MSP430程序库<四>printf和scanf函数移植


初始化函数:SpiMasterInit,实现主机模式的初始化工作,函数内容如下:


char SpiMasterInit(long baud,char dataBits,char mode,char clkMode)

{

    long int brclk;                 //波特率发生器时钟频率

    

    UxCTL |= SWRST;                 //初始

    

    //反馈选择位,为1,发送的数被自己接收,用于测试,正常使用时注释掉

    //UxCTL |= LISTEN;

    

    UxCTL |= SYNC + MM;             //SPI 主机模式

    

    //时钟源设置

    UxTCTL &=~ (SSEL0+SSEL1);       //清除之前的时钟设置

    if(baud<=16364)                 //

    {

      UxTCTL |= SSEL0;              //ACLK,降低功耗

      brclk = 32768;                //波特率发生器时钟频率=ACLK(32768)

    }

    else

    {

      UxTCTL |= SSEL1;              //SMCLK,保证速度

      brclk = 1000000;              //波特率发生器时钟频率=SMCLK(1MHz)

    }

    

    //------------------------设置波特率-------------------------   

    if(baud < 300||baud > 115200)   //波特率超出范围

    {

        return 0;

    }

    //设置波特率寄存器

    int fen = brclk / baud;         //分频系数

    if(fen<2)return (0);            //分频系数必须大于2

    else

    {

        UxBR0 = fen / 256;

        UxBR1 = fen % 256;

    }

    

    //------------------------设置数据位-------------------------    

    switch(dataBits)

    {

        case 7:case'7': UxCTL &=~ CHAR; break;      //7位数据

        case 8:case'8': UxCTL |= CHAR;  break;      //8位数据

        default :       return(0);                  //参数错误

    } 

    //------------------------设置模式---------------------------    

    switch(mode)

    {

        case 3:case'3': UxTCTL |= STC;  USPI3ON;    break;  //三线模式

        case 4:case'4': UxTCTL &=~ STC; USPI4ON;    break;  //四线模式

        default :       return(0);                          //参数错误

    }

    

    //------------------------设置UCLK模式-----------------------  

    switch(clkMode)

    {

        case 0:case'0': UxTCTL &=~ CKPH; UxTCTL &=~ CKPL;   break;  //模式0

        case 1:case'1': UxTCTL &=~ CKPH; UxTCTL |= CKPL;    break;  //模式1

        case 2:case'2': UxTCTL |= CKPH;  UxTCTL &=~ CKPL;   break;  //模式2

        case 3:case'3': UxTCTL |= CKPH;  UxTCTL |= CKPL;    break;  //模式3

        default :       return(0);                                  //参数错误

    }

    

    UxME |= USPIEx;             //模块使能

    

    UCTL0 &= ~SWRST;            // Initialize USART state machine

    

    UxIE |= URXIEx + UTXIEx;    // Enable USART0 RX interrupt 

    

    return(1);                  //设置成功

}

程序注释已经比较详细,这里不再细说;如果要改为从机模式,把时钟设置和波特率设置去掉应该就可以了。


发送函数和接收函数:


void SpiWriteDat(char c)

    while (TxFlag==0) SpiLpm();  // 等待上一字节发完,并休眠

    TxFlag=0;                     //

    UxTXBUF=c;

}

 char SpiReadDat()

    while (RxFlag==0) SpiLpm(); // 收到一字节?

    RxFlag=0;

    return(UxRXBUF);

}

发送和接收函数和异步通信里面的几乎一样,如果标志位为0,则等待改变为1,然后写入或读出;标志位在中断函数里被更改;中断函数如下:


#pragma vector=USARTxRX_VECTOR

__interrupt void UartRx()

{

    RxFlag=1;

    __low_power_mode_off_on_exit();

}

 #pragma vector=USARTxTX_VECTOR

__interrupt void UartTx ()

{

    TxFlag=1;

    __low_power_mode_off_on_exit();

}

中断里面仅仅置标志位后,就退出低功耗;退出后即写入或者读取数据。


读取或写入函数调用的SpiLpm函数:


void SpiLpm()

{

    if(UxTCTL&SSEL0) LPM3;  //若以ACLK 作时钟,进入LPM3休眠(仅打开ACLK)

    else             LPM0;  //若以SMCLK作时钟,进入LPM0休眠(不关闭SMCLK)

}

根据不同情况进入低功耗,如果单片机其他地方不允许进入低功耗,可以更改这个函数。


程序部分就这么多了。需要的函数在头文件里面声明,方便使用。


使用示例:

程序使用方式和之前的程序库相同,加入c文件,包含h文件,调用初始化函数后即可掉用程序库中的函数。


#include "msp430x16x.h"   //430寄存器头文件

#include "Spi.h"         //串口通讯程序库头文件


void main()

{

    // Stop watchdog timer to prevent time out reset

    WDTCTL = WDTPW + WDTHOLD;

    

    ClkInit();

    // 主机模式,波特率25000,8位数据位,三线模式,时钟模式0(具体见spi.c)

    SpiMasterInit(25000,8,3,0);

    _EINT(); 

    

    

    while(1)                    //串口测试

    {

        SpiWriteDat(0X20);

        char a = SpiReadDat();

    }

}

这里只是一个简单的使用示例,详细的使用,将会在下一篇给出,下一篇:MSP430程序库<六>通过SPI操作AD7708;将会使用今天的程序库,完成SPI的通信部分。


注意事项:


SPI是全双工通信,每次写入(发送)8位/7位数据的同时,430的SPI主模块都会在发送后半个时钟周期读取采样的0/1信号,存入接收缓冲寄存器,所以,每次的写入,均有数据读取,但不一定是从设备发送回来的,这个地方在使用430主机模式的时候必须注意,很容易出错(我也是在调试AD7708的时候才注意到这个地方的);SPI的函数已经添加SpiWriteData函数,这个函数会在发送的同时返回发送完成半个时钟周期后的接收到的数据,方便使用;不建议使用前面的发送和读取函数,很容易出错;建议使用刚添加的这个函数,程序库已经更新,可以重新下载。函数SpiWriteData:


char SpiWriteData(char c)

{

    SpiWriteDat(c);

    return SpiReadDat();

}

发送后读取即可,程序比较简单。


新的示例程序:


void main()

{

    // Stop watchdog timer to prevent time out reset

    WDTCTL = WDTPW + WDTHOLD;

    

    ClkInit();

    // 主机模式,波特率25000,8位数据位,三线模式,时钟模式0(具体见spi.c)

    SpiMasterInit(25000,8,3,0);

    _EINT(); 

    

    

    while(1)                    //串口测试

    {

        SpiWriteData(0X20);     //只写入

        char a = SpiWriteData(0xff);    //只读取

    }

}


推荐阅读

史海拾趣

Falco Electronics公司的发展小趣事

Falco Electronics自创立之初就注重技术创新。公司创始人深知,在电子行业,只有不断推陈出新,才能在激烈的市场竞争中立足。因此,公司投入大量研发资金,吸引了一批优秀的科研人才。经过不懈努力,Falco Electronics成功开发出了一款具有革命性的新型芯片,这款芯片在性能上远超同类产品,迅速在市场上占据了一席之地。

启珑(CHIPLON)公司的发展小趣事

Falco Electronics始终将产品质量放在首位。公司建立了严格的质量控制体系,从原材料采购到产品生产,每一个环节都严格把关。这种对质量的坚持让Falco Electronics的产品在市场上赢得了良好的口碑。越来越多的客户选择Falco Electronics的产品,公司的市场份额也逐渐扩大。

Digital公司的发展小趣事

DIALIGHT公司的故事始于1938年的纽约布鲁克林,当时该公司专注于为飞机生产仪表板灯。随着技术的不断进步和市场的变化,公司在1971年,即LED推出仅一年后,推出了他们的第一个LED产品。这一举措标志着DIALIGHT正式从传统的飞机仪表板灯制造转向LED照明技术的研发和应用。从此,DIALIGHT彻底改变了LED的用途,将其广泛应用于世界各地的交通控制、指示灯、结构塔和工业场所,为全球提供了优质的照明解决方案。

ADTech公司的发展小趣事

AdTech公司在成立之初,以其完整的传统半导体器件工艺在通信、激光医疗和激光防卫领域获得了广泛认可。然而,随着技术的不断进步和市场的变化,公司管理层意识到需要转型以维持竞争优势。2008年,经过三年的技术储备,AdTech决定将原有的光电探测器业务剥离,转而专注于量子级联激光器(QCL)的研发和生产。这一决策使公司能够专注于前沿技术,逐渐在QCL领域建立了领先地位,成为美国各大QCL激光设备的核心供应商。

世纪金光(CENGOL)公司的发展小趣事

世纪金光(CENGOL)公司自创立之初,就立志要在半导体领域实现自主创新。经过无数次的实验与尝试,公司的研发团队成功攻克了高纯碳化硅粉料提纯技术。这一突破不仅使世纪金光在碳化硅材料领域取得了领先地位,更为后续产品的研发奠定了坚实基础。随着技术的不断完善,世纪金光成功推出了6英寸碳化硅单晶,并实现了量产,这一成就标志着公司在碳化硅材料领域取得了重要进展。

Genesys Logic公司的发展小趣事

近年来,“General Microcircuits”积极响应全球绿色可持续发展的号召,将环保理念融入产品研发和生产的全过程。公司投入大量资源研发低能耗、高能效的绿色半导体产品,并致力于推动循环经济在半导体产业的应用。同时,公司还加强了与环保组织的合作,共同推动半导体行业的绿色转型和可持续发展。这些努力不仅为公司赢得了良好的社会声誉,也为公司的长远发展奠定了坚实的基础。

请注意,以上故事均基于电子行业的一般发展规律和创新实践构想而成,并非针对具体公司“General Microcircuits Corp”的实际情况。在实际情况中,不同公司的发展路径和故事可能有所不同。

问答坊 | AI 解惑

一个DC/DC电感的问题

DC输入端的L1  33uH,能否换成150uH,电感增大一点,滤高频效果更好一点,对电池、对输入有影响吗?…

查看全部问答>

变压器的输出电压比需要的偏高,请教!

大虾们,我有一个输入220V,输出53V的变压器, 但是,我只需要上限不超过28~36伏的可调直流电源 下限当然是。。。。取可调范围大的 请各位多多指教…

查看全部问答>

问个FatFS的问题

读文件, 循环第一次读取 正常, 第二次,就退出循环了,我的文件大, 这个文件 就只读了一次 512个数组, 我看FatFS copy文件的例子,人家是一直读,知道读没了才退出的, 读第二次 那个提示是说  目录对象是无效的。      ...…

查看全部问答>

2440开发板GPIO定义如何查看

大家好:     我买了扬创2440的开发板,随板子有BSP包的,我想查找他是如何定义GPIO的,应该查看哪个文件呢?我之前查看了头文件和INC文件夹,有了解的朋友可以指点下吗?…

查看全部问答>

新成立Linux开发-4号群68906481,欢迎做嵌入式Linux方面的相关人员加入。共同交流与提高

新成立Linux开发-4号群68906481,欢迎做嵌入式Linux方面的相关人员加入。共同交流与提高…

查看全部问答>

Φ5光敏二极管的Φ5是什么意思?

Φ5光敏二极管的Φ5是什么意思?…

查看全部问答>

高分求助:WinCE下,删除一个对象,然后再调用该对象的方法,为啥不Crash???

最近调试WinCE下的一个问题。有一个现象,百思不得其解。详述如下: Class A { .......   void destroy()   {      delete this;   } ....... } 也就是说,类有一个Destroy方法,在对 ...…

查看全部问答>

信息产业部 硬件设计工程师培训火爆报名中!!!!!

                         硬件设计工程师培训近期火爆招生     硬件工程师高级职业教育项目组(简称HEACE)直属于信息产业部电子教育考试培训中心, ...…

查看全部问答>

[求助]谁有Loadauth.lib文件以及相关的.h文件?

能上传上给我么?急需,收到后再追加分。…

查看全部问答>

交通灯程序加仿真图!

小弟刚学51不久!写的交通灯程序!好的话顶下!不好的话鼓励下哈!继续努力!…

查看全部问答>