STM32f103C8T6入门学习记录

更新时间:2024-01-28 08:24:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

STM32 自 学 笔 记

作者:忙碌的小姚

新浪微博:@忙碌的小姚

新浪博客:

http://blog.sina.com.cn/mlxiaoyao222

目录

STM32 自 学 笔 记 ...................................................................................................................... 1 第一章 .............................................................................................................................................. 3 我与STM32 的那些日子 ............................................................................................................... 3 第二章 .............................................................................................................................................. 4

使用固件库建立一个工程 ....................................................................................................... 4

1、了解STM32F103的固件库 ...................................................................................... 4 2、创建第一个工程 ......................................................................................................... 4 3、接下来就是管理工程文件了 ..................................................................................... 9 4、编写main.c 和文件路径 ......................................................................................... 10

第三章 ............................................................................................................................................ 14

STM32点亮第一个LED 使用keil for ARM MDK 软件仿真 .......................................... 14

1、Main.c 函数代码:.................................................................................................. 14 2、代码分析: ............................................................................................................... 15 3、软件仿真介绍: ....................................................................................................... 16

第四章 ............................................................................................................................................ 19

串口的使用 ............................................................................................................................. 19

1、为什么要用串口? ................................................................................................... 19 2、STM32跟PC机(也就是电脑)如何连接 ........................................................... 19 3、代码分析 ................................................................................................................... 20 4、仿真及调试 ............................................................................................................... 23 5、串口接收数据 ........................................................................................................... 25

第五章 ............................................................................................................................................ 27

通用定时器的使用 ................................................................................................................. 27

1、STM32F103内部定时器有哪些? ......................................................................... 27 2、如何进行程序编写 ................................................................................................... 27 3仿真结果观察 .............................................................................................................. 30 4对第四章串口的补充 .................................................................................................. 31 5工程代码 ...................................................................................................................... 35

第一章

我与STM32 的那些日子

STM32这块板子是在阿莫上跟一个老师买的,砍了半天100块钱。包括一个Jlink v8仿真器(好像65块左右),和一块STM32系统板。那已是一年前的事了。

那时我刚大三,刚学了半年51,于是想学点更高级的。但我好像属于三分钟热度的这种人,买回来学了一个星期,就学不动了,寄存器操作,固件库的使用根本就没明白是怎么一回事,之后就没有然后了。

现在看到那块板子,总有一种说不出的滋味,要是当时能咬牙切齿努力学习,说不定现在也不会安静地坐在电脑前一字一句敲打这篇激励性文章了。对于STM32我没用任何基础,唯一有的也只是一年前学的那一个星期,不过那已不重要了,我现在仍是一个小白。作为一个初学者,也许是坐井观天,看的是片面的,可能有很多观点是错误的,希望读者朋友能勇于指出来。

-----前记

第二章

使用固件库建立一个工程

我打算先从软件编程学起,以前用过KEIL 和IAR,IAR我比较熟悉,因为参加电子设计竞赛的时候,就是用它编程MSP430程序的。但我还是选择了KEIL,也许因为资料比较多,或者别的吧,我没分析过KEIL 和IAR代码效率如何,因为这不是我现在要考虑的,软件都只是工具而已。

我去百度搜索了几篇如何在KEIL MDK环境下建立STM32工程,加上自己的实践。我也把我的初学者步骤贴出来。

1、了解STM32F103的固件库

我在网上下了STM32F103的固件库-----stm32f10x_stdperiph_lib 3.5V版的,里面内容如图所示,据说STM32F103和STM32F101都能用。

2、创建第一个工程

1.新建一个文件夹取名如 first_project,也可以建在桌面。

2.在里面新建 CMSIS ,libraries ,Libaries , List(用于链接的) , Obj(工程文件) , output (输出hex文件), User 文件夹;

3. 将固件库里 Libraries--->STM32F10x_StdPeriph_Driver 下的 inc 和src 文件夹复 制到fisrt_project 文件夹下的libraries 里;

将固件库里 Libraries--->CMSIS--->CM3--->CoreSupport文件夹里面的 core_cm3.c 和core_cm3.h 文件复制到我们新建的fisrt_project 文件夹下的CMSIS 里;

