![STM32的ADC实验中,采用双通道采集50次,定义了一个变量__IO uint16_t ADC_ConvertedValue[50][2];,第1张 STM32的ADC实验中,采用双通道采集50次,定义了一个变量__IO uint16_t ADC_ConvertedValue[50][2];,第1张](/aiimages/STM32%E7%9A%84ADC%E5%AE%9E%E9%AA%8C%E4%B8%AD%EF%BC%8C%E9%87%87%E7%94%A8%E5%8F%8C%E9%80%9A%E9%81%93%E9%87%87%E9%9B%8650%E6%AC%A1%EF%BC%8C%E5%AE%9A%E4%B9%89%E4%BA%86%E4%B8%80%E4%B8%AA%E5%8F%98%E9%87%8F__IO+uint16_t+ADC_ConvertedValue%5B50%5D%5B2%5D%3B.png)
DMA_BufferSize = 100; //传送内存大小,100个16位
DMA_Mode = DMA_Mode_Circular; //循环本实验采用W25Q64芯片
W25Q64是华邦公司推出的大容量SPI
FLASH产品,其容量为64Mb。该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块,每个块大小为64K字节,每个块又分为16个扇区,每个扇区4K个字节。W25Q64的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。所以,这需要给W25Q64开辟一个至少4K的缓存区,这样必须要求芯片有4K以上的SRAM才能有很好的 *** 作。
W25Q64的擦写周期多达10W次,可将数据保存达20年之久,支持27~36V的电压,支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可达80Mhz。
一。SPI接口原理
(一)概述
高速,全双工,同步的通信总线。
全双工:可以同时发送和接收,需要2条引脚
同步: 需要时钟引脚
片选引脚:方便一个SPI接口上可以挂多个设备。
总共四根引脚。
(二)SPI内部结构简明图
MISO: 做主机的时候输入,做从机的时候输出
MOSI:做主机的时候输出,做从机的时候输入
主机和从机都有一个移位寄存器,在同一个时钟的控制下主机的最高位移到从机的最高位,同时从机的最高位往前移一位,移到主机的最低位。在一个时钟的控制下主机和从机进行了一个位的交换,那么在8个时钟的控制下就交换了8位,最后的结果就是两个移位寄存器的数据完全交换。
在8个时钟的控制下,主机和从机的两个字节进行了交换,也就是说主机给从机发送一个字节8个位的同时,从机也给主机传回来了8个位,也就是一个字节。
(三)SPI接口框图
上面左边部分就是在时钟控制下怎么传输数据,右边是控制单元,还包括左下的波特率发生器。
(四)SPI工作原理总结
(五)SPI的特征
(六)从选择(NSS)脚管理
两个SPI通信首先有2个数据线,一个时钟线,还有一个片选线,只有把片选拉低,SPI芯片才工作,片选引脚可以是SPI规定的片选引脚,还可以通过软件的方式选择任意一个IO口作为片选引脚,这样做的好处是:比如一个SPI接口上挂多个设备,比如挂了4个设备,第二个用PA2,第三个用PA3,第四个用PA4作为片选,我们
跟第二个设备进行通信的时候,只需要把第二个片选选中,比如拉低,其他设备的片选都拉高,这样就实现了一个SPI接口可以连接个SPI设备,战舰开发板上就是通过这种方法来实现的。
(七)时钟信号的相位和极性
时钟信号的相位和极性是通过CR寄存器的 CPOL 和 CPHA两个位确定的。
CPOL:时钟极性,设置在没有数据传输时时钟的空闲状态电平。CPOL置0,SCK引脚在空闲时为低电平,CPOL置1,SCK引脚在空闲时保持高电平。
CPHA:时钟相位 设置时钟信号在第几个边沿数据被采集
CPHA=1时:在时钟信号的第二个边沿
CPOL=1,CPHA=1,
CPOL=1表示时钟信号在没有数据传输时即空闲时的状态为高电平。如果CPHA=1,那么数据就在时钟信号的第二个边沿即上升沿的时候被采集。
CPOL= 0,CPHA=1, CPOL=0表示时钟信号在没有数据传输时即空闲时的状态为低电平。
如果CPHA=1,那么数据就在时钟信号的第二个边沿即下降沿的时候被采集。
CPHA=0时:在时钟信号的第一个边沿
CPOL=1,CPHA=0,
CPOL=1表示时钟信号在没有数据传输时即空闲时的状态为高电平。如果CPHA=1,那么数据就在时钟信号的第一个边沿即下降沿的时候被采集。
CPOL= 0,CPHA=0, CPOL=0表示时钟信号在没有数据传输时即空闲时的状态为低电平。
如果CPHA=1,那么数据就在时钟信号的第一个边沿即上升沿的时候被采集。
为什么要配置这两个参数
因为SPI外设的从机的时钟相位和极性都是有严格要求的。所以我们要根据选择的外设的时钟相位和极性来配置主机的相位和极性。必须要与从机匹配。
(八)数据帧的格式和状态标志
数据帧格式:根据CR1寄存器的LSBFIRST位的设置,数据可以MSB在前也可以LSB在前。
根据CR1寄存器的DEF位,每个数据帧可以是8位或16位。
(九)SPI中断
(十)SPI引脚配置 (3个SPI)
引脚的工作模式设置
引脚必须要按照这个表格配置。
二。SPI寄存器库函数配置
(一)常用寄存器
(二)SPI相关库函数
STM32的SPI接口可以配置为支持SPI协议或者支持I2S音频协议。默认是SPI模式,可以通过软件切换到I2S方式。
常用的函数:
1 void SPI_Init(SPI_TypeDef SPIx, SPI_InitTypeDef
SPI_InitStruct);//SPI的初始化
2 void SPI_Cmd(SPI_TypeDef SPIx, FunctionalState NewState); //SPI使能
3 void SPI_I2S_ITConfig(SPI_TypeDef SPIx, uint8_t SPI_I2S_IT,
FunctionalState NewState); //开启中断
4 void SPI_I2S_DMACmd(SPI_TypeDef SPIx, uint16_t SPI_I2S_DMAReq,
FunctionalState NewState);//通 过DMA传输数据
5 void SPI_I2S_SendData(SPI_TypeDef SPIx, uint16_t Data); //发送数据
6 uint16_t SPI_I2S_ReceiveData(SPI_TypeDef SPIx); //接收数据
7 void SPI_DataSizeConfig(SPI_TypeDef SPIx, uint16_t SPI_DataSize);
//设置数据是8位还是16位
8 其他几个状态函数
void SPI_Init(SPI_TypeDef SPIx, SPI_InitTypeDef
SPI_InitStruct);//SPI的初始化
结构体成员变量比较多,这里我们挑取几个重要的成员变量讲解一下:
第一个参数 SPI_Direction 是用来设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式,这里我们选择全双工模式
SPI_Direction_2Lines_FullDuplex。
第二个参数 SPI_Mode 用来设置 SPI 的主从模式,这里我们设置为主机模式 SPI_Mode_Master,当然有需要你也可以选择为从机模式
SPI_Mode_Slave。
第三个参数 SPI_DataSiz 为 8 位还是 16 位帧格式选择项,这里我们是 8 位传输,选择SPI_DataSize_8b。
第四个参数 SPI_CPOL 用来设置时钟极性,我们设置串行同步时钟的空闲状态为高电平所以我们选择 SPI_CPOL_High。
第五个参数 SPI_CPHA
用来设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集,这里我们选择第二个跳变沿,所以选择
SPI_CPHA_2Edge
第六个参数 SPI_NSS 设置 NSS 信号由硬件(NSS 管脚)还是软件控制,这里我们通过软件控
制 NSS 关键,而不是硬件自动控制,所以选择 SPI_NSS_Soft。
第七个参数 SPI_BaudRatePrescaler 很关键,就是设置 SPI 波特率预分频值也就是决定 SPI 的时
钟的参数 , 从不分频道 256 分频 8 个可选值,初始化的时候我们选择 256 分频值
SPI_BaudRatePrescaler_256, 传输速度为 36M/256=140625KHz。
第八个参数 SPI_FirstBit 设置数据传输顺序是 MSB 位在前还是 LSB 位在前, ,这里我们选择
SPI_FirstBit_MSB 高位在前。
第九个参数 SPI_CRCPolynomial 是用来设置 CRC 校验多项式,提高通信可靠性,大于 1 即可。
设置好上面 9 个参数,我们就可以初始化 SPI 外设了。
初始化的范例格式为:
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructureSPI_Direction = SPI_Direction_2Lines_FullDuplex;
//双线双向全双工
SPI_InitStructureSPI_Mode = SPI_Mode_Master; //主 SPI
SPI_InitStructureSPI_DataSize = SPI_DataSize_8b; // SPI 发送接收 8 位帧结构
SPI_InitStructureSPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平
371
SPI_InitStructureSPI_CPHA = SPI_CPHA_2Edge;//第二个跳变沿数据被采样
SPI_InitStructureSPI_NSS = SPI_NSS_Soft; //NSS 信号由软件控制
SPI_InitStructureSPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频
256
SPI_InitStructureSPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
SPI_InitStructureSPI_CRCPolynomial = 7; //CRC 值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器
(三)程序配置步骤
三。W25Qxx配置讲解
(一)电路图
片选用的PB12
W25Q64 是华邦公司推出的大容量SPI FLASH 产品,W25Q64 的容量为 64Mb,该系列还有 W25Q80/16/32
等。ALIENTEK 所选择的 W25Q64 容量为 64Mb,也就是 8M 字节。(1M=1024K)
W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K
个字节。W25Q64 的最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。这样我们需要给 W25Q64 开辟一个至少 4K 的缓存区,这样对 SRAM
要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的 *** 作。
W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 27~36V,W25Q64 支持标准的
SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出时相当于 160Mhz,四输出时相当于 320M),更多的 W25Q64
的介绍,请参考 W25Q64 的DATASHEET。
在往一个地址写数据之前,要先把这个扇区的数据全部读出来保存在缓存里,然后再把这个扇区擦除,然后在缓存中修改要写的数据,然后再把整个缓存中的数据再重新写入刚才擦除的扇区中。
便于学习和参考再给大家分享些spi 的资料
stm32之SPI通信
>单片机可以作为物联网的终端,用于数据的采集和实现简单的控制
其中8051是一款经典单片机,具有通过编程开实现控制电路的功能,广泛应用于电子产品的电路板上面,例如多功能压力锅,多功能风扇等领域。
具体情况具体对待,你可以看我下面的实验对号入座来分析你的问题:
实验目的:
当按键按下时,让PF10引脚的LED灯亮,
当按键再次按下时,让PF10引脚的LED灯灭;
无论按下与否,PF9引脚的LED灯循环闪烁;
实验步骤:
实验程序:
[cpp] view plain copy
/ledc/
#include "stm32f4xxh" //在SYSTEM目录下可以找到
#include "sysh"
void LED_Init(void){
RCC->AHB1ENR = 1<<5; //使能GPIO端口的F时钟
GPIO_Set(GPIOF,PIN9|PIN10,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_25M,GPIO_PUPD_PU);
PFout(9) = 1;
PFout(10) = 1;
}
[cpp] view plain copy
/ledh/
#ifndef _LED_H
#define _LED_H
void LED_Init(void);
#endif
[cpp] view plain copy
/keyc/
#include "sysh"
void Key_Init(void){
RCC->AHB1ENR|=1<<4; //使能PORTE时钟
//void GPIO_Set(GPIO_TypeDef GPIOx,u32 BITx,u32 MODE,u32 OTYPE,u32 OSPEED,u32 PUPD);//GPIO设置函数
GPIO_Set(GPIOE,PIN3,GPIO_MODE_IN,0,0,GPIO_PUPD_PU); //PE3设置上拉输入,这样的话,
//当按键没有按下时,默认电平为高;
}
[cpp] view plain copy
/keyh/
#ifndef _KEY_H
#define _KEY_H
void Key_Init(void);
#endif
[cpp] view plain copy
/extic/
#include "sysh"
#include "delayh"
#include "stm32f4xxh"
/
本示例的作用就是,
当按键按下时,蜂鸣器发出声音,
当按键再次按下时,蜂鸣器静音;
/
/
中断初始化函数:
主要是关于寄存器的相关配置
/
void EXTI3_Init(void){
//方法一:
RCC->APB2ENR |= 1 << 14; //开启SYSCFG时钟
SYSCFG->EXTICR[0] |= 0x4 << 12;//设置IO口与中断线的映射关系;
EXTI->IMR |= 1 << 3; //开启对应中断线上的中断
EXTI->FTSR |= 1 << 3; //设置中断触发条件
//SCB和NVIC,可参考STM32F3与STM32F4系列Cortex M4内核编程手册pdf
SCB->AIRCR |= 0x5 << 8; //设置分组
NVIC->IP[9] |= 0; //设置优先级,具体可分析MY_NVIC_Init()函数;
NVIC->ISER[0] |= 1 << 9; //使能中断;
//方法二:
/
使用SYSTEM目录下提供的API来实现,
具体可参考正点原子示例
/
}
void EXTI3_IRQHandler(void){
/
此按键,在按键按下时,处理不是很到位,
有待进一步改进,主要是在连按那一个环节。
/
delay_ms(20); //消抖
if(PEin(3) == 0){
PFout(10) = !PFout(10);
}
/
在中断里边最后记得清中断:
/
EXTI->PR |= 1 << 3;
}
[cpp] view plain copy
/extih/
#ifndef _EXTI_H
#define _EXTI_H
void EXTI3_Init(void);
#endif
[cpp] view plain copy
/testc/
#include "sysh"
#include "delayh"
#include "keyh"
#include "beeph"
#include "extih"
#include "ledh"
//int i = 0;
int main(void){
Stm32_Clock_Init(336,8,2,7);//设置时钟,168Mhz
delay_init(168); //初始化延时函数
Beep_Init();
Key_Init();
EXTI3_Init();
LED_Init();
while(1){
PFout(9) = 0;
delay_ms(500);
PFout(9) = 1;
delay_ms(500);
}
}
实验分析:
我们主要分析一下extic中的寄存器设置的这几个步骤:
1 RCC->APB2ENR |= 1 << 14;
这一步的作用就是使能SYSCFG时钟,
在使用外部中断的时候一定要先使能SYSCFG时钟;
2 SYSCFG->EXTICR[0] |= 0x4 << 12;
这一步的作用就是设置IO口与中断线的映射关系;
那么问题来了,我如何知道的我的IO口与哪根中断线是关联起来的呢?
而我们是通过KEY1按键,对应的IO口就是PE3,所以由上图的映射关系,我们知道,我们应该选择中断线3与之对应;
在官方提供的头文件stm32f4xxh中,我们可以看到:
[cpp] view plain copy
typedef struct
{
__IO uint32_t MEMRMP; /!< SYSCFG memory remap register, Address offset: 0x00 /
__IO uint32_t PMC; /!< SYSCFG peripheral mode configuration register, Address offset: 0x04 /
__IO uint32_t EXTICR[4]; /!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 /
uint32_t RESERVED[2]; /!< Reserved, 0x18-0x1C /
[cpp] view plain copy
__IO uint32_t CMPCR; /!< SYSCFG Compensation cell control register, Address offset: 0x20 /
SYSCFG_TypeDef;
结合上述三幅图,我们可以得知:
由于PE3对应的中断线为EXTI3,所以,我们我们这里仅需配置EXTI3,而EXTI3是在SYSCFG_EXTICR1中的;
所以我们仅需配置SYSCFG_EXTICR1寄存器的12位-15位为0100,而SYSCFG_EXTICR1寄存器在配置文件中,
对应的是SYSCFG->EXTICR[0],所以我们就写成了SYSCFG->EXTICR[0] |= 0x4 << 12;
3 EXTI->IMR |= 1 << 3;
这条语句的作用就是:开启对应中断线上的中断
由于我们 *** 作的中断线是EXTI3,而IMR寄存器各位解释如下:
所以对应的,我们 *** 作EXTI_IMR寄存器的第3位MR3即可;
所以这条语句就写成了:EXTI->IMR |= 1 << 3
4 EXTI->FTSR |= 1 << 3;
这条语句的作用就是设置中断触发条件;
在我的开发板中,当按键按下时,其端口就会变成低电平,在没有按下时,其是为高电平的;
因为我们keyc中,将按键的引脚设置成了上拉;所以在这里,我得将其设置成下降沿触发;
与此同时,查看EXTI_FTSR寄存器,可以看到:
又由于我们这条中断线是中断线3,所以这条语句就写成了:EXTI->FTSR |= 1 << 3
5 SCB->AIRCR |= 0x5 << 8;
这条语句的作用就是:设置分组;
所以,在这里我们只需设置SCB的AIRCR的 bit10-8即可;查看SCB的结构体,得知:
[cpp] view plain copy
typedef struct
{
__I uint32_t CPUID; /!< Offset: 0x000 (R/ ) CPUID Base Register /
__IO uint32_t ICSR; /!< Offset: 0x004 (R/W) Interrupt Control and State Register /
__IO uint32_t VTOR; /!< Offset: 0x008 (R/W) Vector Table Offset Register /
__IO uint32_t AIRCR; /!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register /
__IO uint32_t SCR; /!< Offset: 0x010 (R/W) System Control Register /
__IO uint32_t CCR; /!< Offset: 0x014 (R/W) Configuration Control Register /
__IO uint8_t SHP[12]; /!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) /
__IO uint32_t SHCSR; /!< Offset: 0x024 (R/W) System Handler Control and State Register /
__IO uint32_t CFSR; /!< Offset: 0x028 (R/W) Configurable Fault Status Register /
__IO uint32_t HFSR; /!< Offset: 0x02C (R/W) HardFault Status Register /
__IO uint32_t DFSR; /!< Offset: 0x030 (R/W) Debug Fault Status Register /
__IO uint32_t MMFAR; /!< Offset: 0x034 (R/W) MemManage Fault Address Register /
__IO uint32_t BFAR; /!< Offset: 0x038 (R/W) BusFault Address Register /
__IO uint32_t AFSR; /!< Offset: 0x03C (R/W) Auxiliary Fault Status Register /
__I uint32_t PFR[2]; /!< Offset: 0x040 (R/ ) Processor Feature Register /
__I uint32_t DFR; /!< Offset: 0x048 (R/ ) Debug Feature Register /
__I uint32_t ADR; /!< Offset: 0x04C (R/ ) Auxiliary Feature Register /
__I uint32_t MMFR[4]; /!< Offset: 0x050 (R/ ) Memory Model Feature Register /
__I uint32_t ISAR[5]; /!< Offset: 0x060 (R/ ) Instruction Set Attributes Register /
uint32_t RESERVED0[5];
__IO uint32_t CPACR; /!< Offset: 0x088 (R/W) Coprocessor Access Control Register /
} SCB_Type;
所以,在这里,我们把这条语句写成了SCB->AIRCR |= 0x5 << 8;
亦即设置成了101,也就是抢占优先级占2位,响应优先级占2位;
注:抢占优先级和响应优先级一样,其值越低则表示其优先级越高;
上述说的子优先级也就是我们说的响应优先级;
6 NVIC->IP[9] |= 0;
有上条语句,我们可以得知:IP寄存器由240个8bit的寄存器组成,每个可屏蔽中断占用8bit,这样总共可以表示240个可屏蔽中断,
而STM32F4只用到了其中的82个。IP[81]~IP[0]分别对应中断81~0而每个可屏蔽中断占用的8bit并没有全部使用,而是只用了高4位;
这4位,又分为抢占优先级和响应优先级;抢占优先级在前,响应优先级在后;也就是说,抢占优先级在高位,响应优先级在低位;
而我们又知道:我们这个中断是外部中断3,所以查看中断向量表可知:
由此可知,EXTI3在中断的位置为9,所以我们只需要设置NVIC->IP[9] 即可;
在这里我们把NVIC->IP[9] |= 0;则表示,我们设置外部中断3的抢占优先级为0,响应优先级也为0,其各占2位;
7 NVIC->ISER[0] |= 1 << 9;
这一步的作用就是使能中断;
ISER是一个中断使能寄存器组;这里用8个32位寄存器来控制,每个位控制一个中断;但是STM32F4的可屏蔽中断
最多只有82个,所以对我们来说,有用的就是三个(ISER[0~2]),总共可以表示96个中断;而STM32F4只用了其中的
前82个中断,ISER[0]的0bit~31分别对应中断0~31;ISER[1]的bit0~32对应中断32~63;ISER[2]的bit0~32对应中断64~81;
在这里,我们知道:我们的EXTI3对应的中断的位置是9,所以我们只需设置ISER[0]的第9位即可;所以我们在这里就将这条
语句写成了:NVIC->ISER[0] |= 1 << 9;
8至于外部中断函数的名称如何编写,我们可以从启动文件中去找到;
当我们设置的外部中断函数与启动文件中定义的名称一致时,
那么当这个中断条件满足时,就会去中断函数里边执行其函数体;
我们只需要在中断发生后,记得清中断,防止中断重复发生;
注意事项:
在本实验中,关于按键处理那一块,处理不是很到位,
主要应该是处在连按这一块,暂时没去整它,待我需要时,再去整整。
转载于 >
STM32 中断初识
前段时间经常用stm32f4 discovery,但是因为对NVIC , EXTI不是很了解,所以使用的过程中一直都在避免使用中断,这两天没什么事决定来学习一下stm32 的中断,写一下自己的心得,如有谬误之处,欢迎指正。
我把用到的几份文档寄存器的文档(RM0090)、《Cortex-M技术参考手册》、《Cortex™-M4 Devices Generic User Guide》、《ARMv7-M Architecture Reference Manual》放在百度云,需要的自取
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)