SPI总线及其demo

news2025/1/16 15:06:12

SPI总线是微处理器和外设之间进行数据交互的常用串行总线接口。本文主要介绍了SPI的接口类型、数据传输(时钟极性、时钟相位)、读、写操作的具体代码,最后是一个完整的xpt2046实现ADC的代码实现及注意事项。

1 、接口简介

串行外设接口(SPI)是微控制器(MCU)和外围IC(如传感器、ADC、DAC、移位寄存器、SRAM等)之间使用较广泛的接口之一。SPI是一种同步、全双工、主从式接口。来自主机或者从机的数据在时钟上升沿或下降沿同步。主机和从机可以同时传输数据。SPI接口可以是三线式或者4线式。本文主要介绍4线式SPI接口。

图1 SPI总线:单一主机对单一从机模式

4线SPI总线有四个信号:

时钟(SPI CLK,SCLK)。

片选(CS)。

主机输出、从机输入(MOSI)。

主机输入、从机输出(MISO)。

产生时钟信号的器件称为主机(MCU)。主机和从机之间传输的数据与主机产生的时钟同步。SPI接口只能有一个主机,但可以有多个从机。

图2 SPI总线:单一主机对多从机模式(2个从机)

来自主机的片选信号用于选择从机。这通常是一个低电平有效的信号,拉高时,从机与主机断开连接。当使用多个从机时,主机需要为每个从机提供单独的片选信号。MOSI和MISO是数据线。MOSI将数据从主机发送到从机,MISO将数据从从机发送到主机,实现主机和从机的数据交互。

2、 数据传输

要开始SPI通信,主机通过使能CS信号选择从机,发送时钟信号。片选(CS)一般是低电平有效信号。因此,主机必须在该信号上发送逻辑“0”以选择从机。SPI是全双工通信接口,主机和从机可以分别通过MOSI和MISO线路发送和接收数据(数据交互)。在SPI通信期间,数据的发送(串行移出到MOSI/MISO)和接收(MISO/MOSI)可以同时进行。串行时钟沿同步数据的移位和采样。SPI接口允许用户灵活选择上升沿或者下降沿采样(读操作,输入)或者移位(写操作,输出)。读、写操作字节都是从最高位(MSB)开始的。

图3 SPI通信读、写的bit顺序

3 、时钟极性(clock polarity,CPOL)与时钟相位(clock phase,CPHA)

在SPI通信接口中,主机可以选择时钟极性和时钟相位。空闲状态就是片选(CS)无效时的状态。在空闲状态期间,CPOL位设置时钟信号(SCLK)的极性。

表1 CPOL极性规定

CPHA位选择时钟相位,规定主机采样数据时的时钟边沿,包括第一个跳变沿和第二个跳变沿。

表2 CPHA极性规定

根据CPOL位和CPHA位的不同组合,有四种SPI模式可用。

表3 CPOL位和CPHA位的不同组合条件下的SPI模式

图4 CPOL和CPHA时序关系

主机在与采用SPI通信接口的从机(ADC(xpt2046))数据交互过程中,要严格按照从机的时序要求进行读写操作,且从机的SPI通信时序变体很多,具体有从机采用只读模式、从机采用只写模式、先写后读模式、数据位数不同等配置非常灵活。并不是只有图4中的模式类别,具体读/写操作要参考从机的数据手册。

4 、读写操作(代码实现)

4.1、 写(主机在上升沿往从机写1byte数据):

void send_byte(dat)  //主机发送数据到从机  写操作
//数据传输顺序:MSB→LSB
{
    unsigned char i;
    cs_n  = 0;
    for(i=0;i<8;i++)
    {
        sclk    = 0;
        if(dat & 0x80)
        {
            mosi = 1;   
        }
        else
        {
            mosi = 0;   
        }
        sclk = 1;//上升沿锁存数据
        dat = (dat << 1);   
    }
    sclk = 0;//拉低dclk,方便后续操作
}

4.2 、读(主机在时钟的下降沿从从机读取一个字节的数据)

