STM32学习笔记1.docx
- 文档编号:3652196
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:74
- 大小:98.60KB
STM32学习笔记1.docx
《STM32学习笔记1.docx》由会员分享,可在线阅读,更多相关《STM32学习笔记1.docx(74页珍藏版)》请在冰豆网上搜索。
STM32学习笔记1
STM32学习笔记
(1):
GPIO口的使用
2011年3月19日顺序点亮LED灯
摸索了很久之后终于把ARM开发板上的LED灯点亮了,虽然是很简单的一个IO口操作,但是由于以前从来都没有什么经验,所以浪费了很多时间,也查找了很多资料。
现在可以操作IO口了,证明迈出了学习ARM的第一步。
实验平台清单如下:
开发板:
奋斗STRIVEV3
核心芯片:
STM32F103VET6
开发环境:
RealViewMDK-ARMVersion:
3.50
PC操作系统:
Windows7家庭普通版
仿真器:
SEGGERJ-Link
其中,STM32F103VET6芯片是基于ARMCortex-M3内核的,具体技术参数请参考ST公司给出的芯片资料(MDK不熟悉的话,可以参考ARMRealView系列丛书《ARM开发工具RealViewMDK使用入门》,李宁编著,北京航空航天大学出版社出版。
如果对于STM32不熟悉的话,可以参考ARMRealViewMDK系列丛书《基于MDK的STM32处理器开发应用》,李宁编著,北京航空航天大学出版社出版。
当软硬件平台都准备好之后,就可以开始新的工程了。
对于一个初学者来说,新建一个可以运行的工程其实是有难度的,因为根本不知道从何下手。
因此,我将每一步细节都描述出来,以便于即使是初学者也能很好的理解ARM的初级操作。
打开MDK开发平台,在菜单栏中单击“Project-NewμVisionProject”创建一个新的工程。
然后在弹出的“SelectDeviceforTarget1”对话框中选择合适的芯片。
由于我采用的是STM32F103VET6,所以选择ST–STM32F103VE。
选择好芯片之后,会弹出一个消息框,“CopySTM32StartupCodetoProjectFolderandAddFiletoProject?
”问你是否需要加载启动代码。
选择“是”后进入工程。
所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初始化处理器模式,设置堆栈,初始化变量等等。
由于以上的操作均与处理器体系结构和系统配置密切相关,所以一般由汇编来编写。
而对于初学者而言,自己设计启动代码有一定的难度,MDK开发平台内置了一些常用芯片的启动代码,因此在新建工程的时候,最好是采用默认的启动代码。
当然,芯片制造厂商也会自己编写一些启动代码,放在官网上供开发者下载。
进入工程之后,我们就可以开始写代码了。
首先得新建一个文件,然后将其保存成为*.c的格式,这样开发环境就可以识别出编写的代码里面一些常用的关键字和其他信息了。
我就直接保存成为main.c。
然后在屏幕左边的ProjectWorkspace中的SourceGroup1单击右键,选择AddFilestoGroup“SourceGroup1”,将我们刚刚保存起来的main.c添加到SourceGroup1中,或者直接双击SourceGroup1,也可以添加文件。
接下来就可以开始写代码了,对于初学者而言,最基础的操作应该是对芯片IO口的操作了。
因此我在学习ARM的时候,第一个工程就选择了让开发板上的3个LED灯顺序点亮。
STM32F103VET6中一共有A-G共7组通用输入输出接口(General-PurposeInputs/Outputs),每个GPIO引脚都可以由软件配置成输出(推挽或开漏)、输入(带或不带上拉或下拉)或复用的外设功能端口。
多数GPIO引脚都与数字或模拟的复用外设共用。
具体的细节请参考Datasheet。
在《基于MDK的STM32处理器开发应用》一书中,“7.1通用IO端口”详细描述了各个端口的功能、寄存器格式以及其他相关信息,因此就不在这里赘述了。
回到MDK开发平台,现在要在main.c中加入相关代码了。
代码清单如下:
#include"stm32f10x_lib.h"
intmain()
{
inti;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOB,ENABLE);//开启外设时钟
GPIOD->CRL=0x33333333;//设置端口配置寄存器
GPIOB->CRL=0x33333333;
while
(1)
{
GPIOD->ODR=0xffffffbf;//设置端口输出寄存器
for(i=0;i<1000000;i++);//延时
GPIOD->ODR=0xffffffff7;
for(i=0;i<1000000;i++);
GPIOD->ODR=0x00000000;
GPIOB->ODR=0xffffffff;
for(i=0;i<1000000;i++);
GPIOB->ODR=0x00000000;
}
}
上述代码中,#include"stm32f10x_lib.h"包含了开发stm32f10x系列芯片所需的基本头文件,在进行程序编写的时候,务必要包含此头文件。
RCC_APB2PeriphClockCmd()函数是设置外设时钟。
ARM与C51单片机不同的是,不用外设的时候,如IO口、ADC、定时器等等,都是禁止时钟的,以达到节能的目的,只有要用到的外设,才开启它的时钟。
因此在需要用到GPIOB和GPIOD的时候,我们需要先开启它的时钟,具体用到的是函数库里面的函数:
voidRCC_APB2PeriphClockCmd(uint32_tRCC_APB2Periph,FunctionalStateNewState)
其中,第一个参数需要指示要开启什么端口的时钟,RCC_APB2Periph_GPIOx就是开启GPIOx的时钟,第二个参数需要指示是开启还是关闭,ENABLE/DISABLE。
开启外设时钟之后,然后就开始对GPIO的配置寄存器进行设置了,具体设置方式参考《基于MDK的STM32处理器开发应用》一书中,“7.1通用IO端口”。
While循环里面就是给GPIO的端口输出寄存器赋值,由于我手上这款奋斗开发板的三个LED灯分别接的是D3、D6和B5,所以只要将D端口和B端口相应的位上置1就可以了。
编译之后我们发现编译器报错,UndefinedsymbolRCC_APB2PeriphClockCmd,是因为我们使用了的RCC_APB2PeriphClockCmd()函数在头文件中声明了,却没有在C文件中定义,这个函数在..Keil\ARM\RV31\LIB\ST\STM32F10x\stm32f10x_rcc.c中,将这个文件复制到工程的根目录下,然后在屏幕左边的Workspace中添加进来,就可以了。
至于如何下载到ARM开发板中,不同的开发板有不同的方法,而开发板生产厂商一般都会将有关的文档连同开发板一起附送,在此就不详细赘述了。
其实,在MDK的库中,还定义了很多宏,可以避免让我们自己去查找相关资料来设置寄存器的各个位。
比如,在本次实验中,对于LED等的亮灭也可以通过以下代码来实现。
#include"stm32f10x_lib.h"
intmain()
{
inti;
GPIO_InitTypeDefGPIO_InitStructure;//定义GPIO宏操作结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOB,ENABLE);//外设时钟配置,开启GPIOB和GPIOD的时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//将B5口配置为通用推挽输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//口线翻转速度为50MHz
GPIO_Init(GPIOB,&GPIO_InitStructure);//配置GPIOB口
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_3;//将D3和D6口配置为推挽输出
GPIO_Init(GPIOD,&GPIO_InitStructure);//配置GPIOD口
while
(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);//B5口输出高电平
GPIO_ResetBits(GPIOD,GPIO_Pin_6);//D6口输出低电平
GPIO_ResetBits(GPIOD,GPIO_Pin_3);//D3口输出低电平
for(i=0;i<1000000;i++);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOD,GPIO_Pin_6);
GPIO_SetBits(GPIOD,GPIO_Pin_3);
for(i=0;i<1000000;i++);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOD,GPIO_Pin_3);
GPIO_SetBits(GPIOD,GPIO_Pin_6);
for(i=0;i<1000000;i++);
}
}
由于我们使用了GPIO_InitTypeDef类型,所以我们需要找到它的定义,这个定义包含在“…\Keil\ARM\RV31\LIB\ST\STM32F10x\stm32f10x_gpio.c”中,将文件复制到工程根目录下,然后再添加进入工程中,编译才不会报错。
在绝大多数C编译器中,要求所有的变量声明都在执行语句块之前,也就是说如果需要定义的变量需要先在进入main函数一开始就全部定义好,如果执行了某一条语句之后再定义变量的话,就会报错
STM32学习笔记
(2):
外部中断的使用
2011年3月20日外部中断
中断对于开发嵌入式系统来讲的地位绝对是毋庸置疑的,在C51单片机时代,一共只有5个中断,其中2个外部中断,2个定时/计数器中断和一个串口中断,但是在STM32中,中断数量大大增加,而且中断的设置也更加复杂。
今天就将来探讨一下关于STM32中的中断系统。
1基本概念
ARMCoetex-M3内核共支持256个中断,其中16个内部中断,240个外部中断和可编程的256级中断优先级的设置。
STM32目前支持的中断共84个(16个内部+68个外部),还有16级可编程的中断优先级的设置,仅使用中断优先级设置8bit中的高4位。
STM32可支持68个中断通道,已经固定分配给相应的外部设备,每个中断通道都具备自己的中断优先级控制字节PRI_n(8位,但是STM32中只使用4位,高4位有效),每4个通道的8位中断优先级控制字构成一个32位的优先级寄存器。
68个通道的优先级控制字至少构成17个32位的优先级寄存器。
4bit的中断优先级可以分成2组,从高位看,前面定义的是抢占式优先级,后面是响应优先级。
按照这种分组,4bit一共可以分成5组
第0组:
所有4bit用于指定响应优先级;
第1组:
最高1位用于指定抢占式优先级,后面3位用于指定响应优先级;
第2组:
最高2位用于指定抢占式优先级,后面2位用于指定响应优先级;
第3组:
最高3位用于指定抢占式优先级,后面1位用于指定响应优先级;
第4组:
所有4位用于指定抢占式优先级。
所谓抢占式优先级和响应优先级,他们之间的关系是:
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套。
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。
如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
每一个中断源都必须定义2个优先级。
有几点需要注意的是:
1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;
2)抢占式优先级别相同的中断源之间没有嵌套关系;
3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。
2GPIO外部中断
STM32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组位一个单位的,同组间的外部中断同一时间只能使用一个。
比如说,PA0,PB0,PC0,PD0,PE0,PF0,PG0这些为1组,如果我们使用PA0作为外部中断源,那么别的就不能够再使用了,在此情况下,我们只能使用类似于PB1,PC2这种末端序号不同的外部中断源。
每一组使用一个中断标志EXTIx。
EXTI0–EXTI4这5个外部中断有着自己的单独的中断响应函数,
EXTI5-9共用一个中断响应函数,
EXTI10-15共用一个中断响应函数。
对于中断的控制,STM32有一个专用的管理机构:
NVIC。
对于NVIC的详细解释,可以参考《ARMCortex-M3权威指南》,JosephYiu著,宋岩译,北京航空航天大学出版社出版,第8章NVIC与中断控制。
中断的使能,挂起,优先级,活动等等部都是NVIC在管理的。
因为我学习STM32重点在于如何开发程序,所以内部的一些东西,在此我就不详细说明了,有感兴趣的可以参看上面提到的那本数。
3程序开发
其实上面那些基本概念和知识只是对STM32的中断系统有一个大概的认识,用程序说话将会更能够加深如何使用中断。
使用外部中断的基本步骤如下:
1.设置好相应的时钟;
2.设置相应的中断;
3.IO口初始化;
4.把相应的IO口设置为中断线路(要在设置外部中断之前)并初始化;
5.在选择的中断通道的响应函数中中断函数。
由于我用的奋斗开发板没有引出相应的芯片引脚,所以只能用按键来触发相应的中断。
根据原理图,K1/K2/K3连接的是PC5/PC2/PC3,因此我将用EXTI5/EXTI2/EXTI3三个外部中断。
PB5/PD6/PD3分别连接了三个LED灯。
中断的效果是按下按键,相应的LED灯将会被点亮。
1.设置相应的时钟
首先需要打开GPIOB、GPIOC和GPIOE(因为按键另外一端连接的是PE口)。
然后由于是要用于触发中断,所以还需要打开GPIO复用的时钟。
相应的函数在GPIO的学习笔记中有了详细了解释。
详细代码如下:
voidRCC_cfg()
{
//打开PEPDPCPB端口时钟,并且打开复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
}
设置相应的时钟所需要的RCC函数在stm32f10x_rcc.c中,所以要在工程中添加此文件。
2.设置好相应的中断
设置相应的中断实际上就是设置NVIC,在STM32的固件库中有一个结构体NVIC_InitTypeDef,里面有相应的标志位设置,然后再用NVIC_Init()函数进行初始化。
详细代码如下:
voidNVIC_cfg()
{
NVIC_InitTypeDefNVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择中断分组2
NVIC_InitStructure.NVIC_IRQChannel=EXTI2_IRQChannel;//选择中断通道2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占式中断优先级设置为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//响应式中断优先级设置为0
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能中断
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI3_IRQChannel;//选择中断通道3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//抢占式中断优先级设置为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应式中断优先级设置为1
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能中断
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel=EXTI9_5_IRQChannel;//选择中断通道5
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占式中断优先级设置为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;//响应式中断优先级设置为2
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能中断
NVIC_Init(&NVIC_InitStructure);
}
由于有3个中断,因此根据前文所述,需要有3个bit来指定抢占优先级,所以选择第2组。
又由于EXTI5-9共用一个中断响应函数,所以EXTI5选择的中断通道是EXTI9_5_IRQChannel,详细信息可以在头文件中查询得到。
用到的NVIC相关的库函数在stm32f10x_nivc.c中,需要将此文件复制并添加到工程中。
具体位置可以查看关于GPIO的笔记。
这段代码编译起来没有任何问题,但是在链接的时候就会报错,需要把STM32F10xR.LIB加入工程中,具体位置在…\Keil\ARM\RV31\LIB\ST\STM32F10xR.LIB。
3.IO口初始化
voidIO_cfg()
{
GPIO_InitTypeDefGPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;//选择引脚2
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出频率最大50MHz
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//带上拉电阻输出
GPIO_Init(GPIOE,&GPIO_InitStructure);
GPIO_ResetBits(GPIOE,GPIO_Pin_2);//将PE.2引脚设置为低电平输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_5;//选择引脚235
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//选择输入模式为浮空输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出频率最大50MHz
GPIO_Init(GPIOC,&GPIO_InitStructure);//设置PC.2/PC.3/PC.5
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_6;//选择引脚36
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出频率最大50MHz
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//带上拉电阻输出
GPIO_Init(GPIOD,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;//选择引脚5
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//输出频率最大50MHz
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//带上拉电阻输出
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
其中连接外部中断的引脚需要设置为输入状态,而连接LED的引脚需要设置为输出状态,初始化PE.2是为了使得按键的另外一端输出低电平。
GPIO中的函数在stm32f10x_gpio.c中。
4.把相应的IO口设置为中断线路
由于GPIO并不是专用的中断引脚,因此在用GPIO来触发外部中断的时候需要设置将GPIO相应的引脚和中断线连接起来,具体代码如下:
voidEXTI_cfg()
{
EXTI_InitTypeDefEXTI_InitStructure;
//清空中断标志
EXTI_ClearITPendingBit(EXTI_Line2);
EXTI_ClearITPendingBit(EXTI_Line3);
EXTI_ClearITPendingBit(EXTI_Line5);
//选择中断管脚PC.2PC.3PC.5
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource3);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line=EXTI_Line2|EXTI_Line3|EXTI_Line5;//选择中断线路235
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrup
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- STM32 学习 笔记