将固件库里 Project---->STM32F10x_StdPeriph_Template(这个文件家里有KEIL和 IAR建好工程的模板)里的stm32f10x_conf.h , stm32f10x_it.c ,

stm32f10x_it.h , system_stm32f10x.c 四个文件 复制到我们新建的fisrt_project 文件夹下的User 里;

如下图所示

打开keil uvision4 MDK , 在菜单栏里点击project 选择 new uVision project

保存工程名,选择Obj文件夹, 文件名为first_project ,不用加后缀格式,系统默认。

接下来会弹出让你选择器件的窗口,这里我们选择 STmicroelectronics 的 STM32F103C8

最后会弹出是否复制启动代码的对话框,选择YES;

3、接下来就是管理工程文件了

1、在project窗口右键点击Tagert 1 选择 manage conponents 或者

找到倒数第二个

品字形图标;

2、在Groups 框下面将Groups1 重命名为startup ,同时添加CMSIS, Libraries, User;

选择CMSIS 在Files框下将CMSIS文件夹的core_cm3.c添加进去;

选择Libraries在Files框下将Libraries--->src文件下的所以文件都加进去添加进去;

选择User在Files框下将User文件下的所以文件都加进去添加进去;

如下图所示:

点击OK,确定;

工程构架如下图所示:

4、编写main.c 和文件路径

1、新建main.c 保存到User文件夹里,将其添加到User工程名下。

#include \

int main(void)

{

while(1) {;}

}

2、在project框下,右键点击Target 1,选择Options for Target target1

在output 选项下,在create HEX file 前打上钩, 点击select Folder for Objects 选择输出文 件夹为 output文件夹;

在Listing选项下,点击select Folder for Listings 选择链接文 件夹为 List文件夹;

在C/C++选项下, define里 填入 USE_STDPERIPH_DRIVER,STM32F10X_MD 中间用英 文的逗号;(一定要记得)

Include Paths 里,点击右边的文件路径框 ,选择相应的CMSIS, libraries 里的INC 和SRC 还有 User ;

也可以填绝对路径,比如: C:\\Users\\Administrator\\Desktop\\STM32编程练习 \\fisrt_project\\CMSIS 这样比较麻烦

好了,这下开始编译链接:

0 Error , 0 Warring;

First_project 就建好了;万里长征的第一步,总算走出去了。

总结:关键是Option for target 的设置,包括output ,listing,和C/C++,尤其是C/C++这个选项里一定要设置对。

Define:USE_STDPERIPH_DRIVER,STM32F10X_MD,这是预定义的两个宏定义。 在stm32f10x.h这个头文件里搜索 USE_STDPERIPH_DRIVER 有这么一段 #ifdef USE_STDPERIPH_DRIVER #include \ #endif

据说是定义控制了是否在应用中启用外设驱动。

在stm32f10x.h这个头文件里搜索 STM32F10X_MD,

/* #define STM32F10X_MD */ /*!< STM32F10X_MD: STM32 Medium density devices */ STM32中密度器件

#ifdef STM32F10X_MD

ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */

USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */ USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */ CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */

: : :

#endif /* STM32F10X_MD */ 可能是定义中断向量的值吧

就写到这里,才发现自己做也许一个两个小时就能建好工程,但是要把写出来我却用了一个下午。为了大家跟我都尽快学会STM32,我会努力学习将成果与大家分享;

2013年12月25日

第三章

STM32点亮第一个LED 使用keil for ARM MDK 软件仿真

按照上一章建立的第一个工程模板,我们只需要在main函数里加入相关的点亮led灯的代码,并使用keil的软件仿真功能。

软件仿真在一定程度上可以在设计的初期发现程序的错误,减少我们下载到开发板的麻烦。但是纯软件的仿真的正确,也不能保证程序就正确。有时候仿真正确,实物却没这个效果。有时软件仿真结果是错误,你分析你的程序却没错误。

遇到多了,经验也就出来,前几天我仿真一个STM32的Systick ,仿真结果就不对,程序就是对的,我估计也许是软件本身的问题。

我建议用STM32+JLINK 进行仿真,有个硬件平台结果的正确性就有保证。

1、Main.c 函数代码:

#include \void delay(void) /* */ {

int i,j; for(i=1000;i>0;i--) for(j=1000;j>0;j--); }