unsigned char receive_byte()
{
    unsigned char dat     = 0x00;
    unsigned char shifter = 0x80;
    unsigned char i;
    cs_n = 0;
    sclk = 0;
    for(i=0;i<8;i++)
    {
        sclk = 1;
        sclk = 0;//下降沿dout数据移出 miso从机发送数据到主机
        if(miso)
        {
            dat = (dat | shifter);      
        }
        else
        {
            dat = dat;      
        }
        shifter = (shifter >> 1);
        
    }
    sclk = 0;//拉低dclk,方便后续操作。
    cs_n = 1;//结束主机、从机数据交互过程。
    return dat;
}

5 、例子

5.1、 ADC(XPT2046 Vbat)

图5 ADC(XPT2046 Vbat)时序(12bit)

控制字节的具体含义见xpt2046数据手册。

代码实现:

1       /*
2       程序功能:A/D转换     采集电位器的电压值。
3       state:success!!
4  
5       */
6       #include <reg52.h>
7       //led灯
8       sbit led_10 = P1^0;
9       sbit led_11 = P1^1;
10      sbit led_12 = P1^2;
11      sbit led_13 = P1^3;
12      sbit led_14 = P1^4;
13      sbit led_15 = P1^5;
14      sbit led_16 = P1^6;
15      sbit led_17 = P1^7;
16      //A/D
17      sbit dclk      = P2^1;
18      sbit xpt_in    = P2^0;
19      sbit xpt_out   = P2^5;
20      sbit xpt_cs_n  = P3^7; //低电平有效     _n表示低电平有效
21      //数码管显示
22      //位选
23      sbit wei_enable  = P2^7;  //与锁存器配合使用
24      //个位→0x7f;十位→0xbf;百位→0xdf;千位→0xef;万位→0xf7;十万位→0xfb;百万位→0xfd;千万位→0xfe;
25      sbit duan_enable = P2^6;  //与锁存器配合使用
26      //0x3f→0;0x06→1;0x5b→2;0x4f→3;0x66→4;0x6d→5;0x7d→6;0x07→7;0x7f→8;0x6f→9;
27      //0x77→A;0x7c→B;0x39→C;0x5e→D;0x79→E;0x71→F;0x76→H;0x38→L;0x40→-;0x00→熄灭
28      unsigned char byte_ctrl = 0xa4;  //1010_0100 0xa4  配置成低功耗模式,不然ADC结果不准,至于原因,不清楚。
29      unsigned int receive_dat = 0x0000;
30      //自定义函数声明:
31      //0.delay
32      void delay_us(unsigned int z);
33      //1.send_byte_ctrl
34      void send_byte_ctrl(dat);
35      //2.receive_data
36      unsigned int receive_data_AD();
37      //3.led_display_dec_3
38      void led_display_dec_3(unsigned int dat);//小数点后两位
39 
40      //main函数
41      void main()
42      {
43 
44          //display
45          while(1)
46          {
47              send_byte_ctrl(byte_ctrl);
48              receive_dat = receive_data_AD();
49              led_display_dec_3(receive_dat); 
50              led_10 = 0; 
51          }
52 
53      }
54      //
55      //0.delay
56      void delay_us(unsigned int z)
57      {
58          unsigned int x;
59          for(x=0;x<z;x++);
60      }
61      //
62      //1.send_byte_ctrl
63      void send_byte_ctrl(dat)   //xpt_cs = 0时,数据在dclk上升沿时锁存进来
64      //数据传输顺序:MSB→LSB
65      {
66          unsigned char i;
67          xpt_cs_n  = 0;
68          for(i=0;i<8;i++)
69          {
70              dclk    = 0;
71              if(dat & 0x80)
72              {
73                  xpt_in = 1; 
74              }
75              else
76              {
77                  xpt_in = 0; 
78              }
79              dclk = 1;//上升沿锁存数据
80              dat = (dat << 1);   
81          }
82          dclk = 0;//拉低dclk,方便后续操作。
83          for(i=0;i<6;i++)
84          {
85              dclk = 0;   
86          }
87          
88      }
89      //
90      //2.receive_data
91      unsigned int receive_data_AD()
92      {
93          unsigned int dat     = 0x0000;
94          unsigned int shifter = 0x0800;
95          unsigned char i;
96          xpt_cs_n = 0;
97          dclk = 0;
98          for(i=0;i<12;i++)
99          {
100             dclk = 1;
101             dclk = 0;//下降沿dout数据移出
102             if(xpt_out)
103             {
104                 dat = (dat | shifter);      
105             }
106             else
107             {
108                 dat = dat;      
109             }
110             shifter = (shifter >> 1);
111             
112         }
113         dclk = 0;//拉低dclk,方便后续操作。
114         for(i=0;i<3;i++)
115         {
116             dclk = 0;
117             dclk = 1;   
118         }
119         xpt_cs_n = 1;//结束A/D转换操作。
120         dat = (dat * 8.4); //显示小数点后两位  实测:VCC=4.3,VCC/4=1.075V,对应得数值是1280(十进制),1.075/1280=8.4e-4。
121         return dat;
122     }
123     //
124     //3.led_display_dec_3
125     void led_display_dec_3(unsigned int dat)//dec表示10进制
126     {
127         unsigned char wei_point_2;
128         unsigned char wei_point_1;
129         unsigned char wei_point;
130         unsigned char wei_ge;
131         wei_enable  = 0;
132         duan_enable = 0;
133         wei_ge = dat / 10000;               //12345   1  个位
134         wei_point_1 = dat % 10000 /1000;    //12345   2  小数点后第一位
135         wei_point_2 = dat % 1000 / 100;     //12345   3  小数点后第二位
136         //wei_point_2
137         //位选
138         P0 = 0x7f;//个位
139         wei_enable = 1;
140         wei_enable = 0;
141         //段选
142         switch(wei_point_2)
143         {
144             case 0 : { P0 = 0x3f; }break;
145             case 1 : { P0 = 0x06; }break;
146             case 2 : { P0 = 0x5b; }break;
147             case 3 : { P0 = 0x4f; }break;
148             case 4 : { P0 = 0x66; }break;
149             case 5 : { P0 = 0x6d; }break;
150             case 6 : { P0 = 0x7d; }break;
151             case 7 : { P0 = 0x07; }break;
152             case 8 : { P0 = 0x7f; }break;
153             case 9 : { P0 = 0x6f; }break;
154             default: { P0 = 0x3f; }break;       
155         }
156         duan_enable = 1;
157         duan_enable = 0;
158         delay_us(300);
159         //wei_point_1
160         //位选
161         P0 = 0xbf; //十位
162         wei_enable = 1;
163         wei_enable = 0;
164         //段选
165         switch(wei_point_1)
166         {
167             case 0 : { P0 = 0x3f; }break; 
168             case 1 : { P0 = 0x06; }break; 
169             case 2 : { P0 = 0x5b; }break; 
170             case 3 : { P0 = 0x4f; }break; 
171             case 4 : { P0 = 0x66; }break; 
172             case 5 : { P0 = 0x6d; }break; 
173             case 6 : { P0 = 0x7d; }break; 
174             case 7 : { P0 = 0x07; }break; 
175             case 8 : { P0 = 0x7f; }break; 
176             case 9 : { P0 = 0x6f; }break; 
177             default: { P0 = 0x3f; }break;
178         }
179         duan_enable = 1;
180         duan_enable = 0;
181         delay_us(300);
182         //wei_ge
183         //位选
184         P0 = 0xdf; //百位
185         wei_enable = 1;
186         wei_enable = 0;     
187         //段选
188         switch(wei_ge)
189         {
190             case 0 : { P0 = 0xbf; }break;//0xbf
191             case 1 : { P0 = 0x86; }break;//0x86
192             case 2 : { P0 = 0xdb; }break;//0xdb
193             case 3 : { P0 = 0xcf; }break;//0xcf
194             case 4 : { P0 = 0xe6; }break;//0xe6
195             case 5 : { P0 = 0xed; }break;//0xed
196             case 6 : { P0 = 0xfd; }break;//0xfd
197             case 7 : { P0 = 0x87; }break;//0x87
198             case 8 : { P0 = 0xff; }break;//0xff
199             case 9 : { P0 = 0xef; }break;//0xef
200             default: { P0 = 0x3f; }break;       
201         }
202         duan_enable = 1;
203         duan_enable = 0;
204         delay_us(300);
205     }

