STM32之SPI通信

发布者:TranquilGaze最新更新时间:2019-04-11 来源: eefocus关键字:STM32  SPI通信  Flash芯片 手机看文章 扫描二维码
随时随地手机看文章

之前一直对SPI通信一知半解,所以想抽空把它搞得明白一些。考虑到之前是结合Flash芯片来学的,十分不直观,而且主要把时间和精力都花在Flash芯片的datasheet和驱动上了,SPI通信也没学好。所以这次就考虑用4位数码管显示模块,模块是直接买的现成的,如下图所示,这样可以简化操作,把精力聚焦到学习的核心–SPI通信本身上来。 


 这里写图片描述 


该模块是用2片74HC595串联驱动的,一片用来控制数码管的位选(U1),一片用来控制数码管的段选(U2)。接口比较简单,总共5个引脚,2个引脚分别接VCC和GND,DIO用来接收串行数据的输入,SCLK用来接收同步时钟,每个SCLK上升沿74HC595内部的移位寄存器会移一位,RCLK用来控制数据的输出,每个RCLK上升沿74HC595内部的移位寄存器的数据会被放进存储寄存器并输出到外部引脚QA~QH上。而QH’是串行输出引脚,该引脚会接收最高位的溢出,从而实现多片74HC595的级联。 

 

这里写图片描述 


当两片74HC595串联时,先发八位数据用于段选,再发八位数据用于位选,然后RCLK上升沿,就可以驱动某位数码管显示某个字符,通过动态扫描数码管,由于人眼的视觉暂停效果,就可以实现4位数码管的同时显示。先用通用I/O来实现该数码管的驱动,程序如下: 

头文件74HC595.h


#ifndef __74HC595_H__

#define __74HC595_H__


#include"stm32f10x_lib.h"       //包含所有的头文件

#include


// 4-Bit LED Digital Tube Module

#define  HC595_SCLK_PIN  GPIO_Pin_5   // SPI1_SCK  PA5

#define  HC595_RCLK_PIN  GPIO_Pin_12   // SPI1_NSS  PA4

#define  HC595_DIO_PIN   GPIO_Pin_7   // SPI1_MOSI PA7

#define  HC595_GPIO           GPIOA 

#define  HC595_RCLK_GPIO      GPIOB 

#define  HC595_RCC            RCC_APB2Periph_GPIOA 

#define  HC595_RCLK_RCC       RCC_APB2Periph_GPIOB 


void HC595_Init(void);

void HC595_SendByte(u8 data);

u8 HC595_Display(u16 num, u8 dp);


#endif


源文件74HC595.c


// 用于HC595实现的4Bit-LED Digit Tube Module

// 注意:该4位数码管是共阳的!

#include "74HC595.h"


// 码表

const u8 digitTable[] = 

{

// 0       1       2       3       4       5       6       7       8       9

    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90,

// A        b       C     d     E     F    -

    0x8C, 0xBF, 0xC6, 0xA1, 0x86, 0xFF, 0xbf

};


/*******************************************************************************

* Function Name  : HC595_Init

* Description    : 初始化HC595

* Input          : None

* Output         : None

* Return         : None

*******************************************************************************/

void HC595_Init(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;            //声明一个结构体变量

    RCC_APB2PeriphClockCmd(HC595_RCC | HC595_RCLK_RCC, ENABLE); //使能HC595的时钟    


    //74HC595, SCLK RCLK DIO 推挽输出

    GPIO_InitStructure.GPIO_Pin = HC595_SCLK_PIN| HC595_DIO_PIN;       

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //管脚频率为50MHZ

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //输出模式为推挽输出

    GPIO_Init(HC595_GPIO, &GPIO_InitStructure);              //初始化寄存器   


    GPIO_InitStructure.GPIO_Pin = HC595_RCLK_PIN;       

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //管脚频率为50MHZ

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //输出模式为推挽输出

    GPIO_Init(HC595_RCLK_GPIO, &GPIO_InitStructure);     //初始化寄存器   


}


/*******************************************************************************

* Function Name  : HC595_SendByte

* Description    : 发送一个字节

* Input          : data

* Output         : None

* Return         : None

*******************************************************************************/

void HC595_SendByte(u8 data)