void GPIO_Config(void) {

GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOB , &GPIO_InitStructure); }

int main(void) { GPIO_Config(); SystemInit(); while(1) { GPIO_SetBits(GPIOB,GPIO_Pin_5 ); //点亮PB.5 delay(); GPIO_ResetBits(GPIOB,GPIO_Pin_5 );//熄灭PB.5 delay(); GPIO_SetBits(GPIOB,GPIO_Pin_6 );//点亮PB.6 delay(); GPIO_ResetBits(GPIOB,GPIO_Pin_6 );//熄灭PB.6 delay(); } }

2、代码分析:

GPIO_InitTypeDef GPIO_InitStructure;

定义一个结构变量GPIO_InitStructure ,结构体类型为GPIO_InitTypeDef ,相当于int A ; GPIO_InitTypeDef =int GPIO_InitStructure=A一样。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 开启GPIOB的时钟,要使用某个管脚都需要开启该管脚的时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6; 管脚号设置为第五脚和第六脚

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Speed_10MHz, GPIO_Speed_2MHz, GPIO_Speed_50MH

设置管脚电平翻转的速率,比如不要求快速变化一般10M,做串口的时候设置较高波 特率的话就使用50MHZ的。

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

设置输出为推挽输出。 它有8中工作模式,输出只有两种推挽和开漏输出。其它可以参

考相关手册

GPIO_Init(GPIOB , &GPIO_InitStructure);

这一句就是将前面四句的设置初始化到GPIOB中;

SystemInit(); 将系统时钟设置为72M,不写这句,默认为8M; GPIO_SetBits(GPIOB,GPIO_Pin_5 ); //点亮PB.5 delay();// 延时 GPIO_ResetBits(GPIOB,GPIO_Pin_5 );//熄灭PB.5 delay(); GPIO_SetBits(GPIOB,GPIO_Pin_6 );//点亮PB.6 delay(); GPIO_ResetBits(GPIOB,GPIO_Pin_6 );//熄灭PB.6 delay();

编译链接,然后开始软件仿真

3、软件仿真介绍:

1、编译连接好后,点击debug 进入调试界面

2、调试窗口如下

3、

图片上加文字不好弄,大概就是复位、运行、停止、单步运行等我也不太熟悉,反正用到的时候试一试就知道了。

4、选择菜单栏上的 peripherals 按钮,选择general purpose I/O 的GPIOB

5、点击左上角的运行按钮 或者按F5键 就可以看到GPIOB的5,6 在一闪一闪的

6、顺便在看看波形仿真功能 点击下图的波形分析按钮

点击setup 按钮 会弹出一个窗口,在窗口的右上边,有个new的按钮,点击后 分别输入 PORTB.5 和PORTB.6

8、点击运行,将窗口调到合适的位置,最终波形图如下;

写得有些地方可能有错误,明天就要回四川了,有些急

2014年1月12日

第四章

串口的使用

1、为什么要用串口?

自上一篇写的时间是1月20号,今6月7号了,半年没更新了。

这半年发生了什么? 过完年就去找公司实习,在那里自我感觉进步很大。其实在公司大多都是自学,师傅基本不会给你说什么。但这并不能说明你的师傅对你不好,带我的那个师傅只比我高一届,但他的水平比我高的好多届。他也是自学,也没人告诉他该怎么做,因为老板也不太懂。 所以自学能力很重要,当然有人带你的话,这样会更好。

不说这些了,串口在调试的时候作用非常大。也学我们在学51的时候,只是将程序下载到开发板,看看是否能运行起来,通过数码管将结果显示出来,从而就知道程序设计的正确性。以前我也是这样做的,没什么不好。

在公司实习的时候,他们调试都是使用串口打印输出信息,观察程序从上电、初始化、运行数据什么的全部都显示到PC机上。然后再一句一句分析它的打印信息,从而找到出错的源头。这使我对串口的认识有更深了一步,所以我决定在学习STM32的时候,开发流程跟在公司学的方法一样——使用串口,观察打印信息。

2、STM32跟PC机(也就是电脑)如何连接

我的STM32F103C8T6只是裸板,没有串口芯片,当然用的也是学生机——笔记本电脑,同样也没串口。

解决办法1、买一块 MAX3232转接板+一条USB转串口线 +邮费=30块左右

2、只需要买一块PL2303的USB转接板。这样就将第一种的转接板和连接结合到一起了。

不过我用的是第一种,MAX3232+USB的串口线 ,为什么不用方便的2种? max3232对于没有串口的开发板可以充当电平转换芯片,如何开发板有了电平转换芯片,我便使用USB转串口线经行连接,这样便灵活了。 第二种只是用在既没转换芯片也没USB转串口的情况,不过对于最小系统板来说,它既可以下载程序,又可以当做串口来调试。 至于台式机就不需要USB转串口线了,普通串口线即可。 连接示意如下所示:

3、代码分析

再写这里之前,应该已经学过模块化编程了,STM32的每个XXX.c 和xxx.h 这都是模块化编程。良好的程序,与其好的代码风格有关。你的代码风格跟你接触教你写代码的有很大关系。 以前刚刚学习单片机编程,我还不信,现在我承认,跟教我单片机的老师风格有些像。

这里我做的是一个串口发送数据到PC机的例子: 要让STM32能够顺利发出数据,要进行如下配置

PA9,PA10管脚要配置, USART也需要配置波特率,数据有几位,停止位,数据流等。 USART和uart有什么区别 USART在做串口时,两者并不区别,但是USART有SPI的功能。还有串口通信为什么要配置波特率,停止位,硬件数据留这些,以前我也没想过,现在只觉得协议这个东西,是一个好的标准。 建立一个usart_debug.c的文本,内容如下 #include\

void GPIO_Configuration(void) {

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能UASRT的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA的时钟,开始的时候,我没用这句话,调了两天,跟源码一句一句比才知道 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_InitStructure.GPIO_Pin =GPIO_Pin_9;

GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz; //波特率较高,IO翻转需较高频率 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出;我看网上有人说设置成GPIO_Mode_Out_PP普通推挽输出也行,但实践出真知,我试了发送是乱码。; GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin =GPIO_Pin_10;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //配置成浮空输入,既然是输入所以就不用配置IO口的频率了

GPIO_Init(GPIOA,&GPIO_InitStructure); }

void USART1_config(void)

{

USART_InitTypeDef USART_InitStructure;

USART_InitStructure.USART_BaudRate= 115200; //配置波特率

USART_InitStructure.USART_WordLength = USART_WordLength_8b; //配置数据位 USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位

USART_InitStructure.USART_Parity= USART_Parity_No ; //奇偶校验位

USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None; //硬件流

USART_InitStructure.USART_Mode =USART_Mode_Rx |USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure);

USART_Cmd(USART1, ENABLE); }

/*这段很重要,如果要使用printf函数打印信息,需要加fputc函数,就需要对printf函数重定向到串口,以前工作中他们老是提重定向,什么串口重定向,USB重定向什么的,我也是云里雾里,如今给我的感觉就是将上层函数实现对底层硬件的操作*/ int fputc(int ch, FILE*f){

while(USART_GetFlagStatus(USART1, USART_FLAG_TC) !=SET); /*网上的一些函数里面是没有这一句代码,如果不加的话,打印时第一个字符就会没有,原因据说是硬件复位后,USART_FLAG_TC被置一了,而要发送数据必须让其为底才可以,一表示数据发送发出的标志,也可以用这样一句USART_ClearFlag(USART2,USART_FLAG_TC);清楚标志位。可是我没这样做一样发成功了,这个疑惑以后再想明白。*/

USART_SendData(USART1, (uint16_t)ch);

while(USART_GetFlagStatus(USART1, USART_FLAG_TC) !=SET); return ch;

}

void usart_debug_config(void) //提供给main函数调用的串口配置函数 {

GPIO_Configuration(); //IO口配置 USART1_config(); //串口配置 }

还就是usart_debug.h

#ifndef __usart_debug_H #define __usart_debug_H

#include \#include

void usart_debug_config(void); int fputc(int ch, FILE *f);

#endif /* __USART1_H */

main函数:

这里使用两种方式一种是 使用普通的方式发送,另一种使用printf函数

其实还有一种USART_printf函数来实现,这里不做介绍。有空看看区别一下printf和usart_printf,据说是支持格式多少的问题

#include\

#include\包含main函数里的调用函数 int main(void) { unsigned char TxBuf1[100] ={\发送字符串!!!\\r\\n\ int i; SystemInit(); usart_debug_config();

for( i = 0; TxBuf1[i] != '\\0'; i++) { while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);//这里跟分析fputc时是一样的 USART_SendData(USART1 , TxBuf1[i]);//发送字符数组里的单个字符 while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); } printf(\ 世界你好! \\r\\n\调用printf函数 while(1) { ; } }

4、仿真及调试

程序编好之后,在target option里要选择 USE microLIB (keil自带的微库),这是使用非标准C库,在编译链接是,将我们编写的fputc函数作为编译的首选,否则就会编译stdio.h里的fputc函数。

写到这里我发现我这个代码感觉不是很好,使用微库而不用标准C库,应该会有影响,我瞬间就明白了他们为什么要自己编写支持输出格式很少的的USART_printf函数了,后面目测我也会使用这个函数。

一个好的程序代码就是结构健全,BUG很少。把简单做到极致

下载后通过secureCRT或者串口小助手观察

在KEIL里继续软件仿真

仿真和下载时要注意的是,仿真要选USE Simulation 而下载要选右边的JTAG。。。。

然后点击debug 进入调试界面

view--->serial windos -->UART 1 全速运行 其结果如下:

串口发送数据就到此为止。

5、串口接收数据

这儿写的跟上面的已经没有关系了,不要搞混。 关于从PC机发送数据到STM32,这一部分则需要中断来实现,因为我们不需要时时刻刻都来检测外部是否发出数据给STM32,因此只要有数据来,就触发中断。这里就需要配置NVIC了

void NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //优先组为2 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //打开USART中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断通道使能 NVIC_Init(&NVIC_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //串口接收中断使能 USART_Cmd(USART1, ENABLE); }

其他配置比如串口复用到IO配置,串口时钟配置,和发送数据是一样的。

串口中断处理函数:

void USART1_IRQHandler(void)

{ int RX_status; //自己定义一个标志位

RX_status = USART_GetFlagStatus(USART1, USART_FLAG_RXNE);//读取接收数据标志位,如果装好了一帧数据则硬件将其置一。 if(RX_status == SET) { USART_SendData(USART1 , USART_ReceiveData(USART1));//将收到的数据再由STM32发送给PC机。 while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET);//等待发送完成。 } }

串口的发送接收大致如此,还是要搞懂什么数据位,硬件流,停止位,校验位什么,一个东西既然出现了就要好好分析出现的意义。想到了今天给电脑拆机清灰,电脑最好两年清一次。反正每次上螺丝都会多出那么一两个来,是不是可以不用要?

肯定不是,生产商肯定也知道节约成本什么,他们的结构工程师也知道PCB上开一个螺丝孔也是要收钱的。所以每一颗螺丝都有它的意义,所以什么校验位,停止位也有他的意义,即便我们不使用。 其实剩的螺丝我也扔了,完全不知道上在哪儿!

记于2014年6月8日 串口发送数据 百度云盘:http://pan.http://www.wodefanwen.com//s/1bn0ADLt 串口发送接收数据 百度云盘:http://pan.http://www.wodefanwen.com//s/1dD7YkZv

第五章

通用定时器的使用

1、STM32F103内部定时器有哪些?

STM32一共有8个通用16位Timer,其中TIMER1和TIMER8是高级定时器,其它的TIMER2~TIMER7是普通定时器。

此外还有一个Systick(系统滴答定时器),这个定时器通常在操作系统中作为系统的任务切换周期。

还有一个RTC,是一个毫秒定时器,支持秒级中断,用来做实时时钟计数器。 看门狗定时器 也可以算一个。

8个定时器中,Timer1 和Timer8是由APB2(输出最高频率为72MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为72MHz。Timer2~Timer7则是由APB1(输出最高频率为36MHZ)预分频后,再通过一个倍频器得到时钟频率,最高为36MHz。

2、如何进行程序编写

这里我通过定时器来控制一个LED亮0.5s 灭0.5s ,交替闪烁。当然要让定时器正常工作起来,还要配置中断NVIC。定时器计数到某个数,产生中断,从而进入中断服务程序,点亮LED灯。

main函数分析:

#include \

void GPIO_Config(void)//GPIO配置 {

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//使能gpioc的时 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //选择管脚PC.13作LED灯 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //管脚速度为50M GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //设置输出模式为推挽输出 GPIO_Init(GPIOC, &GPIO_InitStructure); //将上述设置写入到GPIOC里去 }

void NVIC_Config(void) //中断控制器的配置 {

NVIC_InitTypeDef NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //优先组设置 NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn ; //TIM2中断选通 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断控制 NVIC_Init(&NVIC_InitStructure); }

void Timer_Config(void) //定时器的配置 { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 , ENABLE); //Timer2 时钟使能 TIM_DeInit(TIM2); //复位TIM2定时器 TIM_TimeBaseStructure.TIM_Period=1000; //定时器周期 TIM_TimeBaseStructure.TIM_Prescaler=36000-1; //预分频数

TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //TIM2时钟分频,为1表示不分频

TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//定时器计数为向上计数模式

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器2的溢出标志位 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能定时器2溢出中断 TIM_Cmd(TIM2, ENABLE); //定时器2使能 }

int main(void) {

SystemInit();//初始化时钟,配置为72MHz,我试过将这句注释掉,好像不影响结果。查 了一下,在配置 //main函数之前的启动代码有这样一句 LDR R0, =SystemInit,我疑惑的是难 道启动的时候就配成72Mhz? GPIO_Config(); NVIC_Config(); Timer_Config(); while(1) { ; } }

中断服务函数

void TIM2_IRQHandler(void) {

static int flag_bit=0;//定义一个标志位

if ( TIM_GetITStatus(TIM2 , TIM_IT_Update) != RESET ) //判断中断溢出标志为是否为1 {

TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update); //清除溢出中断标志位 flag_bit = !flag_bit; if(flag_bit == 1) GPIO_SetBits(GPIOC, GPIO_Pin_13); //熄灭LED if(flag_bit == 0)

GPIO_ResetBits(GPIOC, GPIO_Pin_13); //点亮LED }

这个函数是写在stm32f10x_it.c 里面的,我对TIM2_IRQHandler()函数的理解应该是这样的:

首先由定时器定时,定时好了产生中断溢出标志位,发送中断 然后进入中断服务函数TIM2_IRQHandler(),进入函数之后要做的就是清除中断溢出 标志位。

最后再执行函数里的其他内容。 定时器定时时间计算是这两句:

TIM_TimeBaseStructure.TIM_Period=1000; //定时器周期 TIM_TimeBaseStructure.TIM_Prescaler=36000-1; //预分频数

Prescaler可以理解为定时器的基数是72M / Prescaler+1 = 2000k,也就是500us ,Period 可以理解为要计数多少次,这里是1000次。 所以就是每500us记一次,计数1000次,就是500ms。 公式为:

Period / (72M / (Prescaler+1) )=____ 秒 1000 / (72 M/ (35999+1) ) = 0.5 秒

我有的一些疑问:1、进入中断函数之后,定时器要干些什么,是不是就停止计数了? 2、计数记到1000发生中断,计数值是不是有自动清零 问题先放到这儿,边学习,边解决。

3仿真结果观察

前面第三章已经过如何仿真波形的步骤,可以参看前面。点击setup 按钮 会弹出一个窗口,在窗口的右上边,有个new的按钮,点击后输入 PORTC.13 -

仿真运行结果如下:

可以从仿真结果中观察到,方波的周期为一秒。占空比为0.5 ,跟预期一致。

4对第四章串口的补充

第四章介绍了串口的打印函数printf 是如何调用实现的。但要使用keil自带的微库microLIB ,那能不能不使用这个微库呢。我参照野火的教程,修改了程序,自己编写usart_printf()函数来实现打印的功能。

USRT1的配置不改变,主要的就是添加打印函数实现串口输出功能。代码感觉可能很长,但无非就是一些判断,看看字符串最后一位是不是\\0 ,很多时候我都没注意: 1、 这一句while ( *Data != 0) // 判断是否到达字符串结束符 我们平时不是都用 \\0 吗? 用0开始我还没反应过来。 其实ASCII的十六进制的0 就是\\0 如果要使用\\0,while ( *Data !='\\0') ,主要要加单引号表字符串,上面没有加就是十六进制,后面的也就能明白了。

2、stdarg.h这个函数,以前都没见过,但学习就是要学习新知识。 知识改变命运,我一直都相信这句!

3、char *itoa(int value, char *string, int radix) 是指针函数,返回值是一个地址

4、其他的都是涉及指针的操作,所以C指针一定要学好,没学好,不要紧,趁这个机会把它弄明白,当我看懂了下面这些,也就明白了。

usart_debug.h 文件修改:

#ifndef __usart_debug_H #define __usart_debug_H

#include \

#include //stdarg.h是C语言中C标准函数库的头文件,目的为让函数能够接收可变参数。

extern void usart_debug_config(void); //提供给外部函数调用usart_debug_config()函数。 /*

* 函数名:itoa

* 描述 :将整形数据转换成字符串

* 输入 :-radix =10 表示10进制,其他结果为0 * -value 要转换的整形数 * -buf 转换后的字符串 * -radix = 10 * 输出 :无 * 返回 :无

* 调用 :被USART1_printf()调用 */

static char *itoa(int value, char *string, int radix) {

int i, d; int flag = 0; char *ptr = string;

/* This implementation only works for decimal numbers. */ if (radix != 10) {

*ptr = 0; return string; }

if (!value) {

*ptr++ = 0x30; *ptr = 0; return string; }

/* if this is a negative value insert the minus sign. */ if (value < 0) {

*ptr++ = '-';

/* Make the value positive. */ value *= -1; }

for (i = 10000; i > 0; i /= 10) {

d = value / i;

if (d || flag) {

*ptr++ = (char)(d + 0x30); value -= (d * i); flag = 1; } }

/* Null terminate the string. */ *ptr = 0;

return string;

} /* NCL_Itoa */ /*

* 函数名:USART1_printf

* 描述 :格式化输出,类似于C库中的printf,但这里没有用到C库 * 输入 :-USARTx 串口通道,这里只用到了串口1,即USART1 * -Data 要发送到串口的内容的指针 * -... 其他参数 * 输出 :无 * 返回 :无

* 调用 :外部调用

* 典型应用USART1_printf( USART1, \ * USART1_printf( USART1, \ * USART1_printf( USART1, \ */

static void USART1_printf(USART_TypeDef* USARTx, uint8_t *Data,...) { const char *s; int d; char buf[16];

va_list ap; // va_list ap 和 va_start(ap, Data)以及后面的va_arg() 都来自 stdarg.h va_start(ap, Data);//具体用法请参照相关资料。 while ( *Data != 0) // 判断是否到达字符串结束符 { if ( *Data == 0x5c ) // '\\' ASCII表 0x5c就是转义字符'\\' { switch ( *++Data ) { case 'r': //回车符 USART_ClearFlag(USART2,USART_FLAG_TC); USART_SendData(USARTx, 0x0d); while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET ); Data ++; break; case 'n': //换行符 USART_ClearFlag(USART2,USART_FLAG_TC); USART_SendData(USARTx, 0x0a); while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET ); Data ++; break;

default: Data ++; break; } } else if ( *Data == '%') { // switch ( *++Data ) { case 's': //字符串 s = va_arg(ap, const char *); for ( ; *s; s++) { USART_ClearFlag(USART2,USART_FLAG_TC); USART_SendData(USARTx,*s); while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET ); } Data++; break;

case 'd': //十进制 d = va_arg(ap, int); itoa(d, buf, 10);

for (s = buf; *s; s++) { USART_ClearFlag(USART2,USART_FLAG_TC); USART_SendData(USARTx,*s); while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET ); } Data++; break; default: Data++; break; } } /* end of else if */ else { USART_ClearFlag(USART2,USART_FLAG_TC); USART_SendData(USARTx, *Data++); while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET ); }

while( USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET ); } }

#endif /* __USART1_H */

软件仿真中的效果图:

波形图 IO口状态 串口输出 都在下面图里了

5工程代码

定时器中断 百度云盘:http://pan.http://m.wodefanwen.com//s/1bn6a9Ef 定时器中断带串口 百度云盘:http://pan.http://m.wodefanwen.com//s/1gdeifYF 野火串口工程 百度云盘:http://pan.http://m.wodefanwen.com//s/1pJobkWN 记于 2014年6月18日

本文来源:https://www.bwwdw.com/article/vnlw.html

Top