6 、注意事项

①缺乏数据接收确认机制。

②单主机。

③xpt2046进行ADC时,要配置成低功耗模式(PD1=0,PD0=0),不然ADC结果不准,至于为什么,不清楚。

参考文献:

1.https://aticleworld.com/spi-communication-protocol/

2.https://www.analog.com/cn/analog-dialogue/articles/introduction-to-spi-interface.html

本人水平有限,如有错误,望大佬多多指教。非常感谢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1104513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全流程TOUGH系列软件实践技术应用

TOUGH系列软件是由美国劳伦斯伯克利实验室开发的&#xff0c;旨在解决非饱和带中地下水、热运移的通用模拟软件。和传统地下水模拟软件Feflow和Modflow不同&#xff0c;TOUGH系列软件采用模块化设计和有限积分差网格剖分方法&#xff0c;通过配合不同状态方程&#xff08;EOS模…

c/c++语言算法技巧汇总/大复习[未完结]

目录 最简单的hash表子串变位词 排序最简单的桶排序字典序快排练习1 &#xff1a;0-1交换练习2&#xff1a;交换星号 最简单的去重最简单的队列template模版双端队列vector or listvector 队列结构存在于两种算法广度优先搜索贝尔曼福特算法&#xff08;Bellman-Ford&#xff0…