{

    u8 i;

    for (i=8; i>=1; i--)

    {

        // 高位在前

        if (data&0x80) 

            GPIO_SetBits(HC595_GPIO, HC595_DIO_PIN); 

        else 

            GPIO_ResetBits(HC595_GPIO, HC595_DIO_PIN);

        data <<= 1;

        // SCLK上升沿

        GPIO_ResetBits(HC595_GPIO, HC595_SCLK_PIN);

        GPIO_SetBits(HC595_GPIO, HC595_SCLK_PIN);

    }

}


/*******************************************************************************

* Function Name  : HC595_Display

* Description    : 显示4位数字(包括小数点)

* Input          : num: 0000 - 9999 

*                  dp: 小数点的位置1-4 

* Output         : None

* Return         : 正常返回0,错误返回1

*******************************************************************************/

u8 HC595_Display(u16 num, u8 dp)

{

    u8 qian = 0, bai = 0, shi = 0, ge = 0;


    // 对显示的参数范围进行检查

    if (num > 9999 || dp > 4)

        //报错

        return 1;


    // 对num进行分解

    qian = num / 1000;

    bai = num % 1000 / 100;

    shi = num % 100 / 10;

    ge = num % 10;


    // 千位

    if(dp == 1)

        HC595_SendByte(digitTable[qian] & 0x7F);

    else

        HC595_SendByte(digitTable[qian]);

    HC595_SendByte(0x08);       

    GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);

    GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);


    // 百位

    if(dp == 2)

        HC595_SendByte(digitTable[bai] & 0x7F);

    else

        HC595_SendByte(digitTable[bai]);

    HC595_SendByte(0x04);       

    GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);

    GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);


    // 十位

    if(dp == 3)

        HC595_SendByte(digitTable[shi] & 0x7F);

    else

        HC595_SendByte(digitTable[shi]);

    HC595_SendByte(0x02);       

    GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);

    GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);


    // 个位

    if(dp == 4)

        HC595_SendByte(digitTable[ge] & 0x7F);

    else

        HC595_SendByte(digitTable[ge]);

    HC595_SendByte(0x01);       

    GPIO_ResetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);

    GPIO_SetBits(HC595_RCLK_GPIO, HC595_RCLK_PIN);


    return 0;

}


接下来就可以把重心都放在STM32的SPI外设上了,首先需要读一下STM32F10x的参考手册的SPI(串行外设接口)部分,这样对SPI就可以有较好的理解,比较重要的是要看懂SPI的结构框图和主从机通信的示意图,如下: 

这里写图片描述 
这里写图片描述 

这个理解以后,我们就可以参考《STM32F103XX固件库用户手册》的SPI部分来实现STM32的SPI外设配置和收发数据了,具体代码如下: 

头文件74HC595_SPI.h


#ifndef __74HC595_SPI_H__

#define __74HC595_SPI_H__


#include"stm32f10x_lib.h"       //包含所有的头文件

#include


// 4-Bit LED Digital Tube Module

// 引脚                                // SPI1       4位数码管            

#define  HC595_NSS_PIN   GPIO_Pin_4    // SPI1_NSS   未用

#define  HC595_SCK_PIN   GPIO_Pin_5    // SPI1_SCK   SCLK

#define  HC595_MISO_PIN  GPIO_Pin_6    // SPI1_MISO  未用 

#define  HC595_MOSI_PIN  GPIO_Pin_7    // SPI1_MOSI  DIO

#define  HC595_RCLK_PIN  GPIO_Pin_12   //            RCLK


// 端口

#define  HC595_SPI1_GPIO      GPIOA  

#define  HC595_RCLK_GPIO      GPIOB   

// 时钟

#define  HC595_SPI1_RCC  RCC_APB2Periph_GPIOA

#define  HC595_RCLK_RCC  RCC_APB2Periph_GPIOB 


void HC595_Init(void);

void HC595_SendByte(u8 data);

u8 HC595_Display(u16 num, u8 dp);


#endif


源文件74HC595_SPI.c


/************************省略部分代码见(74HC595.c)************************/

/*******************************************************************************

* Function Name  : HC595_Init

* Description    : 初始化HC595

* Input          : None

* Output         : None

* Return         : None

*******************************************************************************/

