目录
一:SPL通信
1:简历
2:硬件电路
3:移动数据图
4:SPI时序基本单元
A : 开/ 终条件
B:SPI时序基本单元
A:模式0
B:模式1
C:模式2
D:模式3
C:SPl时序
A:发送指令
B: 指定地址写
C:指定地址读
5:NSS(CS)
6:时钟
二: W25Q64
1:简历
2: 硬件电路
3:W25Q64框图
4: Flash操作注意事项
5:指令集
三:HAL
A:宏函数
B:轮询方式
C:中断
C:DMA
四:案例
A:轮询方式--W25Q64
B:单线双向模式--MAX7219(数码管)
C:中断方式--W25Q64
D:MDA----W25Q64
主从通信
A:中断+主机身份不改变
B:中断+DMA
C:多主机通信--主从身份可以改变
一:SPL通信
STM32F1c8T6 有2个SPI。
1:简历
SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线
四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)(NSS)
同步(有时钟线),全双工 (传输线有2条,发送和接受线路)
支持总线挂载多设备(一主多从)
SPl没有应答机制
2:硬件电路
所有SPI设备的SCK、MOSI、MISO分别连在一起
主机另外引出多条SS(NSS)控制线,分别接到各从机的SS(NSS)引脚
输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入
SS(NSS)也叫CS片选信号 : 和全部的从机连接, 用于选择主机和那个从机进行通信, 低电平有效; 每个从机的SS(CS)都和主机的SSX相互连接, SS对于主机来说的话,就是输出信号, 从机的话就是输入信号
IO的配置 : 都是以STM32为主角进行的. 主机输出信号配置---推挽输出, 主机输入信号配置----浮空或上拉输入
SCK : 时钟线, 时钟线完全由主机掌控, 所以对于主机来说,时钟线为输出; 对于所有从机来说,时钟线都为输入; 这样主机的同步时钟,就能送到各个从机了
MOSI : 主机输出,从机输入
MISO : 主机输入,从机输出
关于CS和MISO主机输入,从机输出 : 当从机没有被选中的时候,也就是SS段电平为1; 从机的MISO主机输入,从机输出必须切换为高阻态 , 高阻态就相当于引脚断开,不输出任何电平; 这样就可以防止,一条线有多个输出,而导致的电平冲突的问题了; 在SS为低电平时,MISO才允许变为推挽输出----从机的操作-------一般情况下我们只需要写主机的程序,从机的程序不需要我们操心
3:移动数据图
交换数据, 高位先行
SPI的数据收发,都是基于字节交换,这个基本单元来进行的 (移位模型)
首先,我们规定波特率发生器时钟的上升沿主机和从机都移出数据; 下将沿移入数据;
数据为从左往右运动,所以是高为先行, 首先波特率发生器时钟产生上生沿, 主机把它的最高位的数据放在MOSI上面, 从机把它最高位置的数据放在MISO上面; 在由特率发生器产生的下降沿移入数据; 在MISO数据线上从机的最高位的数据放在主机的最低位置上面; MOSI上面主机最高位的数据放在从机的最低位置
4:SPI时序基本单元
A : 开/ 终条件
起始条件:SS从高电平切换到低电平
终止条件:SS从低电平切换到高电平
B:SPI时序基本单元
在任何操作下, 我们只管主机(只写主机的代码) , 从机它自动操作(不用写从机的代码)
我们经常使用的是模式0;主机和从机的模式必须一样,主机采用什么模式,取决于操作的芯片支持那种工作模式。
A:模式0
交换一个字节(模式0)
CPOL=0:空闲状态时,SCK为低电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据(第一个边沿采集)
SCL上升沿主机和从机同步移入数据; SCL下降沿主机和从机同步移出数据
/** * @brief SPL交换数据--使用的为模式0 DI(MOSI)----SPI主机输出从机输入 DO(MISO)-------SPI主机输入从机输出 我们只操作主机:首先主机移出最高位,放在MOSI上面,---主机操作需要我们来 齐次从机把数据放在MISO上面----从机的操作不需要我们管 * @param ByteSend: 主机给从机发送的数据 * @retval 主机读取的数据----即从机给主机发送的数据 */ uint8_t MySPI_SwapByte(uint8_t ByteSend) { MySPI_W_SCK(0); //一般来说&是用来清零的; //一般来说|是用来值一的; uint8_t ByteReceive=0x00; for (uint8_t i=0;i<8;i++) { MySPI_W_MOSI(ByteSend & (0x80>>i)); //MOSI主机输出数据 1000 0000 /* 我们只操作主机: SCL上升沿主机和从机同步移入数据, 从机会自动把主机给它的最高为移动到了从机里面---从机不需要我们操作 主机操作 : 主机需要把从机给它发送的数据移动到了主机里面---即读取MISO线上的数据 */ MySPI_W_SCK(1); if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}//MySPI_R_MISO主机读取数据 MySPI_W_SCK(0); //SCL下降沿主机和从机同步移出数据 //|---置1 } return ByteReceive; }
在任何操作下, 我们只管主机(只写主机的代码) , 从机它自动操作(不用写从机的代码)
B:模式1
交换一个字节(模式1)
CPOL=0:空闲状态时,SCK为低电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据(第二个边沿采集)
SPl为了可以配置更多的时钟芯片, 给我们了2个可以自己调节的位, 分别为:CPOL (Clock Polarity)时钟极性和CPHA (Clock Phase)时钟相位配置这两个为, 就构成了4种模式
模式1 : 波特率发生器时钟的上升沿主机和从机都移出数据; 下将沿移入数据; 模式1的数据移动方式和 3:移动数据图 一样 , 详情参考----3:移动数据图
C:模式2
交换一个字节(模式2)
CPOL=1:空闲状态时,SCK为高电平
CPHA=0:SCK第一个边沿移入数据,第二个边沿移出数据 (第一个边沿采集。同样是在SCLK下降沿进行数据采样,上升沿进行数据传输。)
D:模式3
交换一个字节(模式3)
CPOL=1:空闲状态时,SCK为高电平
CPHA=1:SCK第一个边沿移出数据,第二个边沿移入数据(第二个边沿采集。在SCLK上升沿进行数据采样,下降沿进行数据传输。)
C:SPl时序
A:发送指令
规定 : SPL起始的第一个字节为指令集
发送指令
向SS指定的设备,发送指令(0x06)--0x06使能
B: 指定地址写
指定地址写
向SS指定的设备,发送写指令(0x02),---0x02写入的指令集
随后在指定地址(Address[23:0])下,写入指定数据(Data)
SPl没有应答机制, 交换一个字节后, 直接开始交换下一个字节
C:指定地址读
指定地址读
向SS指定的设备,发送读指令(0x03),---0x03发送指令的指令集
随后在指定地址(Address[23:0])下,读取从机数据(Data)
5:NSS(CS)
SPI_NSS_SOFT
应用:STM32在做主机,防止丢失主机身份。(SPI支持多主机通信,一个STM32可以在主机和从机身份中来回切换。)
SPI_NSS_HARD_INPUT:
主模式切换为从模式:
主控从主机切换为从机身份。NSS被拉意外丢失主模式。
应用:多少应用于多主机身份。
SPI_NSS_HARD_OUTPUT:
6:时钟
SPI1挂载在APB2总线上,APB2总线的最大时钟频率为=72MHZ
SPI2挂载在APB1总线上,APB2总线的最大时钟频率为=36MHZ
二: W25Q64
1:简历
W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器,常应用于数据存储、字库存储、固件程序存储等场景。支持模式0和模式3。
存储介质:Nor Flash(闪存)
时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI)
存储容量(24位地址):
W25Q40: 4Mbit / 512KByte
W25Q80: 8Mbit / 1MByte
W25Q16: 16Mbit / 2MByte
W25Q32: 32Mbit / 4MByte
W25Q64: 64Mbit / 8MByte
W25Q128: 128Mbit / 16MByte
W25Q256: 256Mbit / 32MByte
2: 硬件电路
3:W25Q64框图
4: Flash操作注意事项
非易失性存储器---掉电不丢失
写入操作时:
写入操作前,必须先进行写使能------------是一种保护措施,防止你误操作的
每个数据位只能由1改写为0,不能由0改写为1(写入只能写0,不能写1。)--------------Flash并没有像RAM那样的, 直接完全覆盖改写的能力. eg:在某一个直接的储存单元首先储存了0xaa 1010 1010 在储存0x55 0101 0101 因为Flash没有直接覆盖数据的能力, 在加上第二条规定的限制实际储存的数据为: 0000 0000 不是0x55, 使用在写入第二给数据前必须擦除之前的数据
写入数据前必须先擦除,擦除后,所有数据位变为1--------------有专门的擦除电路把之前写的数据都值1(0xFF), 就可以弥补第二条规定的不足
擦除必须按最小擦除单元进行------------不能指定某一个字节去擦除, 要擦,就得一大片一起擦, 在我们这个芯片里; 你可以选择,整个芯片擦除, 也可以选择,按块擦除,或者按扇区擦除; 最小的擦除单元,就是一个扇区, 个扇区,是4KB,就是4096个字节
连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入--------一个写入时序,最多只能写一页的数据,也就是256字节; 一个页缓存区,它只有256字节; Flash的写入,太慢了. 跟不上SPI的频率. 所以写入的数据,会先放在RAM里暂存. 必须得,从页起始位置开始,才能最大写入256字节, 如果从页中间的地址开始写, 那写到页尾时,这个地址就会跳回到页首, 这会导致地址错乱
写入操作结束后,芯片进入忙状态,不响应新的读写操作--------要想知道芯片什么时候结束忙状态了, 我们可以使用读取状态寄存器的指令, 看一下状态寄存器的BUSY位是否为1, BUSY位为0时,芯片就不忙了,我们再进行操作
在发出擦除指令后,芯片也会进入忙状态, 我们也得等待忙状态结束后,才能进行后续操作
扇区擦除也是写入所以需要使能
读取操作时:
直接调用读取时序,无需使能,无需额外操作,没有页的限制,
读取操作结束后不会进入忙状态,但不能在忙状态时读取,
5:指令集
INSTRUCTION NAME---指令的名字; BYTE----字节X
Write Enable----写使能指令集
Write Disable --------写失能指令集
Read Status Register-1---------读状态寄存器1--作用: 判断寄存器在不在忙, 具体见 二: 4
Page Program----------页编程, 写数据,max为256个字节
Sector Erase (4KB)-------------按4KB的扇区擦除
JEDEC ID----------读取ID
Read Data-----读取数据
三:HAL
A:宏函数
B:轮询方式
HAL_SPl_Transmit:
HAL_SPl_Receive:
C:中断
C:DMA
四:案例
A:轮询方式--W25Q64
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "w25q64.h"
uint8_t wbuff[4096];
uint8_t rbuff[4096];
uint16_t i=0;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi1_Init();
W25q64_SectorErase(0);
W25q64_ReadID();
//
// for(i=0;i<4096;i++)
// {
// wbuff[i]=0x66;
//
// }
//
// for(i=0;i<16;i++)
// {
// //W25q64_WritePage(&wbuff[i*256],i);
// }
//
// W25q64_ReadData(rbuff,0,4096);
//
// for(i=0;i<4096;i++)
// {
// printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
// }
while (1)
{
}
}
#include "stm32f1xx_hal.h"
SPI_HandleTypeDef SPI_Handle;
void Spi1_Init()
{
SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2; //分频
SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE; //第一个边沿捕获;CPHA
SPI_Handle.Init.CLKPolarity=SPI_POLARITY_LOW; //CPOL时钟极性
SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI_Handle.Init.NSS=SPI_NSS_SOFT; //NSS软件模式
HAL_SPI_Init(&SPI_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 普通的IO口
GPIO_InitType.Pin=GPIO_PIN_0;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
//MISO 主机输入,从机输出
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
//MOSI 主机输出,从机输入
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_7;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
#define CS_ENABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
#define CS_DISABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
/**
* @brief 查看busy位是否忙碌.
*/
void W25q64_Busy(void)
{
uint8_t cmd[2],data[2];
cmd[0]=0x05;
cmd[1]=0xFF;
do
{
CS_ENABLE;
HAL_SPI_TransmitReceive(&SPI_Handle,cmd,data,2,1000);
CS_DISABLE;
}
while((data[1] & 0x01)==0x01); //busy为0不忙;
}
/**
* @brief 写使能.
*/
void W25q64_WriteEnable(void)
{
uint8_t cmd;
cmd=0x06;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,&cmd,1,1000);
CS_DISABLE;
}
/**
* @brief 擦除一个扇区的数据(4K;4*1024=4096)
* @param 擦除第几页的数据
*/
void W25q64_SectorErase(uint32_t Sectornum)
{
uint8_t cmd[4];
cmd[0]=0x20;
cmd[1]=(Sectornum*4096)>>16;
cmd[2]=(Sectornum*4096)>>8;
cmd[3]=(Sectornum*4096)>>0;
W25q64_WriteEnable();
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
CS_DISABLE;
}
/**
* @brief 写入一页数据,一页256字节
* @param data: 写入的数据
* @param PageNum:给第几页写入数据
*/
void W25q64_WritePage(uint8_t * data,uint32_t PageNum)
{
uint8_t cmd[4];
cmd[0]=0x02;
cmd[1]=(PageNum*256)>>16;
cmd[2]=(PageNum*256)>>8;
cmd[3]=(PageNum*256)>>0;
W25q64_WriteEnable();
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
HAL_SPI_Transmit(&SPI_Handle,data,256,5000); //sizeof(data)
CS_DISABLE;
}
/**
* @brief 读取数据
* @param *rdata: 读取数据存放的地址
* @param addr: 读取第几页
* @param len: 读取数据的长度(几个字节)
* @retval
*/
void W25q64_ReadData(uint8_t *rdata,uint32_t addr ,uint32_t len)
{
uint8_t cmd[4];
cmd[0]=0x03;
cmd[1]=addr>>16;
cmd[2]=addr>>8;
cmd[3]=addr>>0;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
HAL_SPI_Receive(&SPI_Handle,rdata,len,50000);
CS_DISABLE;
}
/**
* @brief 读取ID.
*/
void W25q64_ReadID()
{
uint8_t cmd[1],data_id[3];
cmd[0]=0x9F;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,1,1000);
HAL_SPI_Receive(&SPI_Handle,data_id,sizeof(data_id),50000);
CS_DISABLE;
printf("W25q64的ID为:制造商ID=%02X 设备ID=%02X 容量ID=%02X\r\n",
data_id[0], data_id[1], data_id[2]);
}
重要参数:
HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
typedef struct
{
uint32_t Mode; !< 指定SPI的工作模式。
这个参数可以是@ref SPI_Mode中的一个值 */
uint32_t Direction; !< 指定SPI双向模式的状态。
这个参数可以是@ref SPI_Direction中的一个值 */
uint32_t DataSize; !< 指定SPI的数据大小。
这个参数可以是@ref SPI_Data_Size中的一个值 */
uint32_t CLKPolarity; !< 指定串行时钟的稳定状态。
这个参数可以是@ref SPI_Clock_Polarity中的一个值 */
uint32_t CLKPhase; !< 指定用于位捕获的时钟活动边沿。
这个参数可以是@ref SPI_Clock_Phase中的一个值 */
uint32_t NSS; !< 指定NSS信号是由硬件(NSS引脚)管理还是由软件使用SSI位管理。
这个参数可以是@ref SPI_Slave_Select_management中的一个值 */
uint32_t BaudRatePrescaler; !< 指定波特率预分频值,用于配置发送和接收SCK时钟。
这个参数可以是@ref SPI_BaudRate_Prescaler中的一个值
@note 通信时钟是从主时钟派生出来的。从机时钟不需要设置。 */
uint32_t FirstBit; !< 指定数据传输是从MSB(最高有效位)还是LSB(最低有效位)开始。
这个参数可以是@ref SPI_MSB_LSB_transmission中的一个值 */
uint32_t TIMode; !< 指定是否启用TI模式。
这个参数可以是@ref SPI_TI_mode中的一个值 */
uint32_t CRCCalculation; !< 指定是否启用CRC(循环冗余校验)计算。
这个参数可以是@ref SPI_CRC_Calculation中的一个值 */
uint32_t CRCPolynomial; !< 指定用于CRC计算的多项式。
这个参数必须是一个介于Min_Data = 1和Max_Data = 65535之间的奇数 */
} SPI_InitTypeDef;
B:单线双向模式--MAX7219(数码管)
频率:
模式:
CLK极性默认为低;上升沿采集数据说明第一个边沿采集数据。
高位先行:
译码的方法:
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi2_Init();
delay_ms(50);
Init_MAX7219();
delay_ms(1000);
Write_Max7219(0x0f, 0x00); //显示测试:1;测试结束,正常显示:0
Write_Max7219(1,8); //第一个数码管显示1
//Write_Max7219(1,0x88); //D7位变为高电平,控制小数点
Write_Max7219(2,7);
Write_Max7219(3,6);
Write_Max7219(4,5);
Write_Max7219(5,4);
Write_Max7219(6,3);
Write_Max7219(7,2);
Write_Max7219(8,1);
while (1)
{
}
}
#include "stm32f1xx_hal.h"
SPI_HandleTypeDef SPI2_Handle;
//MAX7219
void Spi2_Init()
{
SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16; //分频 36/16=2
SPI2_Handle.Init.CLKPhase=SPI_PHASE_1EDGE; //第一个边沿捕获;CPHA
SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_LOW; //CPOL时钟极性 SPI_POLARITY_HIGH
SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI2_Handle.Init.Direction=SPI_DIRECTION_2LINES; //单线半双工
SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI2_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI2_Handle.Init.NSS=SPI_NSS_SOFT; //NSS软件模式
HAL_SPI_Init(&SPI2_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI2)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_SPI2_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
//片选
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 普通的IO口
GPIO_InitType.Pin=GPIO_PIN_12;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_13;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
//MISO 主机输入,从机输出
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_15;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
}
}#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include "UART.h"
#include "delay.h"
/* address define */
#define NO_OP 0x00
#define DIGIT0 0x01
#define DIGIT1 0x02
#define DIGIT2 0x03
#define DIGIT3 0x04
#define DIGIT4 0x05
#define DIGIT5 0x06
#define DIGIT6 0x07
#define DIGIT7 0x08
#define DECODE_MODE 0x09 //译码控制寄存器
#define INTENSITY 0x0A // 亮度控制寄存器
#define SCAN_LIMIT 0x0B //扫描界限寄存器
#define SHUT_DOWN 0x0C //关断模式寄存器
#define DISPLAY_TEST 0x0F //显示测试寄存器
/* mode define */
#define NORMAL_MODE 0
#define NO_DECODE_D0_D7 0x00
#define DECODE_D0_ONLY 0x01
#define DECODE_D0_D3_ONLY 0x0F
#define DECODE_D0_D7 0xFF
#define CS_ENABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
#define CS_DISABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
//功能:向MAX7219内部寄存器写入数据
//参数:addr、dat
void Write_Max7219(uint8_t addr,uint8_t dat)
{
CS_ENABLE_MAX7291;
HAL_SPI_Transmit(&SPI2_Handle,&addr,1,1000); //写入地址
delay_us(10);//必须,点阵不显示时可以延长此延时调试
HAL_SPI_Transmit(&SPI2_Handle,&dat,1,1000); //写入地址
delay_us(10);//必须,点阵不显示时可以延长此延时调试
CS_DISABLE_MAX7291;
}
//MAX7291初始化
void Init_MAX7219(void)
{
Write_Max7219(0x09, 0xFF); //译码方式:不译码
Write_Max7219(0x0a, 0x03); //亮度
Write_Max7219(0x0b, 0x07); //扫描界限;4个数码管显示
Write_Max7219(0x0c, 0x01); //掉电模式:0,普通模式:1
Write_Max7219(0x0f, 0x01); //显示测试:1;测试结束,正常显示:0
}
不译码的方法:
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi2_Init();
uint16_t leddata[]={
0x7e, //"0"
0x30, //"1"
0x6D, //"2"
0x79, //"3"
0x33, //"4"
0x5B, //"5"
0x5F, //"6"
0x70, //"7"
0x7F, //"8"
0x7B, //"9"
0x77, //"A"
0x7F, //"B"
0x4E, //"C"
0x7e, //"D"
0x4F, //"E"
0x47, //"F"
0x37, //"H"
0x00 //熄灭
};
delay_ms(50);
Init_MAX7219();
delay_ms(1000);
Write_Max7219(0x0f, 0x00); //显示测试:1;测试结束,正常显示:0
Write_Max7219(1,leddata[0]); //第一个数码管显示1
Write_Max7219(2,leddata[1]);
Write_Max7219(3,leddata[2]);
Write_Max7219(4,leddata[3]);
Write_Max7219(5,leddata[4]);
Write_Max7219(6,leddata[5]);
Write_Max7219(7,leddata[6]);
Write_Max7219(8,leddata[7]);
while (1)
{
for(uint16_t i=0;i<18;i++)
{
Write_Max7219(1,leddata[i]);
delay_ms(500);
}
}
}
#include "stm32f1xx_hal.h"
SPI_HandleTypeDef SPI2_Handle;
//MAX7219
void Spi2_Init()
{
SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_16; //分频 36/16=2
SPI2_Handle.Init.CLKPhase=SPI_PHASE_1EDGE; //第一个边沿捕获;CPHA
SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_LOW; //CPOL时钟极性 SPI_POLARITY_HIGH
SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI2_Handle.Init.Direction=SPI_DIRECTION_1LINE; //单线半双工
SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI2_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI2_Handle.Init.NSS=SPI_NSS_SOFT; //NSS软件模式
HAL_SPI_Init(&SPI2_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI2)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_SPI2_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
//片选
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 普通的IO口
GPIO_InitType.Pin=GPIO_PIN_12;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_13;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
//MISO 主机输入,从机输出
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_15;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
}
}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include "UART.h"
#include "delay.h"
/* address define */
#define NO_OP 0x00
#define DIGIT0 0x01
#define DIGIT1 0x02
#define DIGIT2 0x03
#define DIGIT3 0x04
#define DIGIT4 0x05
#define DIGIT5 0x06
#define DIGIT6 0x07
#define DIGIT7 0x08
#define DECODE_MODE 0x09 //译码控制寄存器
#define INTENSITY 0x0A // 亮度控制寄存器
#define SCAN_LIMIT 0x0B //扫描界限寄存器
#define SHUT_DOWN 0x0C //关断模式寄存器
#define DISPLAY_TEST 0x0F //显示测试寄存器
/* mode define */
#define NORMAL_MODE 0
#define NO_DECODE_D0_D7 0x00
#define DECODE_D0_ONLY 0x01
#define DECODE_D0_D3_ONLY 0x0F
#define DECODE_D0_D7 0xFF
#define CS_ENABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_RESET);
#define CS_DISABLE_MAX7291 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,GPIO_PIN_SET);
//功能:向MAX7219内部寄存器写入数据
//参数:addr、dat
void Write_Max7219(uint8_t addr,uint8_t dat)
{
CS_ENABLE_MAX7291;
HAL_SPI_Transmit(&SPI2_Handle,&addr,1,1000); //写入地址
delay_us(10);//必须,点阵不显示时可以延长此延时调试
HAL_SPI_Transmit(&SPI2_Handle,&dat,1,1000); //写入地址
delay_us(10);//必须,点阵不显示时可以延长此延时调试
CS_DISABLE_MAX7291;
}
//MAX7291初始化
void Init_MAX7219(void)
{
Write_Max7219(0x09, 0x00); //译码方式:不译码
Write_Max7219(0x0a, 0x03); //亮度
Write_Max7219(0x0b, 0x07); //扫描界限;4个数码管显示
Write_Max7219(0x0c, 0x01); //掉电模式:0,普通模式:1
Write_Max7219(0x0f, 0x01); //显示测试:1;测试结束,正常显示:0
}
C:中断方式--W25Q64
调用回调的时候,说明它的收(发)已经完成。
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "w25q64.h"
uint16_t i=0;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi1_Init();
W25q64_SectorErase(0);
W25q64_ReadID();
for(i=0;i<4096;i++)
{
wbuff[i]=0x66;
}
for(i=0;i<16;i++)
{
W25q64_WritePage(&wbuff[i*256],i);
}
W25q64_ReadData(rbuff,0,4096);
// for(i=0;i<4096;i++)
// {
// printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
// }
while (1)
{
}
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "w25q64.h"
uint8_t wbuff[4096];
uint8_t rbuff[4096];
SPI_HandleTypeDef SPI_Handle;
void Spi1_Init()
{
SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2; //分频
SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE; //第一个边沿捕获;CPHA
SPI_Handle.Init.CLKPolarity=SPI_POLARITY_LOW; //CPOL时钟极性
SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI_Handle.Init.NSS=SPI_NSS_SOFT; //NSS软件模式
HAL_SPI_Init(&SPI_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
HAL_NVIC_SetPriority(SPI1_IRQn,3,2);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 普通的IO口
GPIO_InitType.Pin=GPIO_PIN_0;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
//MISO 主机输入,从机输出
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
//MOSI 主机输出,从机输入
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_7;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
void SPI1_IRQHandler()
{
HAL_SPI_IRQHandler(&SPI_Handle);
}
//IT的API函数执行完毕,开启中断接收函数,但是不代表接收操作的完成。
//调用回调函数的时候,表示接收完毕。
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
for(uint16_t i=0;i<4096;i++)
{
CS_DISABLE;
printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
}
}
}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
#define CS_ENABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
#define CS_DISABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
/**
* @brief 查看busy位是否忙碌.
*/
void W25q64_Busy(void)
{
uint8_t cmd[2],data[2];
cmd[0]=0x05;
cmd[1]=0xFF;
do
{
CS_ENABLE;
HAL_SPI_TransmitReceive(&SPI_Handle,cmd,data,2,1000);
CS_DISABLE;
}
while((data[1] & 0x01)==0x01); //busy为0不忙;
}
/**
* @brief 写使能.
*/
void W25q64_WriteEnable(void)
{
uint8_t cmd;
cmd=0x06;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,&cmd,1,1000);
CS_DISABLE;
}
/**
* @brief 擦除一个扇区的数据(4K;4*1024=4096)
* @param 擦除第几页的数据
*/
void W25q64_SectorErase(uint32_t Sectornum)
{
uint8_t cmd[4];
cmd[0]=0x20;
cmd[1]=(Sectornum*4096)>>16;
cmd[2]=(Sectornum*4096)>>8;
cmd[3]=(Sectornum*4096)>>0;
W25q64_WriteEnable();
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
CS_DISABLE;
}
/**
* @brief 写入一页数据,一页256字节
* @param data: 写入的数据
* @param PageNum:给第几页写入数据
*/
void W25q64_WritePage(uint8_t * data,uint32_t PageNum)
{
uint8_t cmd[4];
cmd[0]=0x02;
cmd[1]=(PageNum*256)>>16;
cmd[2]=(PageNum*256)>>8;
cmd[3]=(PageNum*256)>>0;
W25q64_WriteEnable();
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
HAL_SPI_Transmit(&SPI_Handle,data,256,5000); //sizeof(data)
CS_DISABLE;
}
/**
* @brief 读取数据
* @param *rdata: 读取数据存放的地址
* @param addr: 读取第几页
* @param len: 读取数据的长度(几个字节)
* @retval
*/
void W25q64_ReadData(uint8_t *rdata,uint32_t addr ,uint32_t len)
{
uint8_t cmd[4];
cmd[0]=0x03;
cmd[1]=addr>>16;
cmd[2]=addr>>8;
cmd[3]=addr>>0;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
HAL_SPI_Receive_IT(&SPI_Handle,rdata,len);
//CS_DISABLE;
}
/**
* @brief 读取ID.
*/
void W25q64_ReadID()
{
uint8_t cmd[1],data_id[3];
cmd[0]=0x9F;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,1,1000);
HAL_SPI_Receive(&SPI_Handle,data_id,sizeof(data_id),50000);
CS_DISABLE;
printf("W25q64的ID为:制造商ID=%02X 设备ID=%02X 容量ID=%02X\r\n",
data_id[0], data_id[1], data_id[2]);
}
D:MDA----W25Q64
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "w25q64.h"
uint16_t i=0;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi1_Init();
W25q64_SectorErase(0);
W25q64_ReadID();
for(i=0;i<4096;i++)
{
wbuff[i]=0x44;
}
for(i=0;i<16;i++)
{
W25q64_WritePage(&wbuff[i*256],i);
}
W25q64_ReadData(rbuff,0,4096);
while (1)
{
}
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "w25q64.h"
uint8_t wbuff[4096];
uint8_t rbuff[4096];
SPI_HandleTypeDef SPI_Handle;
DMA_HandleTypeDef DMA_HandleTX;
DMA_HandleTypeDef DMA_HandleRX;
void Spi1_Init()
{
SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_2; //分频
SPI_Handle.Init.CLKPhase=SPI_PHASE_1EDGE; //第一个边沿捕获;CPHA
SPI_Handle.Init.CLKPolarity=SPI_POLARITY_LOW; //CPOL时钟极性
SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI_Handle.Init.NSS=SPI_NSS_SOFT; //NSS软件模式
HAL_SPI_Init(&SPI_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
// HAL_NVIC_SetPriority(SPI1_IRQn,3,2);
// HAL_NVIC_EnableIRQ(SPI1_IRQn);
//MDA通道6配置--发送数据
DMA_HandleTX.Instance=DMA1_Channel3;
//传输方向:内存(数组)--->外设(SPI的DR寄存器)
DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE; //存储区(数组)地址自增
DMA_HandleTX.Init.Mode=DMA_NORMAL;
DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增(SPI的DR寄存器)
DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM; //优先级
__HAL_LINKDMA(&SPI_Handle,hdmatx,DMA_HandleTX); //双向链接
//__HAL_LINKDMA(hspi,hdmatx,DMA_HandleTX); //双向链接
HAL_DMA_Init(&DMA_HandleTX);
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn,1,2);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
//MDA通道7配置---接收数据
DMA_HandleRX.Instance=DMA1_Channel2;
//传输方向:外设(SPI的DR寄存器) ---> 内存(数组)
DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE; //存储区地址自增
DMA_HandleRX.Init.Mode=DMA_NORMAL;
DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM; //优先级
__HAL_LINKDMA(&SPI_Handle,hdmarx,DMA_HandleRX); //双向链接
//__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX); //双向链接
HAL_DMA_Init(&DMA_HandleRX);
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn,1,2);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 普通的IO口
GPIO_InitType.Pin=GPIO_PIN_0;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_5;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
//MISO 主机输入,从机输出
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
//MOSI 主机输出,从机输入
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_7;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
//void SPI1_IRQHandler()
//{
// HAL_SPI_IRQHandler(&SPI_Handle);
//}
void DMA1_Channel2_IRQHandler()
{
HAL_DMA_IRQHandler(&DMA_HandleRX);
}
void DMA1_Channel3_IRQHandler()
{
HAL_DMA_IRQHandler(&DMA_HandleTX);
}
//DMA的API函数执行完毕,开启中断接收函数,但是不代表接收操作的完成。
//调用回调函数的时候,表示接收完毕。
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
for(uint16_t i=0;i<4096;i++)
{
CS_DISABLE;
printf("rbuff[%d]=%x\r\n",i,rbuff[i]);
}
}
}
#include "stm32f1xx_hal.h"
#include <stdarg.h>
#include "stdio.h"
#include "SPI.h"
#include <stdarg.h>
#include "stdio.h"
#include "UART.h"
#define CS_ENABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
#define CS_DISABLE HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
/**
* @brief 查看busy位是否忙碌.
*/
void W25q64_Busy(void)
{
uint8_t cmd[2],data[2];
cmd[0]=0x05;
cmd[1]=0xFF;
do
{
CS_ENABLE;
HAL_SPI_TransmitReceive(&SPI_Handle,cmd,data,2,1000);
CS_DISABLE;
}
while((data[1] & 0x01)==0x01); //busy为0不忙;
}
/**
* @brief 写使能.
*/
void W25q64_WriteEnable(void)
{
uint8_t cmd;
cmd=0x06;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,&cmd,1,1000);
CS_DISABLE;
}
/**
* @brief 擦除一个扇区的数据(4K;4*1024=4096)
* @param 擦除第几页的数据
*/
void W25q64_SectorErase(uint32_t Sectornum)
{
uint8_t cmd[4];
cmd[0]=0x20;
cmd[1]=(Sectornum*4096)>>16;
cmd[2]=(Sectornum*4096)>>8;
cmd[3]=(Sectornum*4096)>>0;
W25q64_WriteEnable();
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
CS_DISABLE;
}
/**
* @brief 写入一页数据,一页256字节
* @param data: 写入的数据
* @param PageNum:给第几页写入数据
*/
void W25q64_WritePage(uint8_t * data,uint32_t PageNum)
{
uint8_t cmd[4];
cmd[0]=0x02;
cmd[1]=(PageNum*256)>>16;
cmd[2]=(PageNum*256)>>8;
cmd[3]=(PageNum*256)>>0;
W25q64_WriteEnable();
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
HAL_SPI_Transmit(&SPI_Handle,data,256,5000); //sizeof(data)
CS_DISABLE;
}
/**
* @brief 读取数据
* @param *rdata: 读取数据存放的地址
* @param addr: 读取第几页
* @param len: 读取数据的长度(几个字节)
* @retval
*/
void W25q64_ReadData(uint8_t *rdata,uint32_t addr ,uint32_t len)
{
uint8_t cmd[4];
cmd[0]=0x03;
cmd[1]=addr>>16;
cmd[2]=addr>>8;
cmd[3]=addr>>0;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,4,1000);
HAL_SPI_Receive_DMA(&SPI_Handle,rdata,len);
//CS_DISABLE;
}
/**
* @brief 读取ID.
*/
void W25q64_ReadID()
{
uint8_t cmd[1],data_id[3];
cmd[0]=0x9F;
W25q64_Busy();
CS_ENABLE;
HAL_SPI_Transmit(&SPI_Handle,cmd,1,1000);
HAL_SPI_Receive(&SPI_Handle,data_id,sizeof(data_id),50000);
CS_DISABLE;
printf("W25q64的ID为:制造商ID=%02X 设备ID=%02X 容量ID=%02X\r\n",
data_id[0], data_id[1], data_id[2]);
}
主从通信
A:中断+主机身份不改变
发现问题:2个板子的SPI通信正常,主机可以正常给从机发送数据,但是从机给主机发送数据的数据顺序会乱。
解决:换线,一定要共电源和共地!!!!!!!!!!!!!!!!!!(下面所有的主从通信必须遵守这个)
主机代码:
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi1_Init();
while (1)
{
if(KEY_Scan())
{
printf("PA0按下,主机发送数据\r\n");
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
}
}
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
SPI_HandleTypeDef SPI_Handle;
uint8_t wbuff[10]={10,11,12,13,14,15,16,17,18,19};
uint8_t rbuff[10];
void Spi1_Init()
{
//主机
SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_128; //分频 72/256=0.60MHZ
SPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE; //第二个边沿捕获;CPHA
SPI_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH; //CPOL时钟极性
SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI_Handle.Init.NSS=SPI_NSS_SOFT; //NSS软件模式
HAL_SPI_Init(&SPI_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
HAL_NVIC_SetPriority(SPI1_IRQn,3,1);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 普通的IO口
GPIO_InitType.Pin=GPIO_PIN_4;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_5|GPIO_PIN_7;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
void SPI1_IRQHandler()
{
HAL_SPI_IRQHandler(&SPI_Handle);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
for(uint8_t i=0;i<10;i++)
{
printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
}
}
}
从机代码:
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi2_Init();
HAL_SPI_TransmitReceive_IT(&SPI2_Handle,w_buff,r_buff,10);
while (1)
{
}
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
SPI_HandleTypeDef SPI2_Handle;
uint8_t w_buff[10]={0,1,2,3,4,5,6,7,8,9};
uint8_t r_buff[10];
void Spi2_Init()
{
//从机
SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256; //分频 36/256=0.14
SPI2_Handle.Init.CLKPhase=SPI_PHASE_2EDGE; //第二个边沿捕获;CPHA
SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH; //CPOL时钟极性
SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI2_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI2_Handle.Init.Mode=SPI_MODE_SLAVE; //从机模式
SPI2_Handle.Init.NSS=SPI_NSS_HARD_INPUT; //NSS硬件模式
HAL_SPI_Init(&SPI2_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI2)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_SPI2_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
HAL_NVIC_SetPriority(SPI2_IRQn,3,1);
HAL_NVIC_EnableIRQ(SPI2_IRQn);
// //片选
// GPIO_InitType.Mode=GPIO_MODE_INPUT; //输入 普通的IO口
// GPIO_InitType.Pin=GPIO_PIN_12;
// GPIO_InitType.Pull=GPIO_NOPULL;
// HAL_GPIO_Init(GPIOB,&GPIO_InitType);
// 配置为 SPI2_Handle.Init.NSS=SPI_NSS_HARD_INPUT; //NSS硬件模式
// 从机片选不用IO口的配置
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_14;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
//MISO 主机输入,从机输出
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_13|GPIO_PIN_15;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
}
}
void SPI2_IRQHandler()
{
HAL_SPI_IRQHandler(&SPI2_Handle);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI2)
{
for(uint8_t i=0;i<10;i++)
{
printf("rbuff[%d]=%d\r\n",i,r_buff[i]);
}
HAL_SPI_TransmitReceive_IT(&SPI2_Handle,w_buff,r_buff,10);
}
}
B:中断+DMA
主机代码和上面的A:中断+主机身份不改变一样;只是把从机改为DMA模式
从机代码:
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi2_Init();
HAL_SPI_TransmitReceive_DMA(&SPI2_Handle,wbuff,rbuff,10);
printf("从机串口正常\r\n");
while (1)
{
}
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
SPI_HandleTypeDef SPI2_Handle;
DMA_HandleTypeDef DMA_HandleTX;
DMA_HandleTypeDef DMA_HandleRX;
uint8_t wbuff[10]={10,11,12,13,14,15,16,17,18,19};
uint8_t rbuff[10];
void Spi2_Init()
{
//从机
SPI2_Handle.Instance=SPI2;//SPI2挂载在APB1总线上,APB2总线=36MHZ
SPI2_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_128; //分频 36/128=0.28MHZ
SPI2_Handle.Init.CLKPhase=SPI_PHASE_2EDGE; //第二个边沿捕获;CPHA
SPI2_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH; //CPOL时钟极性
SPI2_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI2_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI2_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI2_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI2_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI2_Handle.Init.Mode=SPI_MODE_SLAVE; //从机模式
SPI2_Handle.Init.NSS=SPI_NSS_HARD_INPUT; //NSS输入
HAL_SPI_Init(&SPI2_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI2)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
//MDA通道5配置--发送数据
DMA_HandleTX.Instance=DMA1_Channel5;
//传输方向:内存(数组)--->外设(SPI的DR寄存器)
DMA_HandleTX.Init.Direction=DMA_MEMORY_TO_PERIPH;
DMA_HandleTX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
DMA_HandleTX.Init.MemInc=DMA_MINC_ENABLE; //存储区(数组)地址自增
DMA_HandleTX.Init.Mode=DMA_NORMAL;
DMA_HandleTX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
DMA_HandleTX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增(SPI的DR寄存器)
DMA_HandleTX.Init.Priority=DMA_PRIORITY_MEDIUM; //优先级
__HAL_LINKDMA(&SPI2_Handle,hdmatx,DMA_HandleTX); //双向链接
//__HAL_LINKDMA(hspi,hdmatx,DMA_HandleTX); //双向链接
HAL_DMA_Init(&DMA_HandleTX);
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn,1,2);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
//MDA通道4配置---接收数据
DMA_HandleRX.Instance=DMA1_Channel4;
//传输方向:外设(SPI的DR寄存器) ---> 内存(数组)
DMA_HandleRX.Init.Direction=DMA_PERIPH_TO_MEMORY;
DMA_HandleRX.Init.MemDataAlignment=DMA_MDATAALIGN_BYTE; //内存数据宽度
DMA_HandleRX.Init.MemInc=DMA_MINC_ENABLE; //存储区地址自增
DMA_HandleRX.Init.Mode=DMA_NORMAL;
DMA_HandleRX.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE;//外设数据宽度
DMA_HandleRX.Init.PeriphInc=DMA_PINC_DISABLE;//外设地址不自增
DMA_HandleRX.Init.Priority=DMA_PRIORITY_MEDIUM; //优先级
__HAL_LINKDMA(&SPI2_Handle,hdmarx,DMA_HandleRX); //双向链接
//__HAL_LINKDMA(hi2c,hdmarx,DMA_HandleRX); //双向链接
HAL_DMA_Init(&DMA_HandleRX);
HAL_NVIC_SetPriority(DMA1_Channel4_IRQn,1,2);
HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_14;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
//MISO 主机输入,从机输出
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_13|GPIO_PIN_15;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOB,&GPIO_InitType);
}
}
void DMA1_Channel4_IRQHandler()
{
HAL_DMA_IRQHandler(&DMA_HandleRX);
}
void DMA1_Channel5_IRQHandler()
{
HAL_DMA_IRQHandler(&DMA_HandleTX);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI2)
{
for(uint8_t i=0;i<10;i++)
{
printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
}
HAL_SPI_TransmitReceive_DMA(&SPI2_Handle,wbuff,rbuff,10);
}
}
C:多主机通信--主从身份可以改变
PA3控制对方的PA4;PA4(CS片选)
主机和从机可以使用这一个代码,只需要把里面发送的数据改了就ok了,其他代码部分都不需要改。
把对方拉为从机: 走的流程
while (1)
{
if(KEY_Scan())
{
printf("PA0按下,把对方拉为从机,发送数据\r\n");
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
HAL_Delay(20);
HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
}
}
被对面拉低为从机:被拉低产生MODE错误,进入
//主机配拉低,被拉低产生MODE错误,进入这个中断回调函数
//在这个错误中断回调函数中把它变为从机
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
if(hspi->ErrorCode==HAL_SPI_ERROR_MODF)
{
printf("被拉成从机\r\n");
HAL_SPI_DeInit(&SPI_Handle); //清除以前的配置
Spi1_SInit(); //重新配置为从机
HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
}
}
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
for(uint8_t i=0;i<10;i++)
{
printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
}
Spi1_MInit(); //被对方拉低接收数据,接收完毕在变为主模式,以便下一次使用
}
}
__HAL_SPI_ENABLE(&SPI_Handle);
__HAL_SPI_ENABLE_IT(&SPI_Handle,SPI_IT_ERR); 手动打开错误中断,
被拉为从机的时候发送这个错误,调用错误中断回调函数
__HAL_SPI_ENABLE(&SPI_Handle):这个是在接收和发送函数内部的HAL帮助我们调用的。我们现在都是主机不能调用收发函数,(在一主一从的时候才可以正常通信,两个都为主机时没有办法通信。)我们需要自己手动打开SPI的接口。
代码:
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
#include "key.h"
#include "SPI.h"
#include "MAX7219.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
LED_Init(); /* LED初始化 */
Uart_Init(115200);
LED_Exit_Init();
KEY_Init();
Spi1_MInit();
while (1)
{
if(KEY_Scan())
{
printf("PA0按下,把对方拉为从机,发送数据\r\n");
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);
HAL_Delay(20);
HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
}
}
}
#include "stm32f1xx_hal.h"
#include "UART.h"
#include <stdarg.h>
#include "stdio.h"
SPI_HandleTypeDef SPI_Handle;
uint8_t wbuff[10]={1,2,3,4,5,6,7,8,9,10};
uint8_t rbuff[10];
void Spi1_MInit()
{
//主机
SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256; //分频 72/256=0.28MHZ
SPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE; //第二个边沿捕获;CPHA
SPI_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH; //CPOL时钟极性
SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI_Handle.Init.Mode=SPI_MODE_MASTER; //主机模式
SPI_Handle.Init.NSS=SPI_NSS_HARD_INPUT; //NSS硬件输入
HAL_SPI_Init(&SPI_Handle);
__HAL_SPI_ENABLE(&SPI_Handle);
__HAL_SPI_ENABLE_IT(&SPI_Handle,SPI_IT_ERR); //手动打开错误中断
}
void Spi1_SInit()
{
//从机
SPI_Handle.Instance=SPI1;//SPI1挂载在APB2总线上,APB2总线=72MHZ
SPI_Handle.Init.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_256; //分频 72/256=0.28MHZ
SPI_Handle.Init.CLKPhase=SPI_PHASE_2EDGE; //第二个边沿捕获;CPHA
SPI_Handle.Init.CLKPolarity=SPI_POLARITY_HIGH; //CPOL时钟极性
SPI_Handle.Init.CRCCalculation=SPI_CRCCALCULATION_DISABLE; //是否开启CRC校验(硬件自动校验),主要看操作的从机使用否支持CRC校验
SPI_Handle.Init.DataSize=SPI_DATASIZE_8BIT;
SPI_Handle.Init.Direction=SPI_DIRECTION_2LINES; //双线全双工
SPI_Handle.Init.FirstBit=SPI_FIRSTBIT_MSB; //高位先行
SPI_Handle.Init.TIMode=SPI_TIMODE_DISABLE; //不支持TI模式
SPI_Handle.Init.Mode=SPI_MODE_SLAVE; //从机模式
SPI_Handle.Init.NSS=SPI_NSS_HARD_INPUT; //硬件输入
HAL_SPI_Init(&SPI_Handle);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SPI1_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitType;
HAL_NVIC_SetPriority(SPI1_IRQn,3,1);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
GPIO_InitType.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出 普通的IO口
GPIO_InitType.Pin=GPIO_PIN_3;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
if(hspi->Init.Mode==SPI_MODE_MASTER)
{
//主机
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_5|GPIO_PIN_7;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
else
{
//从机
GPIO_InitType.Mode=GPIO_MODE_AF_INPUT; //复用输入 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_5|GPIO_PIN_7;
GPIO_InitType.Speed=GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
GPIO_InitType.Mode=GPIO_MODE_AF_PP; //复用推挽输出 复用为SPI
GPIO_InitType.Pin=GPIO_PIN_6;
GPIO_InitType.Pull=GPIO_NOPULL;
HAL_GPIO_Init(GPIOA,&GPIO_InitType);
}
}
}
void SPI1_IRQHandler()
{
HAL_SPI_IRQHandler(&SPI_Handle);
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);
for(uint8_t i=0;i<10;i++)
{
printf("rbuff[%d]=%d\r\n",i,rbuff[i]);
}
Spi1_MInit(); //被对方拉低接收数据,接收完毕在变为主模式,以便下一次使用
}
}
//主机配拉低,被拉低产生MODE错误,进入这个中断回调函数
//在这个错误中断回调函数中把它变为从机
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance==SPI1)
{
if(hspi->ErrorCode==HAL_SPI_ERROR_MODF)
{
printf("被拉成从机\r\n");
HAL_SPI_DeInit(&SPI_Handle); //清除以前的配置
Spi1_SInit(); //重新配置为从机
HAL_SPI_TransmitReceive_IT(&SPI_Handle,wbuff,rbuff,10);
}
}
}