华为荣耀手机,开启开发者选项,hbuilder调试依然找不到

我的手机是华为荣耀50&#xff0c;其他华为手机不知道是不是这个问题哦 解决办法&#xff1a;usb配置&#xff0c;选择音频来源 然后就可以了

QGIS选择某一个元素,并生成新的图层

第一步&#xff1a;选择元素 第二步&#xff1a;编辑 —> 复制 第三步&#xff1a;Edit —> paste features as —> new vector layer 输入文件名&#xff1a; 生成新图层

【计算机网络笔记】计算机网络性能(1)——速率、带宽、延迟

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 系列文章目录速率带宽延迟/时延(delay或latency) 下面介绍一些计算机网络中常用的性能指标。在本篇中涉及速…

iPhone怎么滚动截长图?分享3个截图技巧!

苹果手机虽然深受大家喜爱&#xff0c;但是有一些功能确实没有安卓方便。就像今天小编要讲的长截图功能&#xff0c;苹果手机本身并不具备。 但是&#xff0c;我们可以借助其他外力来实现长截图&#xff0c;这些方法往往容易被大家忽视。那么&#xff0c;iphone怎么滚动截长图…

零代码编程:用ChatGPT根据excel表格数据来批量修改文件标题名

文件夹里面有很多个mp文件&#xff0c;没有顺序&#xff0c;很乱&#xff1a; 这些文件其实都是有顺序的&#xff0c;Excel表格中前面的数字就是序号&#xff1a; 现在&#xff0c;希望根据Excel表格中的标题名来对文件夹里的mp4文件进行批量重命名&#xff0c;在ChatGPT中输入…

studio one6值不值得下载?好用吗

零基础学混音&#xff0c;持之以恒才能有所收获。首先要明确自己的学习目标&#xff0c;然后选择适合自己的教程。这套教程适用于后期制作和直播&#xff0c;同样适用。我切换到了桌面屏幕。 在这个基础上运行&#xff0c;它提供了适合零基础的模板&#xff0c;适合直播唱歌或…

React函数式写法和类式写法的区别(以一个计数器功能为例子)

函数式写法更加简洁和函数式编程思维导向&#xff0c;适用于无状态、UI纯粹的组件&#xff0c;且可以使用Hooks处理副作用。而类式写法适用于有内部状态、生命周期方法和复杂交互逻辑的组件&#xff0c;提供了更多的灵活性和控制力。 文章目录 一、计数器功能演示 1.函数式写法…

List.of() Vs Arrays.asList()

java中list.of和Arrays.asList方法有什么区别&#xff1f; 简介 Java 提供了几种用于创建列表的方便方法&#xff0c;包括 List.of 和 Arrays.aslist。尽管这两种方法都可以很简单的创建集合对象&#xff0c;但它们实际上是有一些显著差异的。本文将介绍 Java 中的 List.of()…

【补档】基于PyTorch的手写数字识别

“过去都是假的,回忆是一条没有归途的路,以往的一切春天都无法复原,即使最狂热最坚贞的爱情,归根结底也不过是一种瞬息即逝的现实,唯有孤独永恒。” 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领…

新手程序员怎么接单?将超能力转化为 “钞能力” ?

程序员如何在自己年富力强的时候&#xff0c;最大化发挥自己的能力&#xff1f;将超能力转化为 “钞能力” &#xff1f; 有人还在苦哈哈当老黄牛&#xff0c;一身使不完的牛劲&#xff0c;有人已经另辟蹊径&#xff0c;开创了自己的一片致富小天地。 接单找兼职&#xff0c;…

PRCV 2023:语言模型与视觉生态如何协同?合合信息瞄准“多模态”技术

近期&#xff0c;2023年中国模式识别与计算机视觉大会&#xff08;PRCV&#xff09;在厦门成功举行。大会由中国计算机学会&#xff08;CCF&#xff09;、中国自动化学会&#xff08;CAA&#xff09;、中国图象图形学学会&#xff08;CSIG&#xff09;和中国人工智能学会&#…

最新GMS地下水数值模拟教程

详情点击公众号链接&#xff1a;最新GMS地下水数值模拟教程 前言 GMS三维地质结构建模 GMS地下水流数值模拟 GMS溶质运移数值模拟与反应性溶质运移模 目标 1.GMS的建模数据的收集、数据预处理以及格式等&#xff1b; 2.GMS的建模流程&#xff0c;包括三维地质结构建模、直接…

双硬盘安装双系统失败,grub-install to /dev/sd* failed, fatal error

一个机械硬盘一个固态硬盘&#xff0c;如果只装双系统推荐各自的系统的引导都放在各自的硬盘上&#xff0c;除非一个硬盘上想装两个系统。 安装失败&#xff0c;在写引导程序时提示grub-install /dev/sd* failed, fatal error&#xff0c;这种问题有各种各样的原因&#xff0…

脉冲信号485信号翻斗式雨量传感器的区别

本产品是一种水文、气象仪器&#xff0c;用于测量自然界降雨量&#xff0c;同时将降雨量转换为以开关量形式表示的数字信息量输出&#xff0c;以满足信息传输、处理、记录和显示等的需要。 本仪器由承雨器部件和计量部件等组成。承雨口采用口径Φ200mm。计量组件是一个翻斗式机…

选择适合自己的ERP系统

ERP系统是企业管理的核心工具之一&#xff0c;它能够整合各个业务部门的数据和流程&#xff0c;提供一个全面的管理平台。然而&#xff0c;在众多ERP系统中选择适合自己的一款并非易事。本文将全方位介绍选择适合自己的ERP系统的关键因素、评估步骤和注意事项&#xff0c;帮助企…

Linux常用命令——comm命令

在线Linux命令查询工具 comm 两个文件之间的比较 补充说明 comm命令可以用于两个文件之间的比较&#xff0c;它有一些选项可以用来调整输出&#xff0c;以便执行交集、求差、以及差集操作。 交集&#xff1a;打印出两个文件所共有的行。求差&#xff1a;打印出指定文件所包…

Django REST Framework完整教程-认证与权限-JWT的使用

文章目录 1.认证(Authentication)与权限(Permission)1.1.视图添加权限1.2.登录验证1.3.常用DRF自带权限类1.4.自定义权限类1.5.全局权限1.6.函数视图权限 2.认证详解2.1.认证方案2.2.如何使用TokenAuthentication&#xff1f; 3.JSON Web Token(JWT)认证3.1.工作原理3.2.安装3.…

【MySQL】分析SQL的几种方式

文章目录 一、查看SQL执行频率二、定位低效率执行SQL1. show processlist2. 慢查询日志 三、explain分析执行计划1. id2. select_type3. type4. key5. extra 四、show profile 一、查看SQL执行频率 show session status&#xff1a;显示 session 级的统计结果&#xff08;不写…