void HC595_Init(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;    

  SPI_InitTypeDef SPI_InitStructure;                                  // 声明一个结构体变量

    // 不需要开启AFIO时钟

    RCC_APB2PeriphClockCmd(HC595_SPI1_RCC | HC595_RCLK_RCC | RCC_APB2Periph_SPI1, ENABLE);  // 使能HC595及SPI1的时钟  


    //74HC595, SPI1_NSS、SPI1_SCK、SPI1_MOSI 

    GPIO_InitStructure.GPIO_Pin = HC595_NSS_PIN | HC595_SCK_PIN |HC595_MOSI_PIN;       

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       // 管脚频率为50MHZ

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         // 输出模式为复用推挽输出

    GPIO_Init(HC595_SPI1_GPIO, &GPIO_InitStructure);        // 初始化寄存器   


    //74HC595, SPI1_MISO

    GPIO_InitStructure.GPIO_Pin = HC595_MISO_PIN;       

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   // 输入模式为浮空输入

    GPIO_Init(HC595_SPI1_GPIO, &GPIO_InitStructure);        // 初始化寄存器   


    //74HC595, RCLK

    GPIO_InitStructure.GPIO_Pin = HC595_RCLK_PIN;       

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       // 管脚频率为50MHZ

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;        // 输出模式为复用推挽输出

    GPIO_Init(HC595_RCLK_GPIO, &GPIO_InitStructure);        // 初始化寄存器   


    /* Initialize the SPI1 according to the SPI_InitStructure members */

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

    // 第一步:设置主从模式和通信速率

    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

    // SPI_NSS_Hard时需要外部电路把NSS接VCC, SPI_NSS_Soft时SPI外设会将SSM和SSI置位

    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

    // 实测波特率最低为SPI_BaudRatePrescaler_8,否则出错

    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;


    // 第二步:设置数据格式

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

    // MSB在前还是LSB在前要根据码表和数码管与74HC595的接法来定

    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;


    // 第三步:设置时钟和极性

    // 当SPI_CPOL_Low且SPI_CPHA_2Edge出错

    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

    //第四步:其它,CRC校验,可靠通信,这步可以不设置

    SPI_InitStructure.SPI_CRCPolynomial = 7;

    SPI_Init(SPI1, &SPI_InitStructure);


    /* Enable SPI1 */

  SPI_Cmd(SPI1, ENABLE);

}


/*******************************************************************************

* Function Name  : HC595_SendByte

* Description    : 发送一个字节

* Input          : data

* Output         : None

* Return         : None

*******************************************************************************/

void HC595_SendByte(u8 data)

{

    SPI_I2S_SendData(SPI1, data);

    while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE));

}

/************************省略部分代码(见74HC595.c)************************/


这样就大工告成啦,STM32的SPI外设还是比较简单的,尤其是通过库函数来调用。用数码管模块这种简单的可视化工具,我们就可以更好的研究通信协议本身的特性啦,后续我还会用这种方式来学习其它的通信协议,好了,”talk is cheap, show me the code”!

关键字:STM32  SPI通信  Flash芯片 引用地址:STM32之SPI通信

上一篇:stm32 io模拟spi通信
下一篇:基于UART实现数据的自发自收

推荐阅读最新更新时间:2024-03-16 16:27

