打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
stm32 SPI通信[操作寄存器+库函数]-Changing's Blog
    SPI(Serial Peripheral Interface--串行外设接口) 总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。 SPI是Freescale(原 Motorola)公司首先在其处理器上定义的。
    SPI是一种高速、主从式、全双工、同步传输的通信总线,SPI总线在物理层体现为四根传输线:
  • MOSI (Master Output Slaver Input) – 主器件数据输出,从器件数据输入
  • MISO (Master Input Slaver Output)  – 主器件数据输入,从器件数据输出
  • SCLK – 时钟信号,由主器件产生
  • NSS – 从器件使能信号,由主器件控制,有的IC会标注为CS(Chip select)
    CS线用于控制片选信号,当一个SPI从设备的CS线识别到了预先规定的片选电平,则该设备被选中。显然可以通过CS线,完成“一主多从”的SPI网络架设,在进行“一主一从”的SPI通信时,SPI并不是必须的。
    SPI总线传输数据时,由主机的SCLK线提供时钟脉冲,从机被动的接收时钟脉冲。主机在数据发送前,将数据写入数据寄存器,等待时钟脉冲移位输出,每个脉冲周期传输1位数据。 从机在主机的数据发送中,依靠主机的时钟,将从机的数据寄存器内容移位发送。所以要实现主从数据交换,在时钟信号前,主机 从机都必须先将数据写入数据寄存器,并且从机必须在主机前写入,然后由主机的SCLK时钟驱动发送。 不注意这个问题很容易造成SPI接收的数据错位。
    这样的全双工、同步传输完全依赖于 主机控制的时钟线SCLK,而且SCLK上只有数据传输的时候才有时钟信号。主机向从机发送数据不会有问题,但是如果从机主动向主机发送数据呢? 
    从机要发送数据,必须要有SCLK的时钟,所以只能主机发送一个DUMMY(哑巴)字节,产生时钟,来实现和从机的数据交换。 从设备只能被动发送数据,无法主动发送数据。
    本例实现 通过将STM32上的2个SPI接口对接,进行一个简单的数据交换。使用SPI1作为主设备,SPI2作为从设备,通过串口查看数据通信的情况。
实现结果如下:

直接操作寄存器
首先配置SPI主机的频率
SPI1设备属于高速设备,隶属APB2总线,最大时钟72Mhz;
SPI2属于低速设备,隶属APB1总线,最大36Mhz。
在控制寄存器中设置时钟分频值,设置时钟极性和相位等。程序中有注释,详见代码:
User/main.c  (system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置
001#include <stm32f10x_lib.h>   
002#include "system.h"
003#include "usart.h"
004#include "spi.h"
005 
006#define LED1 PAout(4)
007#define LED2 PAout(5)
008#define LED3 PAout(6)
009#define LED4 PAout(7)
010 
011 
012void Gpio_Init(void);
013 
014#define BufferSize 32
015 
016u8 SPI1_Buffer_Tx[BufferSize] =           
017{
018    0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
019    0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,
020    0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
021    0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20
022};
023 
024 
025u8 SPI2_Buffer_Tx[BufferSize] =           
026{
027    0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,
028    0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,
029    0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
030    0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70
031};
032 
033u8 SPI1_Buffer_Rx[BufferSize] = {0xFF};
034u8 SPI2_Buffer_Rx[BufferSize] = {0xFF};
035u8 Tx_Counter = 0;
036u8 Rx_Counter = 0;
037 
038 
039int main(void)
040{  
041    u8 k=0;
042 
043    Rcc_Init(9);                          //系统时钟设置
044 
045    Usart1_Init(72,9600);
046 
047    Nvic_Init(1,0,SPI1_IRQChannel,4);     //设置抢占优先级为1,响应优先级为0,中断分组为4
048    Nvic_Init(1,1,SPI2_IRQChannel,4);     //设置抢占优先级为1,响应优先级为1,中断分组为4
049 
050    Gpio_Init();
051 
052    Spi_Init(SPI1);
053    Spi_Init(SPI2);
054 
055 
056    while(Tx_Counter < BufferSize)
057    {  
058 
059        Spi_Write(SPI2,SPI2_Buffer_Tx[Tx_Counter]);         //必须先将从设备数据写入数据寄存器,等待时钟同步
060 
061        Spi_Write(SPI1,SPI1_Buffer_Tx[Tx_Counter]);         //主设备将数据写入数据寄存器,触发同步时钟,让主从数据寄存器由此时钟发送
062         
063        SPI2_Buffer_Rx[Rx_Counter] = Spi_Read(SPI2);
064 
065        SPI1_Buffer_Rx[Rx_Counter] = Spi_Read(SPI1);   
066 
067        Tx_Counter++;
068        Rx_Counter++;
069 
070    }  
071 
072    printf("\r\n The SPI1 has sended data below : \r\n");
073 
074    while(k<BufferSize)
075    {
076        printf(" %0.2d \r ",SPI1_Buffer_Tx[k]);
077        k++;
078    }
079 
080    printf("\r\n The SPI2 has received data below : \r\n");
081 
082    k=0;
083 
084    while(k<BufferSize)
085    {
086        printf(" %0.2d \r ",SPI2_Buffer_Rx[k]);
087        k++;
088    }
089 
090    k=0;
091 
092    printf("\r\n The SPI2 has sended data below : \r\n");
093                                       
094    while(k<BufferSize)
095    {
096        printf(" %0.2d \r ",SPI2_Buffer_Tx[k]);
097        k++;
098    }
099 
100    printf("\r\n The SPI1 has received data below : \r\n");
101 
102    k=0;
103 
104    while(k<BufferSize)
105    {
106        printf(" %0.2d \r ",SPI1_Buffer_Rx[k]);
107        k++;
108    }  
109 
110    while(1);      
111}
112 
113 
114void Gpio_Init(void)
115{
116    RCC->APB2ENR |= 1<<2;          //使能PORTA时钟    
117    RCC->APB2ENR |= 1<<3;          //使能PORTB时钟;   
118 
119 
120    //SPI1 I/O设置
121 
122    GPIOA->CRL &= 0x000FFFFF;        //PA 5,6,7 复用 
123    GPIOA->CRL |= 0xBBB00000;
124 
125    //SPI2 I/O设置
126 
127    GPIOB->CRH &= 0x000FFFFF;        //PB 13,14,15 复用 
128    GPIOB->CRH |= 0xBBB00000;
129 
130     
131    //USART1 串口I/O设置
132 
133    GPIOA -> CRH &= 0xFFFFF00F;      //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
134    GPIOA -> CRH |= 0x000008B0;   
135 
136}
User/stm32f10x_it.c
01#include "stm32f10x_it.h"
02#include "system.h"
03#include "stdio.h"
04 
05#define LED1 PAout(4)
06#define LED2 PAout(5)
07#define LED3 PAout(6)
08#define LED4 PAout(7)
09 
10 
11void SPI1_IRQHandler()
12{
13    if(SPI1->SR & 1<<7)        //SPI正忙于通信,或者发送缓冲非空
14    {
15        printf("SPI1 is Busy");
16    }
17 
18    if(SPI1->SR & 1<<6)        // 出现溢出错误
19    {
20        printf("SPI1 is Overrun");
21    }
22 
23    if(SPI1->SR & 1<<5)        //出现模式错误
24    {
25        printf("SPI1 is Mode fault");
26    }
27 
28    if(SPI1->SR & 1<<4)        //收到的CRC值和SPI_RXCRCR寄存器中的值不匹配
29    {
30        printf("SPI1 is CRC Error");
31    }                  
32 
33    printf("SPI1 Error");
34 
35}
36 
37void SPI2_IRQHandler()
38{
39    printf("SPI2 Error");
40 
41}
Library/src/spi.c
01#include "spi.h"
02 
03//SPI初始化函数
04//SPI1主机模式,SPI2从机模式,8bit数据格式,时钟空闲保持为低,数据采样从第二个时钟边沿开始,波特率 fPCLK/32
05//先发送LSB(最低有效位),禁止硬件CRC校验
06void Spi_Init(SPI_TypeDef * SPIx)
07{
08    if(SPIx == SPI1){
09 
10        RCC -> APB2ENR  |= 1<<12;     //SPI1时钟使能
11        RCC -> APB2RSTR |= 1<<12;    //复位SPI1寄存器
12        RCC -> APB2RSTR &= ~(1<<12);  //复位结束SPI1寄存器
13 
14        SPIx -> CR1 |= 1<<2;        //主设备选择   0:配置为从设备   1:配置为主设备
15        SPIx -> CR1 |= 1<<8;        //SSI位,要保持主机模式,必须NSS 接到高电平信号  
16 
17    }else if(SPIx == SPI2){
18        RCC -> APB1ENR  |= 1<<14;     //SPI2时钟使能
19        RCC -> APB1RSTR |= 1<<14;    //复位SPI2寄存器
20        RCC -> APB1RSTR &= ~(1<<14);  //复位结束SPI2寄存器
21        SPIx -> CR1 |= 0<<2;        //主设备选择   0:配置为从设备   1:配置为主设备
22        //SPIx -> CR1 |= 0<<8;      //SSI位,要保持主机模式,必须NSS 接到高电平信号
23    }
24 
25 
26    SPIx -> CR1 |= 0<<10;       //设置全双工模式  0:全双工(发送和接收)   1:禁止输出(只接收模式)   
27    SPIx -> CR1 |= 0<<11;       //数据帧格式  0:使用8位数据帧格式进行发送/接收   1:使用16位数据帧格式进行发送/接收
28    SPIx -> CR1 |= 1<<7;        //帧格式          0:先发送MSB  1:先发送LSB
29 
30    //配置NSS为GPIO输出口控制从设备片选
31    SPIx -> CR1 |= 1<<9;        //软件从设备管理 当此位(SSM)被置位时,NSS引脚上的电平由SSI位的值决定。
32         
33 
34    SPIx -> CR1 |= 0<<1;        //配置时钟极性 0: 空闲状态时,SCK保持低电平  1: 空闲状态时,SCK保持高电平
35    SPIx -> CR1 |= 1<<0;        //时钟相位     0: 数据采样从第一个时钟边沿开始  1: 数据采样从第二个时钟边沿开始
36 
37    SPIx -> CR1 |= 4<<3;        //波特率控制[5:3]  000: fPCLK/2   001: fPCLK/4   010: fPCLK/8  011: fPCLK/16  100: fPCLK/32
38                                 //                 101: fPCLK/64   110: fPCLK/128 111: fPCLK/256
39 
40    //SPIx -> CR2 |= 1<<7;      //发送缓冲区空中断使能
41    //SPIx -> CR2 |= 1<<6;      //接收缓冲区非空中断使能
42    SPIx -> CR2 |= 1<<5;        //错误中断使能
43 
44    SPIx -> CR1 |= 1<<6;        //SPI设备使能
45 
46}
47 
48void Spi_Write(SPI_TypeDef * SPIx,u8 data)
49{
50    //while((SPIx->SR&1<<1) == 0); //等待发送缓冲为空置位
51 
52    SPIx->DR = data;
53 
54    Spi_Delay(3);                   //必须稍作延时
55}
56 
57 
58u8 Spi_Read(SPI_TypeDef * SPIx)
59{
60 
61    //while((SPIx->SR&1<<0) == 1); //等待接收缓冲非空置位
62 
63    return SPIx->DR;
64 
65}
66 
67void Spi_Delay(u32 us)
68{
69    u32 time=100*us/7;    
70    while(--time);   
71}
Library/inc/spi.h
1#include <stm32f10x_lib.h>   
2 
3void Spi_Init(SPI_TypeDef * SPIx);
4void Spi_Write(SPI_TypeDef * SPIx,u8 data);
5u8   Spi_Read(SPI_TypeDef * SPIx);
6void Spi_Delay(u32 us);
操作库函数
main.c
001#include "stm32f10x.h"
002#include "stdio.h"
003#include "string.h"
004 
005#define  PRINTF_ON  1
006 
007void RCC_Configuration(void);
008void GPIO_Configuration(void);
009void USART_Configuration(void);
010void SPI_Configuration(void);
011 
012#define BufferSize 32
013 
014#define delay() for(i=0;i<200;i++)
015 
016SPI_InitTypeDef SPI_InitStructure;
017 
018u8 SPI1_Buffer_Tx[BufferSize] =            
019{
020    0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
021    0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,
022    0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
023    0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20
024};
025 
026u8 SPI2_Buffer_Tx[BufferSize] =            
027{
028    0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,
029    0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,
030    0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,
031    0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70
032};
033 
034u8 SPI1_Buffer_Rx[BufferSize+1] = {0};
035u8 SPI2_Buffer_Rx[BufferSize] = {0};
036u8 Tx_Counter = 0;
037u8 Rx_Counter = 0;
038u8 k=0,i=0;
039 
040 
041int main(void)
042{
043    RCC_Configuration();
044    GPIO_Configuration();
045    USART_Configuration();
046    SPI_Configuration();
047 
048    while(Tx_Counter < BufferSize)
049    {  
050        SPI_I2S_SendData(SPI2,SPI2_Buffer_Tx[Tx_Counter]);              //必须从机先发送数据
051        //while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET);    //如果spi2 还有发送缓存则等待发送完成
052 
053        SPI_I2S_SendData(SPI1,SPI1_Buffer_Tx[Tx_Counter]);
054         
055        while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET);  //没有接收缓存则等待
056        SPI2_Buffer_Rx[Rx_Counter] = SPI_I2S_ReceiveData(SPI2);
057 
058         
059        while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) ==RESET);
060         
061        SPI1_Buffer_Rx[Rx_Counter] = SPI_I2S_ReceiveData(SPI1);
062 
063        Tx_Counter++;
064        Rx_Counter++;
065    }  
066 
067    printf("\r\n The SPI1 has sended data below : \r\n");
068 
069    while(k<BufferSize)
070    {
071        printf(" %0.2d \r ",SPI1_Buffer_Tx[k]);
072        k++;
073    }
074 
075    printf("\r\n The SPI2 has received data below : \r\n");
076 
077    k=0;
078 
079    while(k<BufferSize)
080    {
081        printf(" %0.2d \r ",SPI2_Buffer_Rx[k]);
082        k++;
083    }
084 
085    k=0;
086 
087    printf("\r\n The SPI2 has sended data below : \r\n");
088 
089    while(k<BufferSize)
090    {
091        printf(" %0.2d \r ",SPI2_Buffer_Tx[k]);
092        k++;
093    }
094 
095    printf("\r\n The SPI1 has received data below : \r\n");
096 
097    k=0;
098 
099    while(k<BufferSize)
100    {
101        printf(" %0.2d \r ",SPI1_Buffer_Rx[k]);
102        k++;
103    }
104 
105    while(1);  
106}
107 
108void SPI_Configuration(void)
109{
110 
111    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
112    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
113    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
114    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
115    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
116    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
117    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
118    SPI_InitStructure.SPI_CRCPolynomial = 7;
119 
120    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
121    SPI_Init(SPI1,&SPI_InitStructure);
122     
123    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
124    SPI_Init(SPI2,&SPI_InitStructure);
125 
126    SPI_Cmd(SPI1,ENABLE);
127    SPI_Cmd(SPI2,ENABLE);
128 
129}
130 
131void GPIO_Configuration(void)
132{
133    GPIO_InitTypeDef    GPIO_InitStructure;
134 
135    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
136 
137    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
138    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        
139    GPIO_Init(GPIOA , &GPIO_InitStructure);
140 
141    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;     
142    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
143    GPIO_Init(GPIOB , &GPIO_InitStructure);
144 
145 
146    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
147    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        
148    GPIO_Init(GPIOA , &GPIO_InitStructure);
149     
150    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
151    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;          
152    GPIO_Init(GPIOA , &GPIO_InitStructure);
153}
154 
155void RCC_Configuration(void)
156{
157    /* 定义枚举类型变量 HSEStartUpStatus */
158    ErrorStatus HSEStartUpStatus;
159 
160    /* 复位系统时钟设置*/
161    RCC_DeInit();
162    /* 开启HSE*/
163    RCC_HSEConfig(RCC_HSE_ON);
164    /* 等待HSE起振并稳定*/
165    HSEStartUpStatus = RCC_WaitForHSEStartUp();
166    /* 判断HSE起是否振成功,是则进入if()内部 */
167    if(HSEStartUpStatus == SUCCESS)
168    {
169        /* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
170        RCC_HCLKConfig(RCC_SYSCLK_Div1);
171        /* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
172        RCC_PCLK2Config(RCC_HCLK_Div1);
173        /* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
174        RCC_PCLK1Config(RCC_HCLK_Div2);
175        /* 设置FLASH延时周期数为2 */
176        FLASH_SetLatency(FLASH_Latency_2);
177        /* 使能FLASH预取缓存 */
178        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
179        /* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
180        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
181        /* 使能PLL */
182        RCC_PLLCmd(ENABLE);
183        /* 等待PLL输出稳定 */
184        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
185        /* 选择SYSCLK时钟源为PLL */
186        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
187        /* 等待PLL成为SYSCLK时钟源 */
188        while(RCC_GetSYSCLKSource() != 0x08);
189    }
190    /* 打开APB2总线上的GPIOA时钟*/
191    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_USART1|RCC_APB2Periph_SPI1, ENABLE);
192 
193    //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
194 
195    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
196    //RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_WWDG, ENABLE);
197         
198}
199 
200  
201void USART_Configuration(void)
202{
203    USART_InitTypeDef USART_InitStructure;
204    USART_ClockInitTypeDef USART_ClockInitStructure;
205 
206    USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
207    USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
208    USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                     
209    USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
210    USART_ClockInit(USART1 , &USART_ClockInitStructure);
211 
212    USART_InitStructure.USART_BaudRate = 9600;
213    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
214    USART_InitStructure.USART_StopBits = USART_StopBits_1;
215    USART_InitStructure.USART_Parity = USART_Parity_No;
216    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
217    USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
218    USART_Init(USART1,&USART_InitStructure);
219 
220    USART_Cmd(USART1,ENABLE);
221}
222 
223#if  PRINTF_ON
224 
225int fputc(int ch,FILE *f)
226{
227    USART_SendData(USART1,(u8) ch);
228    while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
229    return ch;
230}
231 
232#endif
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
STM32F1系列之常用外设说明
九九的STM32笔记(四)DMA、USART的演示 (www.ourdev.cn)
stm32 外部中断嵌套[操作寄存器+库函数] -Changing's Blog
嵌入式单片机学习之串口接收数据实例
stm32不能进入串口收中断,求解答
day03
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服