STM32硬件SPI主从通信教程
STM32SPIDMA的使用
STM32SPIDMA的使⽤ ⼀是想总结⼀下SPI总线的特点与注意点,⼆是总结⼀下SPI DMA的使⽤⼀、SPI信号线说明 通常SPI通过4个引脚与外部器件相连:MISO:主设备输⼊/从设备输出引脚。
该引脚在从模式下发送数据,在主模式下接收数据。
MOSI:主设备输出/从设备输⼊引脚。
该引脚在主模式下发送数据,在从模式下接收数据。
SCK:串⼝时钟,作为主设备的输出,从设备的输⼊NSS:从设备选择。
这是⼀个可选的引脚,⽤来选择主/从设备。
它的功能是⽤来作为“⽚选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
⼆、原理 MOSI脚相互连接,MISO脚相互连接。
这样,数据在主和从之间串⾏地传输(MSB位在前)。
通信总是由主设备发起。
主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。
这意味全双⼯通信的数据输出和数据输⼊是⽤同⼀个时钟信号同步的;时钟信号由主设备通过SCK脚提供。
三、NSS说明与注意点 NSS分为内部引脚和外部引脚。
NSS外部引脚可以作为输⼊信号或者输出信号,输⼊信号⼀般⽤作硬件⽅式从机的⽚选,⽽输出信号⼀般⽤于主SPI去⽚选与之相连的从SPI。
NSS从设备选择有两种模式:1、软件模式 可以通过设置SPI_CR1寄存器的SSM位来使能这种模式,当它为1时,NSS引脚上的电平由SSI决定。
在这种模式下NSS外部引脚可以⽤作它⽤,⽽内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动。
2、硬件模式两种⽅式:(1)对于主SPI,NSS可以直接接⾼电平,对于从SPI,可以直接接低电平。
(2)当STM32F10xxx⼯作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时主机的NSS讲作为输出信号,引脚信号被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将⾃动变成从SPI设备。
此时两个的NSS信号线可以接个上拉电阻直连。
两块STM32之间的SPI主从通信
本以为与在同一片 STM32 上做 SPI 主、从机通信一样,以为挺简单的,但是实际做起来还是遇到了不少问题,比如出现数据移位、多出了一些数据等问题。
下面简单分享一下实现过程:一、整体框图及说明这里使用 STM32F429IGT6 作为主机,STM32F103ZET6 作为从机,都配置为全双工。
本例要实现的功能就是主、从机之间的数据互传。
主机往从机发送的数据为:从机往主机发送的数据为:二、关键代码主机关键代码:从机关键代码:可见,主机与从机的代码大多都一样。
只是从机多了一步启动传输的操作,这一步很关键,少了这一步传输就不正常。
这是为了制造主机发送的同时也要接收到数据的条件。
这一点参考手册里也有相关说明:此处,要营造这样的条件,必须先启动从机,然后再启动主机。
只有保证主机发送的同时有接收到数据,才能保证其时序的正常,否则可能会产生数据错位,或者会产生多余数据等情况。
三、调试我们平时在做实际的开发时,一般很难做到把所有代码写完,跑一遍就能成功,都是需要进行各个子模块的调试,一步一步来,确保各个子模块都没有问题之后,整体跑起来自然就比较稳定。
一些经验丰富的软件工程师常会教导一些年轻的软件工程师:在接到一个开发任务之前,先不要急着码代码,首先需要明确你这项任务的需求是什么,把任务分解成各个模块,然后在电脑上或纸上画出整体框图,确保框图的正确性之后,再根据框图来编写代码、调试。
同样的,我的主管也是这么教导我们的。
此处,我们要调试 SPI 主从通信,自然也是这样分模块进行调试的:•确认主机是否能正确发送数据•确认从机是否能正确发送数据(返回数据给从机)•确认从机是否接收到主机发过来的数据•确认主机是否接收到从机发过来的数据若这几个点明确了,都没问题之后。
就可以明确我们的 SPI 主、从机的基本通讯没有问题了,之后就可以进行我们的协议方面处理了(本例中没有这一部分)。
下面分别看一些这几个点:1、确认主机是否能正确发送数据方法:使用逻辑分析仪捕捉主机的 MOSI、SCK 这两条信号线,查看其波形。
STM32与FPGA进行SPI通信
STM32与FPGA进⾏SPI通信⼀、器件32单⽚机:STM32F407ZGFPGA :EP4CE6E22C8N⼆、通信⽅式STM32作为主机(软件);FPGA作为从机;SPI通信⽅式为1;三、STM32源代码1 #include "delay.h"2 #include "stm32f4xx.h"34 #ifndef __SPI_H5#define __SPI_H67#define SPI1_SCK PBout(2)8#define SPI1_MOSI PBout(3)9#define SPI1_MISO PBin(4)10#define CS PBout(5)1112//CPOL=0,CPHA=013 u8 SOFT_SPI_RW(u8 byte);14//SPI初始化15void SPIInit(void);1617#endifspi.h1 #include "spi.h"2 #include "delay.h"3 #include "stm32f4xx.h"45//CPOL=0,CPHA=06 u8 SOFT_SPI_RW(u8 byte)7 {8 u8 i;9 u8 Temp=0; //接收数据存储10 SPI1_SCK = 0;11 delay_init(168); //初始化延时函数12for(i=0;i<8;i++) // 循环8次13 {14if(byte&0x80) SPI1_MOSI = 1; //若字节最⾼位为1,则输出⾼15else SPI1_MOSI = 0; //若字节最⾼位为0,则输出低16byte <<= 1; //低⼀位移位到最⾼位17 delay_us(1); //延时1us18 SPI1_SCK = 1; //拉低时钟19 Temp <<= 1; //数据左移20if(SPI1_MISO) Temp++; //若从从机接收到⾼电平,数据⾃加⼀21 delay_us(1); //延时1us22 SPI1_SCK = 0; //拉低时钟23 }24return (Temp); //返回数据25 }2627void SPIInit(void)28 {29 GPIO_InitTypeDef GPIO_InitStructure;30 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能PORTB时钟3132//PB2,PB3,PB5设置33 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_5;34 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;35 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;36 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;37 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;38 GPIO_Init(GPIOB,&GPIO_InitStructure);39//PB4设置42 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;43 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;44 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;45 GPIO_Init(GPIOB,&GPIO_InitStructure);46 }spi.c四、FPGA源代码1//FPGA作为从设备2 module spi_s(I_clk,I_rst_n,I_data_in,O_data_out,O_tx_done,O_rx_done,I_spi_miso,I_spi_sck,I_spi_cs,O_spi_mosi,sck_posedge,sck_negedge,rxd_flag,txd_flag);3 input I_clk; //全局时钟50MHz4 input I_rst_n; //复位信号,低电平有效5//input I_rx_en; //读(接收)使能信号6//input I_tx_en; //写(发送)使能信号7 input [7:0]I_data_in; //要发送的数据8 output[7:0]O_data_out; //接收到的数据9 output O_tx_done; //发送⼀个数据完毕标志位10 output O_rx_done; //接收⼀个字节完毕标志位1112//四线标准SPI信号定义13 input I_spi_miso; //SPI串⾏输⼊,⽤来接收从机的数据14 input I_spi_sck; //SPI时钟15 input I_spi_cs; //SPI⽚选信号16 output O_spi_mosi; //SPI输出,⽤来给从机发送数据1718 output sck_posedge,sck_negedge;19 output rxd_flag;//接收标志位20 output txd_flag;//发送标志位2122 reg [7:0] O_data_out;23 reg O_tx_done;24 reg O_rx_done;25 reg O_spi_mosi;2627 reg [2:0] R_tx_state;28 reg [2:0] R_rx_state;2930 reg sck_r0,sck_r1;//当前SCK态,之前SCK态31 wire sck_posedge; //SCK上升沿32 wire sck_negedge; //SCK下降沿3334//获取SCK时钟当前态以及之前态35 always @(posedge I_clk,negedge I_rst_n)36 begin37if(!I_rst_n)38 begin39 sck_r0 <= 1'b0;40 sck_r1 <= 1'b0;//⼯作⽅式0,空闲时为低电平41 end42else43 begin44 sck_r0 <= I_spi_sck; //当前SCK态45 sck_r1 <= sck_r0; //之前SCK态46 end47 end4849//捕获SCK时钟上升沿以及下降沿50 assign sck_posedge = (~sck_r0 & sck_r1)? 1'b1:1'b0;//上升沿51 assign sck_negedge = (~sck_r1 & sck_r0)? 1'b1:1'b0;//下降沿5253//发送数据触发54 always @(posedge I_clk,negedge I_rst_n)55 begin56//置位57if(!I_rst_n)58 begin59 R_tx_state <= 3'b0;60 O_spi_mosi <= 1'b0;61 O_tx_done <= 0;62 end63//SCK上跳沿时发送数据(⽅式0)64else if(sck_posedge && !I_spi_cs)65 begin66case(R_tx_state)673'd0://发送第七位68 begin69 O_spi_mosi <= I_data_in[7];70 R_tx_state <= R_tx_state + 1'b1;71 O_tx_done <= 1'b0;75 O_spi_mosi <= I_data_in[6];76 R_tx_state <= R_tx_state + 1'b1;77 O_tx_done <= 1'b0;78 end793'd2://发送第五位80 begin81 O_spi_mosi <= I_data_in[5];82 R_tx_state <= R_tx_state + 1'b1;83 O_tx_done <= 1'b0;84 end853'd3://发送第四位86 begin87 O_spi_mosi <= I_data_in[4];88 R_tx_state <= R_tx_state + 1'b1;89 O_tx_done <= 1'b0;90 end913'd4://发送第三位92 begin93 O_spi_mosi <= I_data_in[3];94 R_tx_state <= R_tx_state + 1'b1;95 O_tx_done <= 1'b0;96 end973'd5://发送第⼆位98 begin99 O_spi_mosi <= I_data_in[2]; 100 R_tx_state <= R_tx_state + 1'b1; 101 O_tx_done <= 1'b0;102 end1033'd6://发送第⼀位104 begin105 O_spi_mosi <= I_data_in[1]; 106 R_tx_state <= R_tx_state + 1'b1; 107 O_tx_done <= 1'b0;108 end1093'd7://发送第零位110 begin111 O_spi_mosi <= I_data_in[0]; 112 R_tx_state <= R_tx_state + 1'b1; 113 O_tx_done <= 1'b1;//发送完毕114 end115default:R_tx_state <= 3'd0;116 endcase117 end118 end119120121122//接收数据触发123 always @(posedge I_clk,negedge I_rst_n) 124 begin125if(!I_rst_n)126 begin127 O_data_out <= 8'b0;128 R_rx_state <= 3'b0;129 O_rx_done <= 1'b0;130 end131else if(sck_negedge && !I_spi_cs)132case(R_rx_state)1333'd0://接收第七位134 begin135 R_rx_state <= R_rx_state + 1'b1; 136 O_rx_done <= 1'b0;137 O_data_out[7] <= I_spi_miso; 138 end1393'd1://接收第六位140 begin141 R_rx_state <= R_rx_state + 1'b1; 142 O_rx_done <= 1'b0;143 O_data_out[6] <= I_spi_miso; 144 end1453'd2://接收第五位146 begin147 R_rx_state <= R_rx_state + 1'b1; 148 O_rx_done <= 1'b0;149 O_data_out[5] <= I_spi_miso; 150 end1513'd3://接收第四位152 begin153 R_rx_state <= R_rx_state + 1'b1; 154 O_rx_done <= 1'b0;155 O_data_out[4] <= I_spi_miso;159 R_rx_state <= R_rx_state + 1'b1;160 O_rx_done <= 1'b0;161 O_data_out[3] <= I_spi_miso;162 end1633'd5://接收第⼆位164 begin165 R_rx_state <= R_rx_state + 1'b1;166 O_rx_done <= 1'b0;167 O_data_out[2] <= I_spi_miso;168 end1693'd6://接收第⼀位170 begin171 R_rx_state <= R_rx_state + 1'b1;172 O_rx_done <= 1'b0;173 O_data_out[1] <= I_spi_miso;174 end1753'd7://接收第零位176 begin177 R_rx_state <= R_rx_state + 1'b1;178 O_rx_done <= 1'b1;//接收完毕179 O_data_out[0] <= I_spi_miso;180 end181default:R_rx_state <= 3'd0;182 endcase183 end184185 reg rxd_flag_r0,rxd_flag_r1;//接收标志位(当前态),接收标志位(之前态)186 always@(posedge I_clk or negedge I_rst_n)187 begin188if(!I_rst_n)189 begin190 rxd_flag_r0 <= 1'b0;191 rxd_flag_r1 <= 1'b0;192 end193else194 begin195 rxd_flag_r0 <= O_rx_done;196 rxd_flag_r1 <= rxd_flag_r0;197 end198 end199//接收标志位200 assign rxd_flag = (~rxd_flag_r1 & rxd_flag_r0)? 1'b1:1'b0;201202 reg txd_flag_r0,txd_flag_r1;//发送标志位(当前态),发送标志位(之前态)203 always@(posedge I_clk or negedge I_rst_n)204 begin205if(!I_rst_n)206 begin207 txd_flag_r0 <= 1'b0;208 txd_flag_r1 <= 1'b0;209 end210else211 begin212 txd_flag_r0 <= O_tx_done;213 txd_flag_r1 <= txd_flag_r0;214 end215 end216//发送标志位217 assign txd_flag = (~txd_flag_r1 & txd_flag_r0)? 1'b1:1'b0;218219220 endmodulespi_s.v1 `timescale 1 ns/ 1 ps2 module spi_s_vlg_tst();3// constants4// general purpose registers5 reg eachvec;6// test vector input registers7 reg I_clk;8 reg [7:0] I_data_in;9 reg I_rst_n;10 reg I_spi_cs;11 reg I_spi_miso;12 reg I_spi_sck;13// wires14 wire [7:0] O_data_out;15 wire O_rx_done;16 wire O_spi_mosi;20 wire sck_posedge;21 wire txd_flag;2223// assign statements (if any)24 spi_s i1 (25// port map - connection between master ports and signals/registers26 .I_clk(I_clk),27 .I_data_in(I_data_in),28 .I_rst_n(I_rst_n),29 .I_spi_cs(I_spi_cs),30 .I_spi_miso(I_spi_miso),31 .I_spi_sck(I_spi_sck),32 .O_data_out(O_data_out),33 .O_rx_done(O_rx_done),34 .O_spi_mosi(O_spi_mosi),35 .O_tx_done(O_tx_done),36 .rxd_flag(rxd_flag),37 .sck_negedge(sck_negedge),38 .sck_posedge(sck_posedge),39 .txd_flag(txd_flag)40 );41 reg [2:0]state;42 initial43 begin44 I_clk = 0;45 I_rst_n = 0;46 I_data_in = 8'h00;47 I_spi_miso = 0;48 I_spi_cs = 1;49 I_spi_sck = 0;50 #10051 I_rst_n = 1;52 I_spi_cs = 0;53 end54 always55 begin56 #10 I_clk = ~I_clk;57 end5859 always60 begin61 #20 I_spi_sck = ~I_spi_sck;62 end6364 always @(posedge I_spi_sck,negedge I_rst_n)65 begin66if(!I_rst_n)67 I_data_in <= 8'h00;68else if(I_data_in == 8'hff)69 begin70 I_data_in <= 8'h00;71 end72else if(txd_flag)73 I_data_in <= I_data_in + 1'b1;74 end75//1110101076 always @(negedge I_spi_sck,negedge I_rst_n)77 begin78if(!I_rst_n)79 state <= 3'b000;80else81case(state)823'd0:83 begin84 state <= state + 1;85 I_spi_miso <= 1'b1;86 end873'd1:88 begin89 state <= state + 1;90 I_spi_miso <= 1'b1;91 end923'd2:93 begin94 state <= state + 1;95 I_spi_miso <= 1'b1;96 end973'd3:98 begin99 state <= state + 1;100 I_spi_miso <= 1'b0;103 begin104 state <= state + 1; 105 I_spi_miso <= 1'b1; 106 end1073'd5:108 begin109 state <= state + 1; 110 I_spi_miso <= 1'b0; 111 end1123'd6:113 begin114 state <= state + 1; 115 I_spi_miso <= 1'b1; 116 end1173'd7:118 begin119 state <= state + 1; 120 I_spi_miso <= 1'b0; 121 end122default:state <= 3'b000; 123 endcase124 end125 endmodulespi_s.vt(测试代码)五、仿真波形图六、参考资料。
SPI方式STM32F103与2.4G模块NRF24L01收发通讯
1.简介通过SPI方式与NRF24L01模块进行通讯,接收到的数据通过串口1打印出来,实时监测是否收到数据,发送的数据是“2.4G TEST”,当收不到数据时打印“no data”。
一块STM32F103ZET6开发板接收数据,另一块STM32F103RBT6开发板发送数据,两个淘宝买的2.4G NRF24L01模块。
还用到一块USB转TTL模块用来电平转换传送数据,串口调试助手接收串口发送数据。
2.代码部分---------nrf24l01.h-----------#ifndef __24L01_H#define __24L01_H#include "sys.h"/////////////////////////////////////////////////////////////////////////////// /////////////////////////////NRF24L01寄存器操作命令#define NRF_READ_REG 0x00 //读配置寄存器,低5位为寄存器地址#define NRF_WRITE_REG 0x20 //写配置寄存器,低5位为寄存器地址#define RD_RX_PLOAD 0x61 //读RX有效数据,1~32字节#define WR_TX_PLOAD 0xA0 //写TX有效数据,1~32字节#define FLUSH_TX 0xE1 //清除TX FIFO寄存器.发射模式下用#define FLUSH_RX 0xE2 //清除RX FIFO寄存器.接收模式下用#define REUSE_TX_PL 0xE3 //重新使用上一包数据,CE为高,数据包被不断发送.#define NOP 0xFF //空操作,可以用来读状态寄存器//SPI(NRF24L01)寄存器地址#define CONFIG 0x00 //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;//bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能#define EN_AA 0x01 //使能自动应答功能 bit0~5,对应通道0~5#define EN_RXADDR 0x02 //接收地址允许,bit0~5,对应通道0~5#define SETUP_AW 0x03 //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;#define SETUP_RETR 0x04 //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us#define RF_CH 0x05 //RF通道,bit6:0,工作通道频率;#define RF_SETUP 0x06 //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益#define STATUS 0x07 //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发//bit5:数据发送完成中断;bit6:接收数据中断;#define MAX_TX 0x10 //达到最大发送次数中断#define TX_OK 0x20 //TX发送完成中断#define RX_OK 0x40 //接收到数据中断#define OBSERVE_TX 0x08 //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器#define CD 0x09 //载波检测寄存器,bit0,载波检测;#define RX_ADDR_P0 0x0A //数据通道0接收地址,最大长度5个字节,低字节在前#define RX_ADDR_P1 0x0B //数据通道1接收地址,最大长度5个字节,低字节在前#define RX_ADDR_P2 0x0C //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;#define RX_ADDR_P3 0x0D //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;#define RX_ADDR_P4 0x0E //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;#define RX_ADDR_P5 0x0F //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;#define TX_ADDR 0x10 //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等#define RX_PW_P0 0x11 //接收数据通道0有效数据宽度(1~32字节),设置为0则非法#define RX_PW_P1 0x12 //接收数据通道1有效数据宽度(1~32字节),设置为0则非法#define RX_PW_P2 0x13 //接收数据通道2有效数据宽度(1~32字节),设置为0则非法#define RX_PW_P3 0x14 //接收数据通道3有效数据宽度(1~32字节),设置为0则非法#define RX_PW_P4 0x15 //接收数据通道4有效数据宽度(1~32字节),设置为0则非法#define RX_PW_P5 0x16 //接收数据通道5有效数据宽度(1~32字节),设置为0则非法#define NRF_FIFO_STATUS 0x17 //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RXFIFO满标志;bit2,3,保留//bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环;////////////////////////////////////////////////////////////////////////////////////////////////////////////24L01操作线#define NRF24L01_CE_High GPIO_SetBits(GPIOB,GPIO_Pin_4) //24l01片选#define NRF24L01_CE_Low GPIO_ResetBits(GPIOB,GPIO_Pin_4) //24L01片选信号#define NRF24L01_CSN_High GPIO_SetBits(GPIOB,GPIO_Pin_5) //SPI片选信号#define NRF24L01_CSN_Low GPIO_ResetBits(GPIOB,GPIO_Pin_5)#define NRF24L01_IRQ GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6) //IRQ主机数据输入//24L01发送接收数据宽度定义#define TX_ADR_WIDTH 5 //5字节的地址宽度#define RX_ADR_WIDTH 5 //5字节的地址宽度#define TX_PLOAD_WIDTH 32 //32字节的用户数据宽度#define RX_PLOAD_WIDTH 32 //32字节的用户数据宽度void NRF24L01_Init(void); //初始化void NRF24L01_RX_Mode(void); //配置为接收模式void NRF24L01_TX_Mode(void); //配置为发送模式u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 u8s);//写数据区u8 NRF24L01_Read_Buf(u8 reg, u8 *pBuf, u8 u8s); //读数据区u8 NRF24L01_Read_Reg(u8 reg); //读寄存器u8 NRF24L01_Write_Reg(u8 reg, u8 value); //写寄存器u8 NRF24L01_Check(void); //检查24L01是否存在u8 NRF24L01_TxPacket(u8 *txbuf); //发送一个包的数据u8 NRF24L01_RxPacket(u8 *rxbuf); //接收一个包的数据#endif-----------nrf24l01.c----------------#include "24l01.h"#include "delay.h"#include "usart.h"///////////////////////////////////////////////////////////////////////////////const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01};//初始化24L01的IO口void NRF24L01_Init(void){GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2时钟使能GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOBGPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI 单向或者双向的数据模式:SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPISPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为高电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器//使能SPI外设GPIO_InitStructure.GPIO_Pin =GPIO_Pin_4|GPIO_Pin_5; //PB12上拉防止W25X的干扰GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PG6 输入GPIO_Init(GPIOB, &GPIO_InitStructure);//使能SPI外设SPI_Cmd(SPI2, ENABLE);NRF24L01_CE_Low; //使能24L01NRF24L01_CSN_High; //SPI片选取消}//SPIx 读写一个字节//TxData:要写入的字节//返回值:读取到的字节u8 SPI2_ReadWriteByte(u8 TxData){while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); //检查指定的SPI标志位设置与否:发送缓存空标志位SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据delay_us(2);while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);//检查指定的SPI标志位设置与否:接受缓存非空标志位return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据delay_us(2);}//检测24L01是否存在//返回值:0,成功;1,失败u8 NRF24L01_Check(void){u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};u8 i;//SPI2_SetSpeed(SPI_BaudRatePrescaler_4); //spi速度为9Mhz(24L01的最大SPI 时钟为10Mhz)NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.NRF24L01_Read_Buf(TX_ADDR,buf,5); //读出写入的地址for(i=0;i<5;i++)if(buf[i]!=0XA5)break;if(i!=5)return 1;//检测24L01错误return 0; //检测到24L01}//SPI写寄存器//reg:指定寄存器地址//value:写入的值u8 NRF24L01_Write_Reg(u8 reg,u8 value){u8 status;NRF24L01_CSN_Low; //使能SPI传输status =SPI2_ReadWriteByte(reg);//发送寄存器号SPI2_ReadWriteByte(value); //写入寄存器的值NRF24L01_CSN_High; //禁止SPI传输return(status); //返回状态值}//读取SPI寄存器值//reg:要读的寄存器u8 NRF24L01_Read_Reg(u8 reg){u8 reg_val;NRF24L01_CSN_Low; //使能SPI传输SPI2_ReadWriteByte(reg); //发送寄存器号reg_val=SPI2_ReadWriteByte(0XFF);//读取寄存器内容NRF24L01_CSN_High; //禁止SPI传输return(reg_val); //返回状态值}//在指定位置读出指定长度的数据//reg:寄存器(位置)//*pBuf:数据指针//len:数据长度//返回值,此次读到的状态寄存器值u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len){u8 status,u8_ctr;NRF24L01_CSN_Low; //使能SPI传输status=SPI2_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值for(u8_ctr=0;u8_ctr<len;u8_ctr++)pBuf[u8_ctr]=SPI2_ReadWriteByte(0XFF);//读出数据NRF24L01_CSN_High; //关闭SPI传输return status; //返回读到的状态值}//在指定位置写指定长度的数据//reg:寄存器(位置)//*pBuf:数据指针//len:数据长度//返回值,此次读到的状态寄存器值u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len){u8 status,u8_ctr;NRF24L01_CSN_Low; //使能SPI传输status = SPI2_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值for(u8_ctr=0; u8_ctr<len; u8_ctr++)SPI2_ReadWriteByte(*pBuf++); //写入数据NRF24L01_CSN_High; //关闭SPI传输return status; //返回读到的状态值}//启动NRF24L01发送一次数据//txbuf:待发送数据首地址//返回值:发送完成状况u8 NRF24L01_TxPacket(u8 *txbuf){u8 sta;// SPI2_SetSpeed(SPI_BaudRatePrescaler_8);//spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)NRF24L01_CE_Low;NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 32个字节NRF24L01_CE_High;//启动发送//while(NRF24L01_IRQ!=0);//等待发送完成sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志if(sta&MAX_TX)//达到最大重发次数{NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器return MAX_TX;}if(sta&TX_OK)//发送完成{return TX_OK;}return 0xff;//其他原因发送失败}//启动NRF24L01发送一次数据//txbuf:待发送数据首地址//返回值:0,接收完成;其他,错误代码u8 NRF24L01_RxPacket(u8 *rxbuf){u8 sta;//SPI2_SetSpeed(SPI_BaudRatePrescaler_8); //spi速度为9Mhz(24L01的最大SPI 时钟为10Mhz)sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志if(sta&RX_OK)//接收到数据{NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器return 0;}return 1;//没收到任何数据}//该函数初始化NRF24L01到RX模式//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR//当CE变高后,即进入RX模式,并可以接收数据了void NRF24L01_RX_Mode(void){NRF24L01_CE_Low;NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);/ /写RX节点地址NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置RF通信频率NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式NRF24L01_CE_High; //CE为高,进入接收模式}//该函数初始化NRF24L01到TX模式//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和LNA HCURR//PWR_UP,CRC使能//当CE变高后,即进入RX模式,并可以接收数据了//CE为高大于10us,则启动发送.void NRF24L01_TX_Mode(void){NRF24L01_CE_Low;NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACKNRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置RF通道为40NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断NRF24L01_CE_High;//CE为高,10us后启动发送}-------usart.h----------#ifndef __USART_H#define __USART_H#include "stdio.h"#include "sys.h"/////////////////////////////////////////////////////////////////////////////// void uart_init(u32 bound);void usart1_send_string(u8 *BuffToSend);#endif---------usart.c---------------#include "sys.h"#include "usart.h"/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void uart_init(u32 bound){//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);//使能USART1,GPIOA时钟//USART1_TX GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10//Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器//USART 初始化设置USART_ART_BaudRate = bound;//串口波特率USART_ART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_ART_StopBits = USART_StopBits_1;//一个停止位USART_ART_Parity = USART_Parity_No;//无奇偶校验位USART_ART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_ART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断USART_Cmd(USART1, ENABLE); //使能串口1}//打印字符串void usart1_send_string(u8 *BuffToSend){u8 i=0;while(BuffToSend[i]!='\0'){USART_SendData(USART1, BuffToSend[i]);while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);i++;}}收发主函数是分开写的,因为用的不同的芯片。
SPI1SPI2_DMA通信实验(STM32)
SPI1SPI2_DMA通信实验(STM32)STM32学习笔记(⼆)——之SPI_DMA寄存器级操作⼀、实验⽬标学会配置STM32的SPI寄存器和DMA寄存器,实现STM32的SPI1与SPI2通信功能,每次发送⼀字节数据,并可多次发送,如果接收的数据正确,则点亮LED灯。
⼆、实验⽬的加⼊DMA的SPI通信相对于普通SPI通信有什么好处?ST给SPI加了DMA功能出于什么⽬的?我觉得这是很重要的⼀个问题,⼀直边学习边想。
以下是我的看法:减少CPU负荷?我想这应该是DMA最主要的功能,可是对于SPI通信来说,其实⼤部分时候我们需要根据发送的指令->⽬标器件的应答来决定下⼀个指令,所以此时CPU还是需要⼀直等待每次通信的结束。
⽽且像SD卡的操作,是⼀个顺序流的指令操作过程,⽤中断也不容易控制。
那到底加⼊了DMA有什么好处?仔细查看了STM32F10xxx的⽤户⼿册,发现这么⼀⾏字“连续和⾮连续传输:当在主模式下发送数据时,如果软件⾜够快,能够在检测到每次TXE的上升沿(或TXE中断),并⽴即在正在进⾏的传输结束之前写⼊SPI_DR寄存器,则能够实现连续的通信;此时,在每个数据项的传输之间的SPI时钟保持连续,同时BSY位不会被清除。
如果软件不够快,则会导致不连续的通信;这时,在每个数据传输之间会被清除”以及也就是说如果连续传输⽽不使⽤DMA的话,需要CPU不停检测TXE并很快地置⼊SPI->DR的值,对于复杂程序的话这是很难达到的,⽽如果使⽤DMA,就可以轻易实现连续传输,CPU只需等待其完成就好。
我想到的⼀个应⽤就是在写SD卡的时候,每次写⼀个块512字节,就可以⽤到,能提⾼SD卡的写⼊数据速率。
其次还可以降低功耗,记得数字集成电路⽼师说过⼀句话“软件上降低数字电路功耗的⼀个⽅法就是减少电平转换。
”那么连续通信的时候,像SPI的BSY电平转换会⼤⼤减少!最后⼀点,虽然效果不⼤,就是如果不是⽤DMA,那么CPU的⼯作就是搬运⼯,把SPI->DR 的内容搬到内存存储起来,⽽如果使⽤DMA,就省略了这个环节!我想,为什么实现同⼀个功能,有的执⾏起来很流畅,有的却很卡,应该和这些⼩细节的减载有关吧。
STM32-SPI
4月25日---- 4月27日SPI总线经过两天的学习,哈哈SPI终于搞定,用STM32控制IIC EEPORM,将EEPROM中的数据读到SPI的FLASH中。
---------------------------------------------------------------------------------------------------------------------- SPI(Serial Peripheral Interface)总线:串行外围总线接口一、SPI总线的简介:SPI是由摩托罗拉公司开发的全双工同步串行总线,没有总线仲裁机制,所以工作在单主机系统中。
主设备启动与从设备的同步通讯,从而完成数据的交换。
对于只能做从机的器件接口来说,SPI接口由SDI(串行数据输入),SDO(串行数据输出),SCK(串行移位时钟)和CS(从使能信号)。
通讯时,数据由SDO输出,SDI输入,数据在时钟的上升沿或者下降沿由SDO输出,在紧接着的下降沿或者上升沿由SDI读入,这样经过8/16帧,以完成8/16位数据的传输。
对于有的微控制器来说,它的硬件SPI即可做主机,也可以做从机,即可实现主从配置,MOSI:主出从入、MISO:主入从出、SCK:串行时钟、SS:从属选择。
(当做主机输出时,该信号用于选中需要访问的从机,SS输入高电平,外部决定其为主机;当做从机时,SS为输入或者一般的IO口使用)。
常用的SPI接法:在软件配置的条件下SS不使用。
1、SPI总线的通信时序(1)在SPI通信中,在全双工模式下,发送和接收是同事进行的(2)数据传输的时钟基来自主控制器的时钟脉冲;摩托罗拉没有定义任何通用的SPI 时钟的规范,最常用的时钟设置是基于时钟极性CPOL和时钟相位CPHA两个参数。
a)CPOL=0,表示时钟的空闲状态为低电平b)CPOL=1,表示时钟的空闲状态为高电平c)CPHA=0,表示同步始终的第一个边沿(上升或者下降)数据被采样d)CPHA=1,表示同步始终的第二个边沿(上升或者下降)数据被采样即CPOL和CPHA的设置决定了数据采样的时钟沿。
STM32
STM32 SPI通信--OLED⼀、0.96⼨OLED⼆、原理图⼆、GPIO模拟SPI1. 硬件连接通过引脚和模块电路图可以分析出SPI的电路连接OLED STM32GND <----------> GNDVCC <----------> 3.3VD0 <----------> PA4(CLK)D1 <----------> PA3(MOSI)RES <----------> PA2(RET复位)DC <----------> PA1(命令|数据dc)CS <----------> GND2. 软件驱动由OLED模块数据⼿册上SPI的操作时序图写出由GPIO⼝模拟的SPI驱动代码,模块只⽀持向模块写数据不能读数据,所以只需要写SPI发送即可2.1 “SPI.h”#define OLED_CMD 0 //命令声明#define OLED_DATA 1 //数据声明#define OLED_CLK PAout(4) // CLK时钟 d0#define OLED_MOSI PAout(3) // MOSI d1#define OLED_RST PAout(2) // RET复位 ret#define OLED_DC PAout(1) // 命令|数据 dc (0表传输命令1表传输数据)void OLED_SPI_Init(void); //配置MCU的SPIvoid SPI_WriteByte(uint8_t addr,uint8_t data); //向寄存器地址写⼀个byte的数据void WriteCmd(unsigned char cmd); //写命令void WriteDat(unsigned char data); //写数据2.2 “SPI.c”void OLED_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//使能PA端⼝时钟GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //端⼝配置GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO⼝速度为50MHzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOA}void SPI_WriteByte(unsigned char data,unsigned char cmd){unsigned char i=0;OLED_DC =cmd;OLED_CLK=0;for(i=0;i<8;i++){OLED_CLK=0;if(data&0x80)OLED_MOSI=1; //从⾼位到低位else OLED_MOSI=0;OLED_CLK=1;data<<=1;}OLED_CLK=1;OLED_DC=1;}void WriteCmd(unsigned char cmd){SPI_WriteByte(cmd,OLED_CMD);}void WriteData(unsigned char data){SPI_WriteByte(data,OLED_DATA);}三、STM32对OLED模块的控制3.1 指令集3.2 “OLED.h”void OLED_Init(void);//初始化OLEDvoid OLED_ON(void);//唤醒OLEDvoid OLED_OFF(void);//OLED休眠void OLED_Refresh_Gram(void);//更新显存到OLEDvoid OLED_Clear(void);//清屏void OLED_DrawPoint(u8 x,u8 y,u8 t);//画点void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);//填充void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);//显⽰字符void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);//显⽰2个数字void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size);//显⽰字符串3.3 “OLED.c”//OLED的显存//存放格式如下.//[0]0 1 2 3 (127)//[1]0 1 2 3 (127)//[2]0 1 2 3 (127)//[3]0 1 2 3 (127)//[4]0 1 2 3 (127)//[5]0 1 2 3 (127)//[6]0 1 2 3 (127)//[7]0 1 2 3 (127)u8 OLED_GRAM[128][8];void OLED_DLY_ms(unsigned int ms){unsigned int a;while(ms){a=1335;while(a--);ms--;}}void OLED_Init(void){OLED_SPI_Init();OLED_CLK = 1;OLED_RST = 0;OLED_DLY_ms(100);OLED_RST = 1;//从上电到下⾯开始初始化要有⾜够的时间,即等待RC复位完毕WriteCmd(0xAE); // Display Off (0x00)WriteCmd(0xD5);WriteCmd(0x80); // Set Clock as 100 Frames/SecWriteCmd(0xA8);WriteCmd(0x3F); // 1/64 Duty (0x0F~0x3F)WriteCmd(0xD3);WriteCmd(0x00); // Shift Mapping RAM Counter (0x00~0x3F)WriteCmd(0x40 | 0x00); // Set Mapping RAM Display Start Line (0x00~0x3F) WriteCmd(0x8D);WriteCmd(0x10 | 0x04); // Enable Embedded DC/DC Converter (0x00/0x04) WriteCmd(0x20);WriteCmd(0x02); // Set Page Addressing Mode (0x00/0x01/0x02)WriteCmd(0xA0 | 0x01); // Set SEG/Column MappingWriteCmd(0xC0); // Set COM/x Scan DirectionWriteCmd(0xDA);WriteCmd(0x02 | 0x10); // Set Sequential Configuration (0x00/0x10)WriteCmd(0x81);WriteCmd(0xCF); // Set SEG Output CurrentWriteCmd(0xD9);WriteCmd(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock WriteCmd(0xDB);WriteCmd(0x40); // Set VCOM Deselect LevelWriteCmd(0xA4 | 0x00); // Disable Entire Display On (0x00/0x01)WriteCmd(0xA6 | 0x00); // Disable Inverse Display On (0x00/0x01)WriteCmd(0xAE | 0x01); // Display On (0x01)OLED_Clear(); //初始清屏}void OLED_ON(void){WriteCmd(0X8D); //设置电荷泵WriteCmd(0X14); //开启电荷泵WriteCmd(0XAF); //OLED唤醒}void OLED_OFF(void){WriteCmd(0X8D); //设置电荷泵WriteCmd(0X10); //关闭电荷泵WriteCmd(0XAE); //OLED休眠}void OLED_Refresh_Gram(void){u8 i,n;for(i=0;i<8;i++){WriteCmd(0xb0+i); //设置页地址(0~7)WriteCmd(0x00); //设置显⽰位置—列低地址WriteCmd(0x10); //设置显⽰位置—列⾼地址for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]);}}void OLED_Clear(void){u8 i,n;for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;OLED_Refresh_Gram();//更新显⽰}void OLED_DrawPoint(u8 x,u8 y,u8 t){u8 pos,bx,temp=0;if(x>127||y>63)return;//超出范围了.pos=7-y/8;bx=y%8;temp=1<<(7-bx);if(t)OLED_GRAM[x][pos]|=temp;else OLED_GRAM[x][pos]&=~temp;}void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot){u8 x,y;for(x=x1;x<=x2;x++){for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);}OLED_Refresh_Gram();//更新显⽰}void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode){u8 temp,t,t1;u8 y0=y;u8 csize=(size/8+((size%8)?1:0))*(size/2); //得到字体⼀个字符对应点阵集所占的字节数 chr=chr-' ';//得到偏移后的值for(t=0;t{if(size==12)temp=asc2_1206[chr][t]; //调⽤1206字体else if(size==16)temp=asc2_1608[chr][t]; //调⽤1608字体else if(size==24)temp=asc2_2412[chr][t]; //调⽤2412字体else return; //没有的字库for(t1=0;t1<8;t1++){if(temp&0x80)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp<<=1;y++;if((y-y0)==size){y=y0;x++;break;}}}}//m^n函数u32 mypow(u8 m,u8 n){u32 result=1;while(n--)result*=m;return result;}void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size){u8 t,temp;u8 enshow=0;for(t=0;t{temp=(num/mypow(10,len-t-1));if(enshow==0&&t<(len-1)){if(temp==0){OLED_ShowChar(x+(size/2)*t,y,' ',size,1);continue;}else enshow=1;}OLED_ShowChar(x+(size/2)*t,y,temp+'0',size,1);}}void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size){while((*p<='~')&&(*p>=' '))//判断是不是⾮法字符!{if(x>(128-(size/2))){x=0;y+=size;}if(y>(64-size)){y=x=0;OLED_Clear();}OLED_ShowChar(x,y,*p,size,1);x+=size/2;p++;}四、⽤STM32的SPI资源也可以不使⽤GPIO⼝模拟,使⽤STM32的SPI资源,驱动代码如下:void OLED_SPI_Init(void){GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE ); //①GPIO,SPI 时钟使能GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复⽤推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7); //①初始化 GPIOGPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;//端⼝配置GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO⼝速度为50MHzGPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOASPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置 SPI 全双⼯ SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置 SPI ⼯作模式:设置为主 SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8 位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//选择了串⾏时钟的稳态:时钟悬空⾼ SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第⼆个时钟沿SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由硬件管理SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //预分频 256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式SPI_Init(SPI1, &SPI_InitStructure); //②根据指定的参数初始化外设 SPIx 寄存器SPI_Cmd(SPI1, ENABLE); //③使能 SPI 外设//SPI1_ReadWriteByte(0xff);//④启动传输}void OLED_WB(uint8_t data){while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);SPI_I2S_SendData(SPI1, data);}void SPI_WriteByte(unsigned char data,unsigned char cmd) {unsigned char i=0;OLED_DC =cmd;OLED_WB(data);}。
STM32 SPI初始化和使用
串行外设接口(SPI)。
初始化步骤:1、连接SPI外设时钟,通过RCC->APB2ENR设置。
2、连接被复用的GPIO的外设时钟,也是通过RCC->APB2ENR设置为什么还要连接GPIO时钟,参见STM32参考手册8.1.4节。
手册上这么说的:对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。
3、设置被复用的GPIO为推挽输出,并设置时钟。
不能设置为开漏输出。
设置成开漏输出时,示波器上看输出是锯齿波,而不是需要的方波。
4、通过配置SPIx->CR1来设置SPI 的工作模式。
最后使能SPI5、收发数据。
收发数据可以使用同一个函数,因为SPI是同步输入输出的,在发送数据的时候已经在接受数据。
配置SPI1代码如下:void SPI1_Init(void){RCC->APB2ENR |= 1<<12;//使能SPI1 时钟RCC->APB2ENR |= 1<<2;//配置服用功能输出GPIOA->CRL&=0X000FFFFF;GPIOA->CRL|=0XBBB00000;//PA5.6.7 复用,推挽输出50M时钟(不能配置成开漏,否则输出为锯齿波)GPIOA->ODR|=0X7<<5;SPI1->CR1|=0<<11;//8bit数据格式SPI1->CR1|=0<<10;//全双工模式SPI1->CR1|=1<<9; //软件nss 管理SPI1->CR1|=1<<8;SPI1->CR1|=0<<7; //MSBfirstSPI1->CR1|=7<<3; //设置时钟Fsck=Fcpu/256SPI1->CR1|=1<<2; //SPI 主机SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始SPI1->CR1|=1<<6; //使能SPI}现在可以读写数据了:u8 SPI1_ReadWriteByte(u8 data){//while((SPI1->SR && 1<<7) == 0); //等待SPI1空闲while((SPI1->SR && 1<<1)==0); //等待发送缓冲区空SPI1->DR = data;while((SPI1->SR && 1<<0)==0);return SPI1->DR;}。
STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从, CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
STM32 SPI通信原理及编程步骤
STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从,CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
STM32硬件SPI主从通信教程
STM32硬件SPI主从通信教程一、硬件SPI主从通信原理硬件SPI(Serial Peripheral Interface)是一种支持全双工通信的串行通信协议,常用于连接微控制器与外设。
在SPI通信中,主设备控制整个通信过程,发送和接收数据,从设备则根据主设备的控制进行响应。
1.SCK(时钟信号):用于同步主从设备的数据传输,主设备产生时钟信号控制通信速率。
2.MOSI(主设备发送数据线):主设备向从设备发送数据的线路。
3.MISO(从设备发送数据线):从设备向主设备发送数据的线路。
4.NSS(片选信号):主设备用于选择从设备进行通信的信号。
5.数据寄存器:用于存储传输的数据。
二、硬件SPI主从通信的配置步骤以下是STM32硬件SPI主从通信的配置步骤:1.配置SPI模式和通信速率:选择主从通信模式和时钟速率。
2.配置GPIO引脚:将SPI的SCK、MOSI、MISO和NSS引脚配置为SPI功能。
3.配置SPI控制寄存器:配置SPI的参数,如主从模式、数据大小和时钟相位等。
4.使能SPI和NSS信号:打开SPI和NSS信号,准备开始通信。
5.通过SPI数据寄存器进行数据交换:主设备通过SPI数据寄存器向从设备发送数据,并接收从设备返回的数据。
三、示例代码下面是一个简单的示例代码,展示了如何在STM32中配置和使用硬件SPI主从通信。
```c#include "stm32f4xx.h"void SPI_Init(void)RCC_AHB1PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 , GPIO_Pin_6 ,GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);void SPI_SendData(uint8_t data)while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);SPI_SendData8(SPI1, data);while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);SPI_ReceiveData8(SPI1);int main(void)SPI_Init(;while (1)SPI_SendData(0xAA); // 发送数据}```以上示例代码展示了如何将STM32配置为SPI的从设备,并向主设备发送数据。
STM32之IO口模拟SPI
STM32之IO⼝模拟SPI本⽂介绍如何使⽤STM32标准外设库的GPIO端⼝模拟SPI,本例程使⽤PA5、PA6和PA7模拟⼀路SPI。
SPI有4种⼯作模式,模拟SPI使⽤模式0,即空闲时SCK为低电平,在奇数边沿采样。
本⽂适合对单⽚机及C语⾔有⼀定基础的开发⼈员阅读,MCU使⽤STM32F103VE系列。
1. 简介SPI 协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串⾏外围设备接⼝,是⼀种⾼速全双⼯的通信总线。
它被⼴泛地使⽤在要求通讯速率较⾼的场合。
SPI⽤于多设备之间通讯,分为主机Master和从机Slave,主机只有⼀个,从机可以有多个,通过⽚选信号对从机进⾏选择,⼀次只能选择⼀个从机。
通讯只能由主机发起,⽀持的操作分为读取和写⼊,即主机读取从机的数据,以及向从机写⼊数据。
SPI⼀般有4根线,分别是⽚选线SS、时钟线SCK、主设备输出\从设备输⼊MOSI、主设备输⼊\从设备输出MISO,其中除MISO对于主机为输⼊引脚外,其他引脚对于主机均为输出引脚。
因为有独⽴的输⼊和输出引脚,因此SPI⽀持全双⼯⼯作模式,即可以同时接收和发送。
2. 总线传输信号空闲状态:⽚选信号SS低电平有效,那么空闲状态⽚选信号SS为⾼。
开始信号及结束信号:开始信号需要将⽚选信号SS拉低,结束信号需要将⽚选信号SS拉⾼。
通讯模式:SPI有4种通讯模式,分别为0、1、2、3,根据时钟极性和时钟相位确定,时钟极性分别为空闲低电平和空闲⾼电平,时钟相位分别为SCK奇数边沿采样和偶数边沿采样。
常⽤的模式为模式0和模式3。
SPI模式时钟极性(空闲时SCK时钟)时钟相位(采样时刻)0低电平奇数边沿1低电平偶数边沿2⾼电平奇数边沿3⾼电平偶数边沿3. 时序说明以模式0举例说明:空闲状态:⽚选信号SS为⾼,SCK输出低电平。
开始信号:⽚选信号SS变低,SCK输出低电平。
结束信号:⽚选信号SS变⾼,SCK输出低电平。
STM32实现SPI简单通信配置
STM32实现SPI简单通信配置/*******************************************************说明:次程序成功实现SPI简单通信配置,能实现SPI1发送数据,SPI2接收数据简单通信功能,仅供测试使用。
*******************************************************/#include "stm32f10x_lib.h"#include "stm32f10x_conf.h"#include "ili9320.h"/****************************/#define LCD_X_ADDR 190#define LCD_Y_ADDR 100vu16 SPI_IDx = 0; //Reveive Datau16 SPI_ID_Temp = 0; //接收数据备份,用于判断接收到的数据是否发生变化u16 SPI_ID[4]; //位显示数据/********** 函数声明 *********/void RCC_Configuration(void);void NVIC_Configuration(void);void GPIO_Configuration(void);void SPI_Configuration(void);void Sys_Init(void);void LCD_Print_Out(void);/********** 系统初始化 ******************/void Sys_Init(void){#ifdef DEBUGdebug();#endif//----- 模块初始化配置 -------RCC_Configuration();NVIC_Configuration();GPIO_Configuration();SPI_Configuration();//--------- LCD -----------Lcd_Configuration();ili9320_Initializtion();ili9320_Clear(0);ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + 0, 65535, 0);}/******* 主函数 **********/int main(void){Sys_Init();while(1){LCD_Print_Out();}}/******** 用于调试LCD显示数据界面 *********/void LCD_Print_Out(void){//如果数据发生变化,则执行数据更新if(SPI_ID_Temp != SPI_IDx){if(SPI_IDx >= 1000){SPI_ID[1] = (SPI_IDx % 100) / 10;SPI_ID[2] = (SPI_IDx / 100) % 10;SPI_ID[3] = SPI_IDx / 1000;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 65535, 0);}else if(SPI_IDx >= 100){SPI_ID[0] = SPI_IDx % 10;SPI_ID[1] = (SPI_IDx / 10) % 10;SPI_ID[2] = SPI_IDx / 100;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 0, 0);}else if(SPI_IDx >= 10){SPI_ID[0] = SPI_IDx % 10;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 0, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 0, 0);}else{SPI_ID[0] = SPI_IDx % 10;ili9320_PutChar(LCD_X_ADDR, LCD_Y_ADDR, 0x30 + SPI_ID[0], 65535, 0);ili9320_PutChar(LCD_X_ADDR - 8, LCD_Y_ADDR, 0x30 + SPI_ID[1], 0, 0);ili9320_PutChar(LCD_X_ADDR - 16, LCD_Y_ADDR, 0x30 + SPI_ID[2], 0, 0);ili9320_PutChar(LCD_X_ADDR - 24, LCD_Y_ADDR, 0x30 + SPI_ID[3], 0, 0);}SPI_ID_Temp = SPI_IDx; //数据备份,为判断数据是否发生变化}}/************************************************************** ************//************************************************************** ************//****** 系统时钟配置函数 ********/void RCC_Configuration(void){ErrorStatus HSEStartUpStatus;RCC_DeInit(); //RCC system reset(for debug purpose)RCC_HSEConfig(RCC_HSE_ON); //Enable HSEHSEStartUpStatus = RCC_WaitForHSEStartUp(); //Wait till HSE is readyif(HSEStartUpStatus == SUCCESS){FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Enable Prefetch BufferFLASH_SetLatency(FLASH_Latency_2); //Set 2 Latency cyclesRCC_HCLKConfig(RCC_SYSCLK_Div1); //AHB clock = SYSCLK RCC_PCLK2Config(RCC_HCLK_Div2); //APB2 clock = HCLK/2 RCC_PCLK1Config(RCC_HCLK_Div4); //APB1 clock = HCLK/4 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHz * 9 = 72 MHzRCC_PLLCmd(ENABLE); //Enable PLLwhile(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //Wait till PLL is readyRCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Select PLL as system clock sourcewhile(RCC_GetSYSCLKSource() != 0x08); //Wait till PLL is used as system clock source//-----------------------------------------------------RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB| RCC_APB2Periph_SPI1, ENABLE); //GPIOD clock ENABLERCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //TIM2 clock ENABLE}}/****** 中断配置函数 ********/void NVIC_Configuration(void){NVIC_InitTypeDef NVIC_InitStructure;//--- 在内存中进行调试 -----#ifdef VECT_TAB_RAMNIVC_SetVectorTable(NVIC_VectTab_RAM, 0x0);//--- 在FLASH中进行调试 ----#elseNVIC_SetVectorTable(NVIC_VectTab_FLASH,0x0);#endif//---- 配置TIM2中断,并使能 -----NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQChannel;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//------------------------------------NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQChannel;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);}/********** 通用I/O配置函数 ***************/void GPIO_Configuration(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//------------------GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;GPIO_Init(GPIOB, &GPIO_InitStructure);}/*****************************************************函数名称:SPI_Configuration(void)功能说明:SPI通信模块配置,配置SPI工作模式;输入参数:无返回值:无*****************************************************/void SPI_Configuration(void){SPI_InitTypeDef SPI_InitStructure;//--------------------- SPI1 configuration ------------------SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;SPI_InitStructure.SPI_Mode = SPI_Mode_Master;SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;SPI_InitStructure.SPI_CRCPolynomial = 7;SPI_Init(SPI1, &SPI_InitStructure);//--------- SPI2 configuration ------------------SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;SPI_Init(SPI2, &SPI_InitStructure);//--------- Enable SPI1 TXE interrupt ------------SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, ENABLE);//--------- Enable SPI2 RXNE interrupt -------------SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE);//----- Enable SPI2 ------SPI_Cmd(SPI2, ENABLE);//----- Enable SPI1 ----SPI_Cmd(SPI1, ENABLE);}/*************************************************/#ifdef DEBUGvoid assert_failed(u8 *file, u32 line){while(1){}}#endif/***********************************************************/ /***********************************************************/#include "stm32f10x_it.h"/****************************/extern vu16 SPI_IDx;/*********** TX发送中断服务子程序 *********************/ void SPI1_IRQHandler(void){//-------- 发送数据 ----------SPI_I2S_SendData(SPI1, 47);}/*********** RX接收中断服务子程序 ****************/void SPI2_IRQHandler(void){//------------- 接收数据 ------------SPI_IDx = SPI_I2S_ReceiveData(SPI2);}。
STM32SPI接口的简单实现
STM32SPI接口的简单实现SPI(Serial Peripheral Interface,串行外设接口)是一种常用的外设通信接口协议,用于与外部设备进行高速串行数据通信。
在STM32芯片中,SPI接口是通过SPI外设模块来实现的。
本文将介绍STM32 SPI接口的简单实现方法。
1.SPI接口的基本原理SPI接口是一种同步的、全双工的、点对点的通信协议,它由一个主设备和一个或多个从设备组成。
主设备通过时钟信号(SCK)为从设备提供时钟,同时通过主设备出发的数据信号(MOSI)发送数据给从设备。
从设备通过时钟信号和主设备进行同步,并通过数据信号(MISO)返回数据给主设备。
2.SPI硬件结构在STM32芯片中,多个SPI外设模块(SPI1、SPI2、SPI3等)可以分别配置为主模式或从模式。
每个SPI外设模块都包含一个发送数据寄存器(SPI_DR)和一个控制寄存器(SPI_CR1)。
SPI_DR寄存器用于发送和接收数据,SPI_CR1寄存器用于配置SPI模式、通信速率等参数。
3.SPI初始化配置在使用SPI接口之前,首先需要对SPI进行初始化配置。
具体配置步骤如下:a.配置GPIO管脚,将SPI相关管脚配置为SPI模式。
b.配置SPI_CR1寄存器,设置SPI模式(主模式或从模式)、通信速率、数据长度等参数。
c.使能SPI外设模块。
4.数据传输函数SPI提供了两种数据传输方式:轮询模式和中断模式。
在轮询模式下,主设备通过查询标志位的方式等待从设备的响应,然后发送或接收数据。
在中断模式下,主设备可以配置为在数据发送或接收完成时触发中断,然后进入中断服务程序处理数据。
5.数据传输流程SPI的数据传输流程通常分为以下几个步骤:a.配置SPI模式、通信速率等参数。
b.通过SPI_DR寄存器发送数据。
c.等待接收完毕或发送完毕。
d.通过SPI_DR寄存器读取接收到的数据。
6.实例代码下面是一个简单的SPI接口实现示例代码,以SPI1为例:```c#include "stm32f4xx.h"void SPI1_Init(void)/*配置GPIO管脚*//*将SPI1对应的GPIO管脚配置为SPI模式*//*配置SPI_CR1寄存器*//*设置SPI模式为主模式,通信速率为1MHz,数据长度为8位*/ /*使能SPI外设模块*/SPI1->CR1,=SPI_CR1_SPE;void SPI_SendData(uint8_t data)/*等待发送缓冲区为空*/while (!(SPI1->SR & SPI_SR_TXE));/*将数据写入发送缓冲区*/SPI1->DR = data;uint8_t SPI_ReceiveData(void)/*等待接收缓冲区非空*/while (!(SPI1->SR & SPI_SR_RXNE));/*读取接收缓冲区的数据*/return SPI1->DR;int main(void)uint8_t sendData;/*初始化SPI1外设*/SPI1_Init(;/*发送数据*/sendData = 0xAA;SPI_SendData(sendData);/*接收数据*/uint8_t receiveData = SPI_ReceiveData(;while (1)/*业务逻辑处理*/}```以上就是STM32SPI接口的简单实现方法,通过对SPI进行初始化配置和数据传输函数的调用,即可实现与外部设备的通信。
STM32SPI接口的简单实现要点
STM32 SPI接口的简单实现通常SPI通过4个引脚与外部器件相连:・MISO:主设备输入/从设备输出引脚。
该引脚在从模式下发送数据,在主模式下接收数据。
・MOSI:主设备输出/从设备输入引脚。
该引脚在主模式下发送数据,在从模式下接收数据。
・SCK:串口时钟,作为主设备的输出,从设备的输入・NSS:从设备选择。
这是一个可选的引脚,用来选择主/从设备。
它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
从设备的NSS引脚可以由主设备的一个标准I/O引脚来驱动。
一旦被使能(SSOE位),NSS引脚也可以作为输出引脚,并在SPI处于主模式时拉低;此时,所有的SPI设备,如果它们的NSS引脚连接到主设备的NSS引脚,则会检测到低电平,如果它们被设置为NSS硬件模式,就会自动进入从设备状态。
当配置为主设备、NSS配置为输入引脚(MSTR=1, SSOE=0)时,如果NSS被拉低,则这个SPI 设备进入主模式失败状态:即MSTR位被自动清除,此设备进入从模式。
时钟信号的相位和极性SPI_CR寄存器的CPOL和CPHA位,能够组合成四种可能的时序关系。
CPOL(时钟极性)位控制在没有数据传输时时钟的空闲状态电平,此位对主模式和从模式下的设备都有效。
如果CPOL被清‘ 0’,SCK引脚在空闲状态保持低电平;如果CPOL 被置’ 1’,SCK引脚在空闲状态保持高电平。
如果CPHA (时钟相位)位被置‘ 1’,SCK时钟的第二个边沿(CPOL位为0时就是下降沿,CPOL位为‘ 1’时就是上升沿)进行数据位的采样,数据在第二个时钟边沿被锁存。
如果CPHA位被清‘ 0’,SCK时钟的第一边沿(CPOL位为‘ 0’时就是下降沿,CPOL位为‘ 1’时就是上升沿)进行数据位采样,数据在第一个时钟边沿被锁存。
CPOL时钟极性和CPHA时钟相位的组合选择数据捕捉的时钟边沿。
图212显示了SPI传输的4种CPHA和CPOL位组合。
STM32SPI详解
STM32SPI详解1、SPI简介SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). ⼀个 Master 设备可以通过提供 Clock 以及对 Slave 设备进⾏⽚选 (Slave Select) 来控制多个Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本⾝不能产⽣或控制 Clock, 没有 Clock 则 Slave 设备不能正常⼯作。
2、SPI特点2.1、SPI控制⽅式采⽤主-从模式(Master-Slave) 的控制⽅式。
SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave). ⼀个 Master 设备可以通过提供 Clock 以及对 Slave 设备进⾏⽚选 (Slave Select) 来控制多个Slave 设备, SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本⾝不能产⽣或控制 Clock, 没有 Clock 则 Slave 设备不能正常⼯作。
2.2、SPI传输⽅式采⽤同步⽅式(Synchronous)传输数据Master 设备会根据将要交换的数据来产⽣相应的时钟脉冲(Clock Pulse), 时钟脉冲组成了时钟信号(Clock Signal) , 时钟信号通过时钟极性 (CPOL) 和时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进⾏采样, 来保证数据在两个设备之间是同步传输的。
2.3、SPI数据交换SPI数据交换框图上图只是对 SPI 设备间通信的⼀个简单的描述, 下⾯就来解释⼀下图中所⽰的⼏个组件(Module):SSPBUF,Synchronous Serial Port Buffer, 泛指 SPI 设备⾥⾯的内部缓冲区, ⼀般在物理上是以 FIFO 的形式, 保存传输过程中的临时数据;SSPSR, Synchronous Serial Port Register, 泛指 SPI 设备⾥⾯的移位寄存器(Shift Regitser), 它的作⽤是根据设置好的数据位宽(bit-width) 把数据移⼊或者移出 SSPBUF; Controller, 泛指 SPI 设备⾥⾯的控制寄存器, 可以通过配置它们来设置 SPI 总线的传输模式。
简述SPI的使用流程
简述SPI的使用流程SPI(Serial Peripheral Interface)是一种同步的串行通信接口协议,常用于连接微控制器、传感器、外围设备等。
在SPI通信中,主设备(MCU)控制一个或多个从设备(外设)进行通信。
SPI的使用流程主要包括以下几个步骤:1.硬件连接:首先需要将主设备与从设备通过SPI接口进行连接。
通常,SPI接口包括四根信号线:SCLK(时钟线)、MISO(主设备数据输出从设备数据输入线)、MOSI(主设备数据输入从设备数据输出线)和SS (片选线)。
通过连接这些信号线,主设备与从设备可以进行数据传输和通信。
2.配置主设备:在主设备中,需要配置SPI接口的设置,包括时钟频率、数据传输位序(高位先传输或低位先传输)、数据传输格式(数据位数、数据线的电平极性和相位)等。
这些设置通常由MCU提供的SPI控制寄存器进行配置。
3.初始化从设备:在进行SPI通信之前,需要对从设备进行初始化设置。
这通常涉及到配置从设备的工作模式、使能信号线(如片选线)、设置从设备的地址等。
4.发送数据:主设备通过SPI接口向从设备发送数据。
这需要先选择从设备(通过拉低相应的片选线),然后将待发送的数据写入SPI控制寄存器。
主设备每次发送一个数据字节,等待传输完成后再发送下一个。
5.接收数据:主设备在发送数据的同时,会接收从设备返回的数据。
从设备在接收到主设备的数据后,会将自己的响应数据通过MISO线发送给主设备。
主设备在SPI控制寄存器中可以读取到收到的数据。
6.关闭通信:当通信完成后,可以关闭通信。
通常,需要将片选线恢复为高电平,以完成与从设备的通信。
总结起来,SPI的使用流程可以归纳为硬件连接、配置主设备、初始化从设备、发送数据、接收数据和关闭通信等六个步骤。
其中,主设备通过配置SPI的相关设置和发送数据,从设备通过初始化设置和接收数据来完成通信。
SPI通信具有简洁、高速、性能稳定等特点,广泛应用于各种嵌入式系统中。
STM32硬件SPI主从通信教程
STM32硬件SPI主从通信教程STM32是一款广泛使用的微控制器,具有丰富的外设和强大的性能。
其中,硬件SPI(Serial Peripheral Interface)是一种常用的通信协议,用于在主设备和从设备之间进行高速数据交换。
本篇文章将介绍如何在STM32上实现硬件SPI主从通信。
首先,我们需要了解SPI的工作原理。
SPI是一种同步串行通信协议,使用四根线进行通信:SCLK(时钟线)、MOSI(主输出从输入线)、MISO (主输入从输出线)和NSS(片选信号线)。
其中,时钟线用于同步数据传输,主设备通过MOSI线发送数据,从设备通过MISO线接收数据,NSS线用于选择从设备。
SPI可以支持全双工通信,即主设备和从设备可以同时发送和接收数据。
在STM32中,SPI外设由SPI1、SPI2等多个SPI控制器组成。
每个SPI控制器都有两个工作模式:主模式和从模式。
主设备负责控制通信的时序和片选信号,从设备则被动地响应主设备的操作。
现在,我们来编写一个简单的SPI主从通信程序,以说明硬件SPI的使用方法。
首先,我们需要初始化SPI控制器。
在STM32CubeIDE中,可以通过CubeMX工具进行外设的初始化配置。
选择SPI控制器并设置工作模式为主模式,然后生成代码。
在生成的代码中,我们需要将SPI控制器的时钟频率、数据位、校验位等参数进行配置,并使能SPI外设。
此外,还需要配置GPIO,将SCLK、MOSI、MISO和NSS线连接到正确的引脚上。
接下来,我们需要编写主设备和从设备的SPI通信代码。
首先,主设备通过SPI发送数据给从设备://选择从设备HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);//发送数据uint8_t sendData = 0x55;HAL_SPI_Transmit(&hspi1, &sendData, 1, HAL_MAX_DELAY);//取消选择从设备HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);```这段代码首先使用NSS引脚选择从设备,然后使用HAL_SPI_Transmit函数发送数据。
最新STM32的SPI通信总结(含DMA)
STM32---SPI(DMA)通信的总结(库函数操作)本文主要由7项内容介绍SPI并会在最后附上测试源码供参考:1.SPI的通信协议2.SPI通信初始化(以STM32为从机,LPC1114为主机介绍)3.SPI的读写函数4.SPI的中断配置5.SPI的SMA操作6.测试源码7.易出现的问题及原因和解决方法一、SPI的通信协议SPI(Serial Peripheral Interface)是一种串行同步通讯协议,由一个主设备和一个或多个从设备组成,主设备启动一个与从设备的同步通讯,从而完成数据的交换。
SPI 接口一般由4根线组成,CS片选信号(有的单片机上也称为NSS),SCLK时钟信号线,MISO数据线(主机输入从机输出),MOSI数据线(主机输出从机输入),CS 决定了唯一的与主设备通信的从设备,如没有CS 信号,则只能存在一个从设备,主设备通过产生移位时钟信号来发起通讯。
通讯时主机的数据由MISO输入,由MOSI 输出,输入的数据在时钟的上升或下降沿被采样,输出数据在紧接着的下降或上升沿被发出(具体由SPI的时钟相位和极性的设置而决定)。
二、以STM32为例介绍SPI通信1.STM32f103 带有3个SPI模块其特性如下:2SPI 初始化初始化SPI 主要是对SPI要使用到的引脚以及SPI通信协议中时钟相位和极性进行设置,其实STM32的工程师已经帮我们做好了这些工作,调用库函数,根据自己的需要来修改其中的参量来完成自己的配置即可,主要的配置是如下几项:引脚的配置SPI1的SCLK, MISO ,MOSI分别是PA5,PA6,PA7引脚,这几个引脚的模式都配置成GPIO_Mode_AF_PP 复用推挽输出(关于GPIO的8种工作模式如不清楚请自己百度,在此不解释),如果是单主单从,CS引脚可以不配置,都设置成软件模式即可。
通信参数的设置1.SPI_Direction_2Lines_FullDuplex把SPI设置成全双工通信;2.在SPI_Mode 里设置你的模式(主机或者从机),3.SPI_DataSize是来设置数据传输的帧格式的SPI_DataSize_8b是指8位数据帧格式,也可以设置为SPI_DataSize_16b,即16位帧格式4.SPI_CPOL和SPI_CPHA是两个很重要的参数,是设置SPI通信时钟的极性和相位的,一共有四种模式在库函数中CPOL有两个值SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0). CPHA有两个值SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)CPOL表示时钟在空闲状态的极性是高电平还是低电平,而CPHA则表示数据是在什么时刻被采样的,手册中如下:我的程序中主、从机的这两位设置的相同都是设置成1,即空闲时时钟是高电平,数据在第二个时钟沿被采样,实验显示数据收发都正常。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
例子说明及框图本例子基于STM32F103ZET6芯片(代码工程可在文末获取),实现SPI1与SPI2的主从通信。
其中SPI1配置为主机,SPI2配置为从机,均配置为全双工模式。
硬件连接图:其中,我们需要注意的是,SPI的从机不能主动发送数据,只能被动应答数据。
本例子的数据交互过程:1.主机使用查询方式发送数据给从机。
2.从机使用中断接收方式接收数据,把接收到的数据加上0x05再发送给主机。
从机总是在收到主机的数据时,才会发送数据给从机。
即从机被动发送数据,也即主机主动申请数据。
代码细节主函数:int main(void){uint8_t i = 0;//-----------------------------------------------------------------------------------------------// 上电初始化函数SysInit();//-----------------------------------------------------------------------------------------------// 主程序while (1){/* 主机发、收数据 */for (i = 0; i < SPI_BUF_LEN; i++){ucSPI1_RxBuf[i] = SPI1_ReadWriteByte(ucSPI1_TxBuf[i]);}}return 0;}其中,ucSPI1_RxBuf与ucSPI1_TxBuf的定义为:uint8_t ucSPI1_RxBuf[SPI_BUF_LEN] = {0};uint8_t ucSPI1_TxBuf[SPI_BUF_LEN] = {0x01, 0x02, 0x03, 0x04, 0x05};SPI1_ReadWriteByte函数为SPI1的读写函数,其作用是往SPI1发送缓冲区写入数据的同时可以读取SPI1接收缓冲区中的数据,其内部实现为:uint8_t SPI1_ReadWriteByte(uint8_t TxData){while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // 等待发送区空SPI_I2S_SendData(SPI1, TxData); // 通过外设SPI1发送一个byte数据while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);// 等待接收完一个bytereturn SPI_I2S_ReceiveData(SPI1); // 返回通过SPIx最近接收的数据}为什么可以这么写呢?看一下SPI的框图:从框图可看出SPI有 2 个缓冲区,一个用于写入(发送缓冲区),一个用于读取(接收缓冲区)。
对数据寄存器执行写操作时,数据将写入发送缓冲区,从数据寄存器执行读取时,将返回接收缓冲区中的值。
这样写并不会出现读到的数据等于发送的数据。
SPI2中断函数:void SPI2_IRQHandler(void){/* 判断接收缓冲区是否为非空 */if (SET == SPI_I2S_GetITStatus(SPI2, SPI_I2S_IT_RXNE)){ucSPI2_RxBuf[ucSPI2_RxCount] = SPI2->DR; /* 读取接收缓冲区数据 */while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); /* 等待发送区空 */SPI2->DR = ucSPI2_RxBuf[ucSPI2_RxCount] + 0x05; /* 往发送缓冲区填数据 *//* 计数器处理 */ucSPI2_RxCount++;if (ucSPI2_RxCount > SPI_BUF_LEN - 1){ucSPI2_RxCount = 0;}/* 清中断标志 */SPI_I2S_ClearITPendingBit(SPI2, SPI_I2S_IT_RXNE);}}从机接收到主机数据后,会加上0x05,再返还给主机。
SPI1初始化函数:void bsp_SPI1_Init(void){/* 定义SPI结构体变量 */GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;/* SPI的IO口和SPI外设打开时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);/* SPI的IO口设置 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);/* SPI的基本配置 */SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 设置SPI工作模式:设置为主SPISPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 设置SPI的数据大小:SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 串行同步时钟的空闲状态为高电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 串行同步时钟的第二个跳变沿(上升或下降)数据被采样SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;// 定义波特率预分频的值:波特率预分频值为256SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;// 指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC值计算的多项式SPI_Init(SPI1, &SPI_InitStructure); // 根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_Cmd(SPI1, ENABLE); // 使能SPI外设}SPI1配置为主模式,全双工。
SPI2初始化函数:void bsp_SPI2_Init(void){/* 定义SPI结构体变量 */GPIO_InitTypeDef GPIO_InitStructure;SPI_InitTypeDef SPI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;/* SPI的IO口和SPI外设打开时钟 */RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);/* SPI的IO口设置 */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);/* SPI的基本配置 */SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; // 设置SPI工作模式:设置为从SPISPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 设置SPI的数据大小:SPI发送接收8位帧结构SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; // 串行同步时钟的空闲状态为高电平SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // 串行同步时钟的第二个跳变沿(上升或下降)数据被采样SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;// 定义波特率预分频的值:波特率预分频值为256SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;// 指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC值计算的多项式SPI_Init(SPI2, &SPI_InitStructure); // 根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE); // 使能接收中断SPI_Cmd(SPI2, ENABLE); // 使能SPI2外设/* NVIC中断控制器配置 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级分组2NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn; // SPI2中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; // 抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; // 子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器}SPI2配置为从模式,全双工,使能接收中断。