Waveshare
直播中

南惜北

10年用户 89经验值
擅长:可编程逻辑 处理器/DSP
私信 关注
[经验]

【XNUCLEO-F030R8试用体验】之七:mbed开发之SPI

XNUCLEO-F030R8试用体验】之七:mbed开发之SPI
SPI是英语 Serial Peripheral interface 的缩写,译为串行外围设备接口,是 Motorola首先在其 MC68HCXX 系列处理器上定义的。SPI,是一种高速、全双工、同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信。
1.png
SPI 接口使用4 条线通信:
MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。
从图中可以看出,主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。寄存器通过MOSI信号线将字节传送给从机,从机也将自己的移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。
SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性( CPOL)和相位( CPHA)可以进行配置。CPOL和CPHA分别取0或1,就构成了SPI通信的4种工作模式。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电平。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果 CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。 SPI 主模块和与之通信的外设备时钟相位和极性应该一致。
下图是4种工作模式的时序图:
2.png
关于SPI的知识大致就是这些,我们来看看mbed提供的库函数有哪些:
3.png
这里提供了两类函数库,一类是SPI,适用于SPI主机,另一类是SPISlave,适用于SPI从机。
SPI接口的器件很多,主要应用在EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。为了使效果更明显,这里用8个数码管做显示,用SPI接口的MAX7219芯片驱动,显示数字。
MAX7219是串行输入/输出共阴极显示驱动器,它连接微处理器与8位数字的7段数字LED显示,也可以连接条线图显示器或者64个独立的LED。其上包括一个片上的B型BCD编码器、多路扫描回路,段字驱动器,而且还有一个8*8的静态RAM用来存储每一个数据。 只有一个外部寄存器用来设置各个LED的段电流。
模块的5个引脚分别为:VCC、GND、DIN(MOSI)、CS、SCLK,这里省去了MISO引脚,对于显示模块,一般来说仅有输出就够了。
4.png
5.png
MAX7219时序图:
6.png
从时序图中我们可以分析出,采用SPI通信接口与MAX7219芯片通信时,CPOL=0(串行同步时钟的空闲状态为低电平),CPHA=0,这点在编写程序时比较重要。
关于MAX7219的资料见附件,这里不再详细说明。简单介绍一下初始化步骤:
1.     设置解码模式,地址0x09,指令0xff(对0~7位数码都解码)
2.     设置亮度,地址0x0a,指令0x08。
3.     设定扫描控制,地址0x0b,指令0x07(扫描0~7位)。
4.     设定电源模式,地址0x0c,指令0x01(正常显示模式)。
完成以上初始化步骤后,想在对应位置显示某个数字,只要向0x01~0x08位写入相应的数字就可以显示了。以下是一个简单的显示程序:
#include "mbed.h"
SPI max_7219(D11,D12,D13);
DigitalOut PIN_CS(D8);
DigitalOut myled(D2);
void max7219_init(void);                                                                       //7219初始化函数
void max7219_write(int addr,int val);                                                //7219写数据函数
charbuffer[]={0x05,0x02,0x00,0x01,0x03,0x01,0x04};        //显示的数字
char code[]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};     //8个位的数据寄存器
int main()
{
   max_7219.format(8,0);
   max_7219.frequency(1000);
   max7219_init();
   int i,j=7;
   for(i=0;i<8;i++)
    {
       max7219_write(code,buffer[j]|0x80);
       j--;
       //wait(1);
       myled=!myled;
    }
   //while(1);
}
void max7219_write(int addr,int val)
{
   PIN_CS = 0;
   max_7219.write(addr);
   max_7219.write(val);
   PIN_CS = 1;  
}
void max7219_init(void)
{
   max7219_write(0x09,0xff);                                   //解码模式
   max7219_write(0x0a,0x08);                                     //设置亮度
   max7219_write(0x0b,0x07);                                     //扫描控制
   max7219_write(0x0c,0x01);                                      //正常显示
   //max7219_write(0x0f,0x00);                                            //显示检测,可以省略
   myled = 1;
}
这样的方式显示起来比较麻烦,我们可以尝试写一个库,把常用的功能用库函数的方式呈现出来,方便调用。下面的例子就用到了笔者自己编写的一个MAX7219的驱动库,它可以显示整数和浮点数,使用起来非常简单。现在我们写一个程序,用ADC采集电压,然后在数码管上显示。主程序很简单,如下:
#include "mbed.h"
#include "Smg_7219.h"
Serial pc1(USBTX,USBRX);
AnalogIn light(A0);
Smg_7219 my7219(D11,D12,D13,D8);
float value;
int main(void)
{
while(1)
  {
   value= light.read();
   my7219.ShowFnum(value*3.3);
   pc1.printf("value is %fn",value*3.3);
   wait_ms(500);
  }
}
这里包含了笔者编写的数码管驱动库Smg_7219.h,这里只实现了两个函数,一个用来显示整数void ShowNum(float num),一个用来显示浮点数void ShowFnum(float num),源代码会在附件里提供下载,感兴趣的读者可以自行扩充。程序思路很简单,采集A0口的电压,在数码管上显示电压值,同时在串口输出,每500ms采样一次。A0口采样的电压由光敏电阻和电阻分压得到。
下面是实验现象:
8.png
较强光照下电压为1.8V左右(采集的是光敏电阻和地之间的电压,下同)
9.png
强光下电压为0.87V左右
10.png
串口收到的数据
这里谈一点自己的体会吧,虽然这个库很简单,用户函数只有两个,但是在编写时花费的时间也有大半天,后面调试加上下载验证,功能改进等又过去了大半天,总耗时将近一天。实际上,这应该是笔者第一个用C++编写的驱动库(以前都是用C),虽然很艰难,但总归迈出了第一步。官方提供的库函数如DigitalInOut、AnalogIn等,用起来很简单,但是自己动手写一个简单的库却并不容易,学习得越深入,越感觉mbed确实非常好用和易用,大大简化了底层驱动,灵活性很强。另一方面,mbed的库仅仅提供了基础的功能,以STM32的GPIO为例,根据官方库函数,其输出可以配置成不同的模式,如推挽输出、开漏输出、复用输出等,其输出还可设定输出速率以减小功耗。在mbed里就直接简化为输入和输出两种。同理,其他片上外设也做了简化。这是一种通过牺牲复杂的功能来换取易用性的尝试,可以说有得有失,这也是一种中庸之道吧。
下期将带来LCD1602液晶的讲解。
spi_7219_Liarbry.zip (1.95 MB)
(下载次数: 42, 2016-6-22 22:54 上传)

回帖(3)

yangwb00

2016-6-23 09:47:49
顶顶顶
举报

李爱

2018-3-15 06:51:21
楼主,mbed OS main之前的准备相关资料么
举报

WUEVEN

2019-4-23 10:48:04
楼主你的讲解非常好,都看懂了。而且,楼主用c++基于mbed类库,实现驱动的方法,很值得借鉴
现在正在做一个项目,是SPI驱动DAC的,也计划按照楼主的这种方式实现
举报

更多回帖

×
20
完善资料,
赚取积分