STM32学习笔记----keil工程的建立
1、keil编译的三个按键的功能 A、第一个按钮:Translate 就是翻译当下修改过的文件,说明白点就是检查下有没有语法错误,并不会去链接库文件,也不会生成可执行文件。 B、第二个按钮:Build 就是编译当下修改过的文件,它包含了语法检查,链 接动态库文件,生成可执行文件。 C、第三个按钮:Rebuild 重新编译整个工程,跟 Build 这个按钮实现的功能 是一样的,但有所不同的是它编译的是整个工程的所有文件,耗时巨大。 综上:当我们编辑好我们的程序之后,只需要用第二个 Build 按钮就可以, 即方便又省时。第一个跟第三个按钮用的比较少 2 、core_cm3.c 跟启动文件一样都是底层文件,都是由 ARM 公司提供的,
[单片机]
STM32常用数据类型分析
stdint.h /* exact-width signed integer types */ typedef signed char int8_t; //有符号8位 typedef signed short int int16_t; typedef signed int int32_t; typedef signed __INT64 int64_t; /* exact-width unsigned integer types */ typedef unsigned char uint8_t; //也就是常用的u8无符号8位 **char默认类型** typedef
[单片机]
stm32 低功耗下使用看门狗(不喂狗)
在项目中需要低功耗处理,而使用看门狗可以保证程序不会跑死。但使用看门狗有一个问题,打开看门狗以后关闭不了,必须一直喂狗进行操作,那有啥好的办法呢?狗是必须养的,养这条狗花费了我半天时间,在此记录。 我能想到的有两种方案: 一、 使用独立看门狗,在低功耗下唤醒进行喂狗操作后继续休眠。 该方法可以通过增大看门狗的时钟分频,最大256分频,即可达到最大限度的喂狗时间,如果按照40K的时钟来算,(1 / (40K / 256)) * 4096 = 26.2144S, 在二十多秒起来喂狗在功耗方面可以接受,这种方法比较简单,不过二十多秒需唤醒一次。 二、利用复位关闭看门狗,在低功耗下不进行喂狗操作 使用的前提是需要判断复
[单片机]
<font color='red'>stm32</font> 低功耗下使用看门狗(不喂狗)
STM32 UART DMA实现未知数据长度接收
串口通信是经常使用到的功能,在STM32中UART具有DMA功能,并且收发都可以使用DMA,使用DMA发送基本上大家不会遇到什么问题,因为发送的时候会告知DMA发送的数据长度,DMA按照发送的长度直接发送就OK了,但是使用DMA接收时候就不同了,因为有时候数据接收并不是每一次都是定长的,但是DMA只在接收数据长度和设定数据长度相同的时候才可以触发中断,告诉MCU数据接收完毕,针对这个问题,解决方法如下,有一点复杂,但是很管用。 UART在传输一个字节的时候,首先拉低,传输起始位,然后在是LSB MSB,最后是停止位,停止位是高电平 超时时间 搞过串口通信的都知道,如果串口有协议,一般都是有个超时时间的,超时时间是定义两个帧之间的间
[单片机]
STM32的中断优先级设定
在STM32的手册里没有提到关于设置中断优先级的情况,只列给出了中断向量表。根据该向量表可以创建一个中断编号的枚举数据,方便设置优先级及使能等的使用。 先说一下如何设置优先级。 优先级的设置在Cortex-CM3中:异常 - 优先级定义 中有描述;针对优先级的寄存器描述需要查看Cortex-M3编程手册。 可在此处下载手册:http://download.csdn.net/detail/gao_xyz/9780686 1、优先级组的设置 在权威指南 优先级定义中有描述,其优先级由:抢占优先级 和 子优先级组成。 此处需注意:抢占优先级,顾名思义,可以抢占CPU的,当有更高的抢占优先级时,可打断低抢占优先级的
[单片机]
<font color='red'>STM32</font>的中断优先级设定
STM32与S3C2440的区别
一、定位 STM32:    高功能单片机、工业控制 S3C2440:   处理器、智能设备 二、跑系统 STM32:    ucos-II S3C2440:   Linux等大型系统 三、硬件架构 STM32:    Cortex-M3、无MMU、Flash和RAM空间小 S3C2440:   ARM920T、有MMU、Flash和RAM外接空间大 四、软件结构 STM32:    程序运行空间和数据存放空间固定 S3C2440:   程序和数据都在SDRAM中 五、指令集 STM32:    ARMv6的大部分指令集和ARMv7的Thumb-2指令集、指令密度更大(适宜于Flash比较小的单片机) S3C2440:   A
[单片机]
stm32快速学习5——串口中断接收
串口自发自收 设定串口时钟 设定引脚功能 中断优先级 设定串口 Main文件 #include stm32f10x.h void RCC_Configuration(void); void GPIO_Configuration(void); void USART_Configuration(void); void NVIC_Configuration(void); int main(void) { RCC_Configuration(); GPIO_Configuration(); NVIC_Configuration(); USART_Configuration();
[单片机]
怎么使用C语言控制硬件
C语言的应用编程在单片机的领域占了很大一部分,使用的比较多的51单片机和STM32单片机都可以使用MDK软件编写固件。 单片机烧写了固件后可以点亮LED灯,可以驱动ADC检测电压,也可以驱动蜂鸣器发声,这就是简单地控制硬件。稍微复杂一点的,可以驱动NRF2401进行无线的连接,也可以使用ESP8266这类wifi芯片连接网络。 接下来,简单地讲讲如何使用C语言控制硬件。 1、电路连接 简单的模块可以直接使用高低电平来控制,比如红外线发射模块,当你在驱动引脚上的电压达到3.3v,就能发出红外线;然后将电平设置为0v,红外线发射就停止了。 一般而言,单片机的引脚输出电压能够达到3.3v,也是可以点亮红外线LED,但是可能会导致
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved