1、stm32初学例程If an error, please contact author, to be corrected.For other uses, indicate the source, to express my recognition of the results.Thank you.Introduction由于公司需要,以及充满对arm的向往,开始学习STM32.。与8位单片机不同,32位处理器的开发变得更加复杂,同时也伴随着性能和功能的显著提高。由于初学STM32,遇到了很多莫名其妙的问题,但是总归是在自己的摸索中一个个的解决了。说来惭愧,三个星期天天忙STM32,总算实现了几
2、个常用的功能,心中窃喜arm也不过如此嘛!当然,STM32许多过人之处还没有细细研究,巧妙设计之处还没有完全的感受到,暂且就当是小小的步入STM32开发初期阶段吧。为了纪念STM32学习过程的辛劳和无助,记下我的学习过程,与大家共勉。也希望能为广大的初学者提供小小的帮助。如有任何问题和建议,您可以联系 。此文章所采用的开发环境如下A、开发板、仿真器:使用的是STM32F103C8-PKT+ST-LINK;B、开发环境:IAR Embedded Workbench for ARM 6.10 Kick start;C、Firmware:STM32F10x_StdPeriph_Lib_V首先安装IA
3、R Embedded Workbench for ARM, 32K Kick start Edition;下载STM32F10x_StdPeriph_Lib_V;购买一块开发板,通过ST-Link进行仿真和调试;(所有的资源都可以到IAR和ST官网上下载,资源的获取也是很重要的,要是自己不培养找资源的功夫,就不是一个好的开发人员,这里就不留网址了)关于编译环境和仿真方法可以参考我的另一篇记录环境搭建的文章EWARM_STM32_Use_Instructions。在成功搭建开发环境之后,我们就可以对STM32进行深入的学习了,STM32功能繁多,不可以一下子学习所有的知识,因此采用各个击破,由简
4、转难的学习方法,一步步的学会STM32的功能。总之,我们需要调通每一个经常使用到的功能。而本篇文章就是记录了调通某些功能的历程,以及在调试过程中可能会出现的问题。由于自身能力的限制,对有些知识点可能理解的不是很透彻,因此错误难免,希望谅解并给出指导建议。您可以通过 联系我,谢谢。在使用固件库时,需要自己有点C语言的相关基础,如结构体、枚举、指针等;1 GPIO Test(端口操作实验)GPIO实验是最简单,也是一般最先开始的一个实验,他可以搭建一个最小的工程项目,之后,所有的实验都可以建立在该项目之上,从而节省了在搭建过程中所消耗的时间和精力。建议,当该实验顺利完成后,作为一个模板,供以后实验
5、使用,确保您可以将精力花费在需要实现的功能上。关于环境的搭建,您可以参考EWARM_STM32_Use_Instructions;本实验需要实现:四个LED的简单控制。1.1 硬件设计:1. 确认硬件连接:根据开发板原理图,如图,得知:与LED相连的有PB12、PB13、PB14、PB15;以下就是对这四个引脚的配置,及相关操作;1.2 软件设计:/main()程序开始1.2.1 头文件:#include stm32f10x.h#include main.h/#include k_gpio.h /已经转移到main函数中;#define VECT_TAB_RAM /选择在RAM中调试;/mai
6、n()函数:1.2.2 系统初始化SystemInit(); /选择系统运行时钟,默认是72MHz,可以调试跟踪进行修改;#ifdef VECT_TAB_RAM /设置仿真调试区域,这里设置成在RAM中; / Set the Vector Table base location at 0x20000000 NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ / Set the Vector Table base location at 0x08000000 NVIC_SetVectorTable(NVIC
7、_VectTab_FLASH, 0x0); #endif1.2.3 GPIO配置 /首先,选择GPIOB外设时钟;只有选择好时钟后,才可以进行下面的配置;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); /配置端口,开始时,不需要知道所有的细节,从字面上理解就ok;gpio.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_Out_PP;GPIO
8、_Init(GPIOB, &gpio); GPIO_SetBits(GPIOB, GPIO_Pin_12); /将端口拉高,灭LED灯;GPIO_SetBits(GPIOB, GPIO_Pin_13);GPIO_SetBits(GPIOB, GPIO_Pin_14);GPIO_SetBits(GPIOB, GPIO_Pin_15);如此,GPIOB连接LED的四个端口都配置好了;1.2.4 GPIO操作While(1) GPIO_ResetBits(GPIOB, GPIO_Pin_12); Delay(0xffffff); GPIO_ResetBits(GPIOB, GPIO_Pin_13);
9、 Delay(0xffffff); GPIO_ResetBits(GPIOB, GPIO_Pin_14); Delay(0xffffff); GPIO_ResetBits(GPIOB, GPIO_Pin_15); Delay(0xffffff); Delay(0xffffff); Delay(0xffffff); GPIO_SetBits(GPIOB, GPIO_Pin_12); Delay(0xffffff); GPIO_SetBits(GPIOB, GPIO_Pin_13); Delay(0xffffff); GPIO_SetBits(GPIOB, GPIO_Pin_14); Delay(
10、0xffffff); GPIO_SetBits(GPIOB, GPIO_Pin_15); Delay(0xffffff); GPIO_ResetBits(GPIOB, GPIO_Pin_12); GPIO_ResetBits(GPIOB, GPIO_Pin_13); GPIO_ResetBits(GPIOB, GPIO_Pin_14); GPIO_ResetBits(GPIOB, GPIO_Pin_15);如此而已;这是最简单的实验,可以为此建立一个拥有自己特色的工程模板;1.3 Test注意A、硬件必须首先确保正确;B、开发环境搭建确保正确;C、系统时钟的选择要清楚;仿真所处的区域要清楚;D
11、、外设时钟的配置是第一位的,其他配置都在其后;2 Time2 Test(Time2定时实验)Timer作为控制器和处理器的一个重要的组成部分,是几乎所有系统都需要的一个模块,可以提高程序的实时性、精确性,以及安全性,是最为重要的一个学习方面;STM32有着丰富的Time系统,很容易使初次接触的人产生恐惧,不过,归咎起来,他的作用也只是定时和计数,只不过衍生的功能比较强大,有PWM,比较捕获,强制输出等,其实不需要所有的功能都掌握,您只需要用到什么功能再去学习某个功能即可;本实验需要实现:使用Time2进行定时的功能,使得LED灯进行相应时间的闪烁。2.1 硬件设计在Time2上,主要是芯片内部
12、实现,所以Time2无需关注硬件;操作的LED,我们需要实现与PB12引脚相连的LED的亮灭;2.2 软件设计#include stm32f10x.h#include main.h/#include k_gpio.h /已经转移到main函数中;/#include k_time2.h /本实验增加部分,但已经转移到main函数中;#define VECT_TAB_RAM void main() GPIO_InitTypeDef gpio; TIM_TimeBaseInitTypeDef time2; /本实验增加部分NVIC_InitTypeDef nvic; /本实验增加部分SystemIn
13、it(); #ifdef VECT_TAB_RAM / Set the Vector Table base location at 0x20000000 NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ / Set the Vector Table base location at 0x08000000 NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif /GPIOB端口配置RCC_APB2PeriphClockCmd(RCC_APB2Periph_
14、GPIOB, ENABLE); gpio.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_Init(GPIOB, &gpio); GPIO_SetBits(GPIOB, GPIO_Pin_12); GPIO_SetBits(GPIOB, GPIO_Pin_13);GPIO_SetBits(GPIOB, GPIO_Pin_14);GPIO_SetBits(GPIOB, GPIO_
15、Pin_15);/以上已经有过详细的论述,可以参考先前的Test;/以下是本实验增加的部分;/Time2配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2|RCC_APB2Periph_AFIO, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); /设置Time2中断向量相关;nvic.NVIC_IRQChannel = TIM2_IRQn;/ nvic.NVIC_IRQChannelPreemptionPriority = 0;nvic.NVIC_IRQChannelSubPriority
16、= 1;nvic.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&nvic); TIM_DeInit(TIM2); /设置Time2相关; time2.TIM_Prescaler = 0; time2.TIM_CounterMode = TIM_CounterMode_Up;time2.TIM_Period = 1000;time2.TIM_ClockDivision = TIM_CKD_DIV1;/TIM_TimeBaseStructure.TIM_RepetitionCounter TIM_TimeBaseInit(TIM2, & time2);TIM_Pre
17、scalerConfig(TIM2, 0x8c9F, TIM_PSCReloadMode_Immediate);TIM_ARRPreloadConfig(TIM2, DISABLE); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); /启动Time2;while(1)关于系统外设时钟的选择,可以参见相关手册,这是一个比较复杂的关系,这里给个简单的图:(由红色框图中得知,Time2采用的是APB1之后的时钟系统)由于本实验,涉及到中断,而且对于端口的操作,是在中断中进行的;其中中断函数名称的由来,在startup
18、_stm32f10x_hd.s(或其它)文件中,如图:中断程序如下:/-u8 ledflag = 0;void TIM2_IRQHandler(void) if(TIM_GetITStatus(TIM2, TIM_FLAG_Update) != RESET) TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update); if(ledflag+%2) GPIO_SetBits(GPIOB, GPIO_Pin_12); else GPIO_ResetBits(GPIOB, GPIO_Pin_12); 我想这么简单的中断程序应该没问题吧。2.3 Test注意A、学会使用
19、以前用过的模板,节省环境搭建时间;B、学会查看数据手册,理解外设系统时钟的选择原因;C、学会中断程序的编写,中断向量、函数的获取;3 USART Test(串口收发实验)只要是Project,只要是嵌入式开发,我想都免不了使用通讯,来和外界联系;只要使用通讯,最先想到的就是USART。这是一个很是常用的和非常好用的通讯方式。所有的处理器、控制器上,基本上都无一例外的增加USART功能。初学者在经历了环境搭建、GPIO、Time定时之后,就需要接触USART相关了。本实验需要实现:USART的中断接收和发送,实现将收到的数据回发出去。3.1 硬件设计查看开发板原理图,确定好通道连接情况:引脚是否
20、对,跳线是否短接。图中显示:使用PA9(TX)和PA10(RX)和STM32连接。3.2 软件设计由上述两个实验,我们可以总结出:1、外设时钟选择时钟是第一位的;2、只要使用到端口,就需要初始化端口;3、使用到其他外设,需要一一配置外设时钟;4、使用中断的,需要配置好中断向量,以及编写中断函数;本实验开始:#include stm32f10x.h#include main.h/#incude k_usart.h /已经转移到main函数中;#define VECT_TAB_RAMvoid main(void) GPIO_InitTypeDef GPIO_InitStructure; USART
21、_InitTypeDef USART_InitStructure; NVIC_InitTypeDef nvic; /USART_ClockInitTypeDef USART_InitClockStructure; SystemInit(); #ifdef VECT_TAB_RAM / Set the Vector Table base location at 0x20000000 NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); #else /* VECT_TAB_FLASH */ / Set the Vector Table base location
22、at 0x08000000 NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); #endif/ 以下是USART的配置部分 /- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); /- /GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPI
23、O_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); /- nvic.NVIC_IRQChannel = USART1_IRQn; nvic.NVIC_IRQChannelPreemptionPriority =
24、3; nvic.NVIC_IRQChannelSubPriority = 2; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); /- /USART_StructInit(&USART_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USAR
25、T_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_Init(USART1, &USART_InitStructure); /* USART_InitClockStructure.USART_CPOL = USART_CPOL_Low; USART_Ini
26、tClockStructure.USART_CPHA = USART_CPHA_2Edge; USART_InitClockStructure.USART_LastBit = USART_LastBit_Disable; USART_InitClockStructure.USART_Clock = USART_Clock_Disable; USART_ClockInit(USART1, &USART_InitClockStructure); */ USART_Cmd(USART1, ENABLE); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
27、/先使能接收中断; USART_ITConfig(USART1, USART_IT_TXE, DISABLE); /先禁止发送中断; while(1)/接下来就交给中断处理;USART1_IRQn中断函数在stm32f10x_it.c中:/include stdio.huint32_t RxBuffer;void USART1_IRQHandler(void) if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) RxBuffer = USART_ReceiveData(USART1); USART_ITConfig(USART1, USA
28、RT_IT_TXE, ENABLE); USART_ITConfig(USART1, USART_IT_RXNE, DISABLE); /printf(R); if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) USART_SendData(USART1, RxBuffer); USART_ITConfig(USART1, USART_IT_TXE, DISABLE); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); /printf(S); 详细细节在这里不做论述,可以从命名中得知一般配置usart的步骤;可以F12去探究其缘由,以及涉及的相关寄存器;3.3 Test注意1、在中断中,需要注意USART的中断条件,当发送中断ENABLE时,只要没有数据处理,就会进入中断,因此需要发送时才打开,不需要时,一定要关闭;2、还是那句话,先配置外设时钟;3、关于STM32中断相关配置,可以参考数据手册理解NVIC;4 USART DMA Test(串口DMA收发实验)5 Time3 PWM Test(Time3 PWM输出实验)6 I2C Test(I2C读写实验)7 ADC Text(AD转换实验)