手把手教你STM32笔记.docx
- 文档编号:10771065
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:18
- 大小:305.12KB
手把手教你STM32笔记.docx
《手把手教你STM32笔记.docx》由会员分享,可在线阅读,更多相关《手把手教你STM32笔记.docx(18页珍藏版)》请在冰豆网上搜索。
手把手教你STM32笔记
STM32F103
1、IO口处理
IO口包含7个寄存器
配置寄存器两个:
CRL(32),CRH(32)
数据寄存器两个:
IDR(32),ODR(32),但是他们只用了16位
置位复位寄存器:
BSRR(32)
复位寄存器:
BRR(16)
锁存寄存器:
LCKR(32)
常用的有前面四个:
其中前面两个是用来设置的,后面两个是用来操作的。
每个IO口占用四位进行设置(低两位是MODE,高两位是CNF),每组16个,总共需要64个位设置,分别从CRL低位开始,到CRH的高位结束。
每个IO口四位二进制的常用配置:
模拟输入模式(ADC):
0x0;
推挽输出模式(输出口50MHz):
0x3;
上/下拉输入模式(输入口用):
0x8;
复用输出(第二供能):
0xB;
STM32F407学习笔记
1、系统时钟的设置:
Stm32_Clock_Init(168,4,2,7);
参数分别是:
PLLN,PLLM,PLLP,PLLQ
HSE分频PLLM之后为VCO的输入,一般VCO的输入要求为1~2MHz,一般建议取为2MHz,防止PLL抖动。
VCO输出是输入的PLLN倍频,SYSCLK在去PLL输出时,SYSCLK=PLL=HSE/PLLM*PLLN/PLLP
而PLLQ是为48MHz时钟配置用的,CLK48=HSE/PLLM*PLLN/PLLQ
所以要设置系统时钟为168MHz时候推荐的参数取值为
SYSCLK=PLL=HSE/PLLM*PLLN/PLLP=8/4*168/2=168MHz
CLK48=HSE/PLLM*PLLN/PLLQ=8/4*168/7=48MHz
2、延时函数设置:
delay_init(168);
延时函数参数为系统时钟SYSCLK
初始化后就可以调用延时函数:
delay_ms(ms);参数不能大于65536,因为参数是16位数
delay_us(us);参数不能大于798915
3、普通IO的使用
a.首先是使能时钟
RCC->AHB1ENR|=1<<5;在该寄存器相应的位置1即可
b.IO口模式设置:
GPIO_Set(GPIOF,PIN9|PIN10,GPIO_MODE_OUT,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);
参数分别是:
GPIOx,
PIN9|PIN10(具体对应的口,可以使用与的关系)因为每种占一位
#definePIN01<<0
#definePIN11<<1
#definePIN21<<2
#definePIN31<<3
#definePIN41<<4
#definePIN51<<5
#definePIN61<<6
#definePIN71<<7
#definePIN81<<8
#definePIN91<<9
#definePIN101<<10
#definePIN111<<11
#definePIN121<<12
#definePIN131<<13
#definePIN141<<14
#definePIN151<<15
Mode:
四种,各个模式只能设置一种
#defineGPIO_MODE_IN0//普通输入模式
#defineGPIO_MODE_OUT1//普通输出模式
#defineGPIO_MODE_AF2//AF功能模式
#defineGPIO_MODE_AIN3//模拟输入模式
输出推挽或者开漏选择:
#defineGPIO_OTYPE_PP0//推挽输出
#defineGPIO_OTYPE_OD1//开漏输出
推挽输出可输出强的高、低电平,用于连接数字器件
开漏输出相当于三极管的集电极,电流型驱动,只可以输出强的低电平,高电平需外拉。
(电路中只有一个MOS管)
GPIO的速度
#defineGPIO_SPEED_2M0
#defineGPIO_SPEED_25M1
#defineGPIO_SPEED_50M2
#defineGPIO_SPEED_100M3
#defineGPIO_PUPD_NONE0//不带上拉不带下拉
#defineGPIO_PUPD_PU1//上拉
#defineGPIO_PUPD_PD2//下拉
#defineGPIO_PUPD_RES3//保留
注意:
IO口推挽输出时候是否上拉不重要,但是尽量设置成为上拉或者是下课,如果外设是高低平驱动则设置成下拉,低电平驱动设置成为上拉,防止误动作。
按键扫描笔记:
按键扫描函数同以前的一样不必多讲
主要是讲解IO口作为输入口的设置:
GPIO通用设置
GPIO_Set函数的前两个参数是用于来定位具体的哪一个IO口的,第三个参数是设置输入输出模式的
按键是普通输入模式,第四第五参数是与输出有关的,写为0即可,最后一个参数设置需要看按键按下是高电平则设置为下拉,按键按下是低电平则设置为上拉。
串口实验:
串口初始化函数参数:
第一个是APB2的时钟PCLK2。
第二个是波特率,不用多说
uart_init(84,115200);
时钟设置:
需要设置两个时钟,一个相应外设的时钟,一个是对应的IO口的时钟。
APB2的时钟PCLK2的设置,默认系统SYSCLK=168时候APB2=84MHz
设置需要自己配置寄存器(自己测试)
RCC_CFGR寄存器设置:
[15:
13]APB2(高速)从PHB而来,可以设置自己的分频(1,2,4,8,16)最高84MHz,系统默认是不分频
[12:
10]APB1(低速)从PHB而来,可以设置自己的分频(1,2,4,8,16)最高42MHz,系统默认是不分频
[7:
4]AHB从SYSCLK而来,可以设置自己的分频(1,2,4,8,16,64,128,236,512)当使用以太网时候AHB时钟至少为25MHz
在系统时钟分设置时候早已经将APB2设置为2分配,PHB不分频,APB1设置为4分频
IO口设置分为通用设置和复用设置
GPIO设置:
GPIO_Set(GPIOA,PIN9|PIN10,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);
设置相应的功能:
设置为推挽上拉输出,且为复用模式
复用输出GPIO_AF_Set(GPIOA,9,7);
前两个参数是定位IO口,后面一个参数是定位属于哪一中复用
AF0:
MCO/SWD/SWCLK/RTC
AF1:
TIM1/TIM2;
AF2:
TIM3~5;
AF3:
TIM8~11
AF4:
I2C1~I2C3;
AF5:
SPI1/SPI2;
AF6:
SPI3;
AF7:
USART1~3;
AF8:
USART4~6;
AF9:
CAN1/CAN2/TIM12~14
AF10:
USB_OTG/USB_HS
AF11:
ETH
AF12:
FSMC/SDIO/OTG/HS
AF13:
DCIM
AF14:
AF15:
EVENTOUT
串口中断设置:
MY_NVIC_Init(NVIC_PreemptionPriority,NVIC_SubPriority,NVIC_Channel,NVIC_Group)
4个参数:
分别是
抢占优先级数:
响应优先级数:
中断编号:
在顶层头文件stm32f4xx.h中包含定义是一个枚举类型:
IRQn类型数据
中断分组:
0~4组
//组0:
0位抢占优先级,4位响应优先级
//组1:
1位抢占优先级,3位响应优先级
//组2:
2位抢占优先级,2位响应优先级
//组3:
3位抢占优先级,1位响应优先级
//组4:
4位抢占优先级,0位响应优先级
串口发送时候可以正常使用printf函数,同以前学习的C语言一样使用,注意一点是换行必须是\r\n一起使用才行。
外部中断使用:
外部中断分两部分,初始化与中断服务程序的编写
初始化:
第一步:
外部中断一般必须要与按键输入一起使用,所以外部中断必须有与按键一样的IO初始化,直接照搬过来即可
第二步:
中端口映射与触发设置(包括开启中断)
Ex_NVIC_Config(GPIO_E,2,FTIR);
参数1与参数2是选择映射的IO口,参数3是触发模式:
FTIR=1标示下降沿触发,RTIR=2标示上升沿3标示任意沿
注意该函数一次调用只能设置一个IO口,多个口需要多次调用
第三步:
设置中断分组优先级以及终端号(包括是使能中断)
MY_NVIC_Init(NVIC_PreemptionPriority,NVIC_SubPriority,NVIC_Channel,NVIC_Group)该函数同上,串口处已经讲过
中断服务函数部分:
voidEXTI0_IRQHandler(void)
{
delay_ms(10);
if(WK_UP==1)
{
BEEP=!
BEEP;
}
EXTI->PR=1<<0;//
}
中断服务程序是一个函数,该函数的函数名是一个固定的,叫做中断向量,定义在启动文件startup_stm32f40_41xxx.s中,已经被定义好了,不能随便定义。
所有的中断服务程序函数名都在这里被定义了。
用的时候去选用就行了。
注意外部中断的0~4外部中断是每一个单独的外部中断,5~9公用一个中断服务程序:
EXTI9_5_IRQHandler
10~15公用:
EXTI15_10_IRQHandler
中断服务函数执行完毕前要清除中断标志:
EXTI->PR=1<<0;
请标志是在相应的寄存器(挂起寄存器PendingRegister)的相应为置1,而不是清零
当中断公用一个中断服务函数时候如何进行区分:
可以在中断服务程序里检测判断:
通道读取各个中断对应的IO口的状态进行判断。
里面可以这样写
if((EXTI->PR & 0x20)==0x20) //说明中断线5发生了中断
{
//中断内容
}
EXTI->PR|=1<<5;//清除中断挂起
例如下面代码:
voidEXTI9_5_IRQHandler(void)
{
delay_ms(10);//延时去抖动
if(KEY3==0)
{
DS3=!
DS3;
}
elseif(KEY4==0)//
{
DS3=!
DS3;
}
elseif(KEY5==0)
{
DS3=!
DS3;
}
EXTI_ClearITPendingBit(EXTI_Line5);
EXTI_ClearITPendingBit(EXTI_Line8);
EXTI_ClearITPendingBit(EXTI_Line9);
}
有个问题,为什么库函数版本的程序里面中断配置里有一个要选择是中断模式还是事件模式,这个地方没有看懂?
外部中断的红色代码没有进行测试。
需要测试一下
外部中断讲解完毕。
定时器:
定时器分为初始化和中断程序编写:
初始化:
TIM3_Int_Init(5000-1,8400-1);
该函数有两个参数:
第一个参数是arr自动重装初值,第二个参数是时钟分频数
定时器溢出时间:
Tout=((arr+1)*(psc+1))/Ft单位是us
其中Ft为定时器的工作频率单位MHz
Ft=APB1*2=42*2=84MHz
如图,因为APB1分频系数presc=4,APB2分频系数presc=2,所以此处是else,APB1或者APB2时钟的二倍作为定时器时钟。
各种时钟的挂载:
RCC_AHB1Periph_GPIOA:
GPIOAclock
RCC_AHB1Periph_GPIOB:
GPIOBclock
RCC_AHB1Periph_GPIOC:
GPIOCclock
RCC_AHB1Periph_GPIOD:
GPIODclock
RCC_AHB1Periph_GPIOE:
GPIOEclock
RCC_AHB1Periph_GPIOF:
GPIOFclock
RCC_AHB1Periph_GPIOG:
GPIOGclock
RCC_AHB1Periph_GPIOG:
GPIOGclock
RCC_AHB1Periph_GPIOI:
GPIOIclock
RCC_AHB1Periph_GPIOJ:
GPIOJclock(STM32F42xxx/43xxxdevices)
RCC_AHB1Periph_GPIOK:
GPIOKclock(STM32F42xxx/43xxxdevices)
RCC_AHB1Periph_CRC:
CRCclock
RCC_AHB1Periph_BKPSRAM:
BKPSRAMinterfaceclock
RCC_AHB1Periph_CCMDATARAMENCCMdataRAMinterfaceclock
RCC_AHB1Periph_DMA1:
DMA1clock
RCC_AHB1Periph_DMA2:
DMA2clock
RCC_AHB1Periph_DMA2D:
DMA2Dclock(STM32F429xx/439xxdevices)
RCC_AHB1Periph_ETH_MAC:
EthernetMACclock
RCC_AHB1Periph_ETH_MAC_Tx:
EthernetTransmissionclock
RCC_AHB1Periph_ETH_MAC_Rx:
EthernetReceptionclock
RCC_AHB1Periph_ETH_MAC_PTP:
EthernetPTPclock
RCC_AHB1Periph_OTG_HS:
USBOTGHSclock
RCC_AHB1Periph_OTG_HS_ULPI:
USBOTGHSULPIclock
RCC_AHB2Periph_DCMI:
DCMIclock
RCC_AHB2Periph_CRYP:
CRYPclock
RCC_AHB2Periph_HASH:
HASHclock
RCC_AHB2Periph_RNG:
RNGclock
RCC_AHB2Periph_OTG_FS:
USBOTGFSclock
RCC_APB1Periph_TIM2:
TIM2clock
RCC_APB1Periph_TIM3:
TIM3clock
RCC_APB1Periph_TIM4:
TIM4clock
RCC_APB1Periph_TIM5:
TIM5clock
RCC_APB1Periph_TIM6:
TIM6clock
RCC_APB1Periph_TIM7:
TIM7clock
RCC_APB1Periph_TIM12:
TIM12clock
RCC_APB1Periph_TIM13:
TIM13clock
RCC_APB1Periph_TIM14:
TIM14clock
RCC_APB1Periph_WWDG:
WWDGclock
RCC_APB1Periph_SPI2:
SPI2clock
RCC_APB1Periph_SPI3:
SPI3clock
RCC_APB1Periph_USART2:
USART2clock
RCC_APB1Periph_USART3:
USART3clock
RCC_APB1Periph_UART4:
UART4clock
RCC_APB1Periph_UART5:
UART5clock
RCC_APB1Periph_I2C1:
I2C1clock
RCC_APB1Periph_I2C2:
I2C2clock
RCC_APB1Periph_I2C3:
I2C3clock
RCC_APB1Periph_CAN1:
CAN1clock
RCC_APB1Periph_CAN2:
CAN2clock
RCC_APB1Periph_PWR:
PWRclock
RCC_APB1Periph_DAC:
DACclock
RCC_APB1Periph_UART7:
UART7clock
RCC_APB1Periph_UART8:
UART8clock
RCC_APB2Periph_TIM1:
TIM1clock
RCC_APB2Periph_TIM8:
TIM8clock
RCC_APB2Periph_USART1:
USART1clock
RCC_APB2Periph_USART6:
USART6clock
RCC_APB2Periph_ADC1:
ADC1clock
RCC_APB2Periph_ADC2:
ADC2clock
RCC_APB2Periph_ADC3:
ADC3clock
RCC_APB2Periph_SDIO:
SDIOclock
RCC_APB2Periph_SPI1:
SPI1clock
RCC_APB2Periph_SPI4:
SPI4clock
RCC_APB2Periph_SYSCFG:
SYSCFGclock
RCC_APB2Periph_TIM9:
TIM9clock
RCC_APB2Periph_TIM10:
TIM10clock
RCC_APB2Periph_TIM11:
TIM11clock
RCC_APB2Periph_SPI5:
SPI5clock
RCC_APB2Periph_SPI6:
SPI6clock
RCC_APB2Periph_SAI1:
SAI1clock(STM32F42xxx/43xxxdevices)
RCC_APB2Periph_LTDC:
LTDCclock(STM32F429xx/439xxdevices)
定时器中断服务程序编写:
函数名称已经定义好了,就是启动文件中的中断向量表
voidTIM3_IRQHandler(void)
{
if(TIM3->SR&0X0001)//溢出中断
{
LED1=!
LED1;//中断的内容
}
TIM3->SR&=~(1<<0);//清除标志位
}
定时器输出PWM波形:
初始化函数
TIM14_PWM_Init(u32arr,u32psc)
参数分别是重装初值和预分频系数,与之前讲的相同主要是为了设置溢出时间,也就是PWM波形的周期
占空比设置:
#definePWM_VALTIM14->CCR1
通过寄存器CCR1的值进行设置该寄存器值最大为arr
占空比=PWM_VAL/arr*100%
TIM_PWM初始化中不同的是要加上IO口设置:
IO时钟使能,通用IO口设置(选择具体IO口设置为复用输出,推挽输出,输出速率,上拉输出)复用IO设置(同之前串口讲过的部分)
剩下的设置:
以后慢慢测试
重映设部分:
自己看看手册
ADC与DMA部分讲解:
DMA部分讲解:
F407总共两个DMA控制器共有16个数据流(stream),每一个控制器8个,每一个数据流有8个通道(channel)或请求(request),每一个通道都一个仲裁器处理处理优先级。
两种模式:
FIFO模式:
直接模式:
外设对应的控制器,数据流,通道是规定好的,使用时需要查表使用,不能随便设置。
传输数量最大65535个,在地址增量模式下,只有当传输完设置的所有数据个数以后才会再次王内存地址的第一个地址中存数,否则内存指针会一直增加。
循环模式:
ADC比较适合这中模式,如果此时配置了存储器突发模式,此时传输数量DMA_SxNDTR=((Mburst节拍)X(Msize)/(Psize))的倍数。
循环模式标示传完DMA_SxNDTR个数量之后再从头接着传
单次传输:
突发传输:
仅在指针增量模式下使用
FIFO的阀值必须与突发传输的值进行匹配
流控制器:
DMA控制器的时候传输数量DMA_SxNDTR可以设定
外设为流控制器的时候传输数量DMA_SxNDTR没有用
定时器:
三种:
高级,通用,基本
定时器时钟输入四种:
RCC
ETR外部引脚:
经过极性选择等等
ITR0-3:
内部的级联,来自其他定时器的TRGO
TI1FP:
定时器外部引脚
经过一个PSC预分频器后得到CK_CNT
基本计数单元
输入捕获
输出比较控制
输入捕获与输出比较是一个通道,不能同时用
TIM_Clock_Division_CKD:
这个是输入捕获时候用的
配置:
时钟选择:
APB1时钟—X2/X1—CK_INT或者CK_PSC
APB1的分频系数是1时候则X1
APB1的分频系数不是1时候则X2
一般是APB1=AHB/4=42,因此处要X2
所以CK_INT=84
然后在进行分频:
这个是预分频系数TIM_Prescaler
模式:
向上,向下,中央对齐
(*(volatileunsignedlong*)(摘自Openedv)
对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。
如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的“端口”的概念。
如果是内存映射,那就方便的多了。
以#defineIOPIN(*((volatileunsignedlong*)0xE0028000))为例:
作为一个宏定义语句,define是定义一个变量或常量的伪指令。
首先(volatileunsignedlong*)的意思是将后面的那个地址强制转换成volatileunsignedlong*,unsignedlong*是无符号长整形,volatile是一个类型限定符,如const一样,当使用volatile限定时,表示这个变量是依赖系统实现的,以为着这个变量会被其他程序或者计算机硬件修改,由于地址依赖于硬件,volatile就表示他的值会依赖于硬件。
volatile类型是这样的,其数据确实可能在未知的情况下发生变化。
比如,硬件设备的终端更改了它,现在硬件设备往往也有自己的私有内存地址,比如显存,他们一般是通过映象的方式,反映到一段特定的内存地址当中,这样,在某些条件下,程序就可以直接访问这些私有内存了。
另外,比如共享的内存地址,多个程序都对它操作的时候。
你的程序并不知道,这个内存何时被改变了。
如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的。
归纳起来如下:
1.volatile变量可变允许除了程序之外的比如硬件来修改他的内容
2.访问该数据任何时候都会直接访问该地址处内容,即通过cache提高访问速度的优化被取消
对于((volatileunsignedlong*)0xE0
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 手把手 STM32 笔记