stm32单片机教程怎样查看一格程序执行时间

STM32f103按键检测程序实现长按短按
20:31:37来源: eefocus 关键字:&&&&
背景只要使用,按键检测基本上是一定要实现的功能。按键检测要好用,最重要的是实时和去抖。初学者往往会在主循环调用(实时)并利用延时去抖(准确)。这种在主循环内延时的做法对整个程序非常不友好,也非常不高效。因此,本篇就我自己实现的一个检测按键并可判断按键是否长短按的程序做个介绍和记录。正文在硬件连接上,按键一端连接在普通IO口上,另一端接地,IO配置为内部弱上拉。在软件上,先配置一个5ms并打开中断,每进入该定时中断则置位一次“key_”。接着在主循环调用一个“scan_key()”函数,判断“key_handle”标志位是否在定时器内被置位,若被置位则将该位复位并读取连接按键的IO口值。此时,“scan_key()”函数内分为按键按下和松开两个分支:按键按下,则计数值“longkey”每隔5ms自加一次,因为这个分支每隔5ms才会进入执行一次;按键放开,则先判断“longkey”的值,若“longkey”的值换算出来代表按键按下时间在10ms-1s内,(10ms是去抖值,1s是与短按与长按的分界点。)则判断按键为短按;若“longkey”的值大于1s,则判断按键为长按。并将按键状态赋值给按键状态变量“keybuf”。同时,由于此时按键已经放开,因此“longkey”的值要置位“0”等待用户下次按下按键并执行从“0”开始的自加操作。若程序又一次进入按键检测代码段,说明所有功能块代码已经获知key状态,有对key感兴趣的代码段也肯定已经进行过相应处理,因此此时要及时将“keybuf”置为无按键按下状态以此来同步实际按键状态。实际代码如下:void&scan_key(void){&&&&unsigned&char&key_&&&&
&&&&if((longkey&==&0)&&&&(keybuf&!=&_KEYNULL)){&&&&&&&&//&在keybuf被标记为长按或短按后,若是按键已经松开,
&&&&&&&&//&则在主循环跑完一次后,及时将按键状态标记为无按键按下。
&&&&&&&&keybuf&=&_KEYNULL;
&&&&}&&&&if(key_handle&==&_ON)&{&//5ms进入一次
&&&&&&&&key_handle&=&_OFF;&&&&//&复位5ms标志位&
&&&&&&&&key_status&=&GPIO_ReadInputDataBit(KEYRESET_GPIO_PORT,&KEYRESET_GPIO_PIN);&&&&&&&&if(key_status&==&0)&{&&&//按下按键
&&&&&&&&&&&&longkey++;
&&&&&&&&}&&&&&&&&else&{&&//&松开按键
&&&&&&&&&&&&if((longkey&>=&3)&&&&(longkey&=&200)&{&//&2s
&&&&&&&&&&&&&&&&keybuf&=&_LONGKEY;
&&&&&&&&&&&&}&else&{
&&&&&&&&&&&&&&&&keybuf&=&_KEYNULL;//&若为扰动,按键状态也该为无按键按下
&&&&&&&&&&&&}
&&&&&&&&&&&&longkey&=&0;
}补充:和Tony~Liu讨论了下,发现其实还有效率更高,占用CPU更少的按键检测办法。在硬件上,按键连接在具有外部中断的IO口上,按键连接在该IO口上的脚外部上拉,按键另一端接地。在软件上,连接按键的IO口配置为双边沿中断,同时配置一个1ms。当按键按下时,触发外部中断,在外部中断内判断IO口电平,以此确定此为上升沿还是下降沿。下降沿则代表按键按下,开始计时,上升沿则代表按键松开,停止计时。上升沿中断时,在中断内置位“key_handle”,主循环在判断到“key_handle”被置位后,则开始判断计时器时间,若是时间在10ms-1s内,(10ms是去抖值,1s是与短按与长按的分界点。)则判断按键为短按;若时间大于1s,则判断按键为长按。具体实施也很简单,在此就不再贴代码,只是说说这种思想。至此,记录完毕
关键字:&&&&
编辑:什么鱼
引用地址:
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。
关注eeworld公众号快捷获取更多信息
关注eeworld服务号享受更多官方福利
热门关键词
大学堂最新课程
汇总了TI汽车信息娱乐系统方案、优质音频解决方案、汽车娱乐系统和仪表盘参考设计相关的文档、视频等资源
热门资源推荐
频道白皮书
何立民专栏
北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。21ic官方微信-->
ST MCU Finder
安装免费手机应用,
寻找理想的ST MCU
请完成以下验证码
使用IAR软件仿真STM32程序,如何知道每条指令运行的时间呢?
中级技术员, 积分 243, 距离下一级还需 57 积分
中级技术员, 积分 243, 距离下一级还需 57 积分
中级技术员, 积分 243, 距离下一级还需 57 积分
中级技术员, 积分 243, 距离下一级还需 57 积分
在哪个选项里面可以得到答案?我使用的是IAR&for&ARM&4.42版本的。
, , , , , , ,
由于STM32F内部有cache和总线仲裁机制,会导致CPU等待。
其指令运行时间不能象单片机一样简单的看出来。我搞手持万用在线编程机的Freescale&HCS08&BDM时序的时候,用示波器,花了不少功夫才搞明白这里面的名堂。最终还是用汇编语言,驯服了STM32时序这头烈马。
需要知道每条指令运行的时间,请看相关手册
在软件中仿真,不能得到准确结果;原因如2楼所说。
指令进入CACHE,就不能被打断的了!
至于总线仲裁,1.如果不开放DMA是可以避免的。2.在内核甚至CACHE内的指令流是不被打断的,在内核与cache也没有总线仲裁问题.因此还是可以大概看出来的。&&IAR有那个CYCLECOUNTER嘛!上次我就发了个贴关于这个问题。其实很简单。仿真时看看那个CPU寄存器栏就可以看到的。
更正 应该是进入流水线的指令不败打断。
流水线打不到断
至于中断的影响。
这个会有影响的。最明显的是CORTEX-M3的中断响应要:忘了,好像是6个时钟周期吧。比TI的C28X长。&原因可能是就是没有硬件堆栈。C28X有8级深度的硬件堆栈,专门用来保护&PC&。其他REG要自己根据情况&PUSH/POP到软件堆栈去。而ARM的要&STMFD&r13!,&{r4,&lr}LDMFD&r13!,&{r4,&pc}或是:PUSH&{r4,&lr}POP&{r4,&pc}
STM8的SWIM时序也搞定,呵呵。
扫描二维码,随时随地手机跟帖
核心会员奖章
等级类勋章
坚毅之洋流
发帖类勋章
时间类勋章
技术领袖奖章
人才类勋章
荣誉元老奖章
等级类勋章
您需要登录后才可以回帖1160人阅读
STM32F103单(17)
系统中断管理:NVIC
我的理解——管理系统内部的中断,负责打开和关闭中断。
基础应用 1,中断的初始化函数,包括设置中断向量表位置,和开启所需的中断两部分。所
有程序中必须的。
void NVIC_Configuration(void)
NVIC_InitTypeDef NVIC_InitS
VECT_TAB_RAM
更改内容的表格)
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
NVIC_PriorityGroup_x 可以是 0、1、2、3、4,分别代表抢占优先级有 1、2、4、8、16 个和
响应优先级有 16、8、4、2、1 个。规定两种优先级的数量后,所有的中断级别必须在其中
选择,抢占级别高的会打断其他中断优先执行,而响应级别高的会在其他中断执行完优先执
阅读 rcc:单片机时钟管理。
我的理解——管理外部、内部和外设的时钟,设置、打开和关闭这些时钟。
基础应用 1:时钟的初始化函数过程——
用法:void RCC_Configuration(void)
ErrorStatus HSEStartUpS
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if (HSEStartUpStatus == SUCCESS)
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div2);
RCC_PCLK1Config(RCC_HCLK_Div2);
USB,SPI,I2C,CAN,串口 2345,普通 TIM。
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET){}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08){}
1、阅读 exti:外部设备中断函数
我的理解——外部设备通过引脚给出的硬件中断,也可以产生软件中断,19 个上升、下降
或都触发。EXTI0~EXTI15 连接到管脚,EXTI 线 16 连接到 PVD(VDD 监视)
,EXTI 线 17 连接
到 RTC(闹钟),EXTI 线 18 连接到 USB(唤醒)。
基础应用 1,设定外部中断初始化函数。按需求,不是必须代码。
void EXTI_Configuration(void)
EXTI_InitTypeDef EXTI_InitS
EXTI_InitStructure.EXTI_Line =
通道 1|通道 2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_I
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_F
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
2、阅读 dma:通过总线而越过 CPU 读取外设数据
我的理解——通过 DMA 应用可以加速单片机外设、存储器之间的数据传输,并在传输期间
不影响 CPU 进行其他事情。这对于入门开发基本功能来说没有太大必要,这个内容先行跳
3、阅读 systic:系统定时器
我的理解——可以输出和利用系统时钟的计数、状态。
基础应用 1,精确计时的延时子函数。推荐使用的代码。
static vu32 TimingD
void SysTick_Config(void)
SysTick_CounterCmd(SysTick_Counter_Disable);
SysTick_ITConfig(DISABLE);
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
SysTick_SetReload(9000);
SysTick_ITConfig(ENABLE);
void Delay (u32 nTime)
SysTick_CounterCmd(SysTick_Counter_Enable);
TimingDelay = nT
while(TimingDelay != 0);
SysTick_CounterCmd(SysTick_Counter_Disable);
SysTick_CounterCmd(SysTick_Counter_Clear);
void TimingDelay_Decrement(void)
if (TimingDelay != 0x00)
TimingDelay‐‐;
注:建议熟练后使用,所涉及知识和设备太多,新手出错的可能性比较大。新手可用简化的
延时函数代替:
void Delay(vu32 nCount)
for(; nCount != 0; nCount‐‐);(循环变量递减计数)
当延时较长,又不需要精确计时的时候可以使用嵌套循环:
void Delay(vu32 nCount)
for(; nCount != 0; nCount‐‐)
{for (i=0; i&0xffff; i++)}
4、阅读 gpio:I/O 设置函数
我的理解——所有输入输出管脚模式设置,可以是上下拉、浮空、开漏、模拟、推挽模式,
频率特性为 2M,10M,50M。也可以向该管脚直接写入数据和读取数据。
基础应用 1,gpio 初始化函数。所有程序必须。
用法:void GPIO_Configuration(void)
GPIO_InitTypeDef GPIO_InitS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_标号
| GPIO_Pin_标号
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
FLATING。如果其中任意一行与前一组相应设置相同,那么那一行可以省略,由此推论如果
前面已经将此行参数设定为默认参数(包括使用 GPIO_InitTypeDef GPIO_InitStructure 代码),
本组应用也是默认参数的话,那么也可以省略。以下重复这个过程直到所有应用的管脚全部
被定义完毕。
基础应用 2,向管脚写入 0 或 1
用法:GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);
sw 笨笨的 STM32 笔记之七:让它跑起来,基本硬件
功能的建立
发表于 2009 年 02 月 27 日
实验之前的准备
接通串口转接器
下载 IO 与串口的原厂程序,编译通过保证调试所需硬件正常。
flash,lib,nvic,rcc 和 GPIO,基础程序库编写
这几个库函数中有一些函数是关于芯片的初始化的,每个程序中必用。为保障程序品
质,初学阶段要求严格遵守官方习惯。注意,官方程序库例程中有个 platform_config.h 文件,
是专门用来指定同类外设中第几号外设被使用,就是说在 main.c 里面所有外设序号用 x 代
替,比如 USARTx,程序会到这个头文件中去查找到底是用那些外设,初学的时候参考例程
别被这个所迷惑住。
全部必用代码取自库函数所带例程,并增加逐句注释。
习惯顺序——Lib(debug),RCC(包括 Flash 优化),NVIC,GPIO
必用模块初始化函数的定义:
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void Delay(vu32 nCount);
Main 中的初始化函数调用:
RCC_Configuration();
NVIC_Configuration();
GPIO_Configuration();
Lib 注意事项:
属于 Lib 的 Debug 函数的调用,应该放在 main 函数最开始,不要改变其位置。
RCC 注意事项:
Flash 优化处理可以不做,但是两句也不难也不用改参数......
根据需要开启设备时钟可以节省电能
时钟频率需要根据实际情况设置参数
NVIC 注意事项
注意理解占先优先级和响应优先级的分组的概念
GPIO 注意事项
注意以后的过程中收集不同管脚应用对应的频率和模式的设置。
作为高低电平的 I/O,所需设置:RCC 初始化里面打开 RCC_APB2
PeriphClockCmd(RCC_APB2Periph_GPIOA);GPIO 里面管脚设定:IO 输出(50MHz,Out_PP);
IO 输入(50MHz,IPU);
GPIO 应用 GPIO_WriteBit(GPIOB, GPIO_Pin_2, Bit_RESET);
GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);
GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6) ;
简单 Delay 函数
void Delay(vu32 nCount)
{for(; nCount != 0; nCount‐‐);}
初 始 化 函 数 里 添 加 : RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1
RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB , ENABLE);
不用其他中断,NVIC 初始化函数不用改
GPIO 初始化代码:
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
简单的延迟函数:
void Delay(vu32 nCount)
{ for (; nCount != 0; nCount‐‐);}
完成之后再在 main.c 的 while 里面写一段:
GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x01);
Delay(0xffff);
GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)0x00);
Delay(0xffff);
就可以看到连接在 PB2 脚上的 LED 闪烁了,单片机就跑起来了。
sw 笨笨的 STM32 笔记之八:来跟 PC 打个招呼,基本串口通讯
目的:在基础实验成功的基础上,对串口的调试方法进行实践。硬件代码顺利完成之
后,对日后调试需要用到的 printf 重定义进行调试,固定在自己的库函数中。
初始化函数定义:
USART_Configuration(void);
初始化函数调用:
UART_Configuration(void);
初始化代码:
USART_Configuration(void)
USART_InitTypeDef
USART_InitS
USART_InitStructure.USART_BaudRate
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_N
USART_InitStructure.USART_Mode
USART_Mode_Rx
USART_Mode_Tx;
x 发送功能
USART_Init(USART1,
&USART_InitStructure);
USART_Cmd(USART1,
RCC 中打开相应串口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1
GPIO 里面设定相应串口管脚模式
GPIO_InitStructure.GPIO_Pin
GPIO_Pin_9;
GPIO_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;
发送一位字符
USART_SendData(USART1,
while(USART_GetFlagStatus(USART1,
USART_FLAG_TXE)
接收一位字符
while(USART_GetFlagStatus(USART1,
USART_FLAG_RXNE)
(USART_ReceiveData(USART1));
发送一个字符串
先定义字符串:char
rx_data[250];
然后在需要发送的地方添加如下代码
while(rx_data!='\0')
{USART_SendData(USART1,
while(USART_GetFlagStatus(USART1,
USART_FLAG_TXE)
USART 注意事项:
发动和接受都需要配合标志等待。
只能对一个字节操作,对字符串等大量数据操作需要写函数
使用串口所需设置: RCC 初始化里面打开 RCC_APB2PeriphClockCmd
(RCC_APB2Periph_USARTx);GPIO 里面管脚设定:串口 RX ( 50Hz , IN_FLOATING );串口 TX ( 5
0Hz , AF_PP );
printf 函数重定义(不必理解,调试通过以备后用)
需要 c 标准函数:
粘贴函数定义代码
PUTCHAR_PROTOTYPE
__io_putchar(int
RCC 中打开相应串口
GPIO 里面设定相应串口管脚模式
增加为 putchar 函数。
putchar(int
'\n'){putchar('\r');}
USART_SendData(USART1,
while(USART_GetFlagStatus(USART1,
USART_FLAG_TXE)
通过,试验成功。 printf 使用变量输出 :%c 字符, %d 整数, %f 浮点数 ,%s 字符串,
/n 或 /r 为换行。注意:只能用于 main.c 中。
NVIC 串口中断的应用
目的:利用前面调通的硬件基础,和几个函数的代码,进行串口的中断输入练习。因
为在实际应用中,不使用中断进行的输入是效率非常低的,这种用法很少见,大部分串口的
输入都离不开中断。
初始化函数定义及函数调用:不用添加和调用初始化函数,在指定调试地址的时候已
经调用过,在那个 NVIC_Configuration 里面添加相应开中断代码就行了。
在串口初始化中 USART_Cmd 之前加入中断设置:
USART_ITConfig(USART1,
USART_IT_TXE,
接收中断, PE 奇偶错误中断,可以是多个。
RCC 、 GPIO 里面打开串口相应的基本时钟、管脚设置
NVIC 里面加入串口中断打开代码:
NVIC_InitTypeDef
NVIC_InitS
NVIC_InitStructure.NVIC_IRQChannel
USART1_IRQC
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority
NVIC_InitStructure.NVIC_IRQChannelSubPriority
NVIC_InitStructure.NVIC_IRQChannelCmd
NVIC_Init(&NVIC_InitStructure);
在 stm32f10x_it.c 文件中找到 void
USART1_IRQHandler 函数,在其中添入执行代码。
一般最少三个步骤:先使用 if 语句判断是发生那个中断,然后清除中断标志位,最后给字符
串赋值,或做其他事情。
USART1_IRQHandler(void)
(USART_GetITStatus(USART1,
USART_IT_RXNE)
GPIO_WriteBit(GPIOB,
GPIO_Pin_10,
(BitAction)0x01);
RX_dat=USART_ReceiveData(USART1)
USART_SendData(USART1,
while(USART_GetFlagStatus(USART1,
USART_FLAG_TXE)
中断注意事项:
可以随时在程序中使用 USART_ITConfig(USART1,
USART_IT_TXE,
DISABLE); 来关闭中断响应。
NVIC_InitTypeDef
NVIC_InitStructure 定义一定要加在 NVIC 初始化模块的第一句。
全局变量与函数的定义:在任意 .c 文件中定义的变量或函数,在其它 .c 文件中使用 extern+
定义代码再次定义就可以直接调用了。
sw 笨笨的 STM32 笔记之九:打断它来为我办事,EXI
(外部 I/O 中断)应用
目的:跟串口输入类似,不使用中断进行的 IO 输入效率也很低,而且可以通过 E
XTI 插入按钮事件,本节联系 EXTI 中断。
初始化函数定义:
EXTI_Configuration(void);
初始化函数调用:
EXTI_Configuration();
初始化函数:
EXTI_Configuration(void)
EXTI_InitTypeDef
EXTI_InitS
EXTI_ClearITPendingBit(EXTI_LINE_KEY_BUTTON);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,
GPIO_PinSource3);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,
GPIO_PinSource4);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,
GPIO_PinSource5);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,
GPIO_PinSource6);
EXTI_InitStructure.EXTI_Mode
EXTI_Mode_I
EXTI_InitStructure.EXTI_Line
EXTI_Line3
EXTI_Line4;
EXTI_InitStructure.EXTI_LineCmd
EXTI_Init(&EXTI_InitStructure);
RCC 初始化函数中开启 I/O 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
GPIO 初始化函数中定义输入 I/O 管脚。
GPIO_InitStructure.GPIO_Pin
GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode
GPIO_Mode_IPU;
GPIO_Init(GPIOA,
&GPIO_InitStructure);
在 NVIC 的初始化函数里面增加以下代码打开相关中断:
NVIC_InitStructure.NVIC_IRQChannel
EXTI9_5_IRQC
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority
NVIC_InitStructure.NVIC_IRQChannelSubPriority
NVIC_InitStructure.NVIC_IRQChannelCmd
NVIC_Init(&NVIC_InitStructure);
在 stm32f10x_it.c 文件中找到 void
USART1_IRQHandler 函数,在其中添入执行代
码。一般最少三个步骤:先使用 if 语句判断是发生那个中断,然后清除中断标志位,最后给
字符串赋值,或做其他事情。
if(EXTI_GetITStatus(EXTI_Line3)
断发生来源
EXTI_ClearITPendingBit(EXTI_Line3);
清除中断标志
USART_SendData(USART1,
/发送字符“a”
GPIO_WriteBit(GPIOB,
GPIO_Pin_2,
(BitAction)(1‐GPIO_ReadOutputDataBit(GPIOB,
中断注意事项:
中断发生后必须清除中断位,否则会出现死循环不断发生这个中断。然后需要对中断类型进
行判断再执行代码。
使用 EXTI 的 I/O 中断,在完成 RCC 与 GPIO 硬件设置之后需要做三件事:初始化 EXTI、NVIC 开中断、编写中断执行代码。
sw 笨笨的 STM32 笔记之十:工作工作,PWM 输出
目的:基础 PWM 输出,以及中断配合应用。输出选用 PB1,配置为 TIM3_CH4,
是目标板的 LED6 控制脚。
对于简单的 PWM 输出应用,暂时无需考虑 TIM1 的高级功能之区别。
初始化函数定义:
TIM_Configuration(void);
初始化函数调用:
TIM_Configuration();
初始化函数,不同于前面模块,TIM 的初始化分为两部分——基本初始化和通道
TIM_Configuration(void)
TIM_TimeBaseInitTypeDef
TIM_TimeBaseS
TIM_OCInitTypeDef
TIM_OCInitS
TIM_TimeBaseStructure.TIM_Period
TIM_TimeBaseStructure.TIM_Prescaler
TIM_TimeBaseStructure.TIM_ClockDivision
TIM_TimeBaseStructure.TIM_CounterMode
TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3,
&TIM_TimeBaseStructure);
TIM_ITConfig(TIM3,
TIM_IT_CC4,
TIM_OCStructInit(&
TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode
TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState
TIM_OutputState_E
需要 PWM 输出才需要这行代码
TIM_OCInitStructure.TIM_Pulse
TIM_OCInitStructure.TIM_OCPolarity
TIM_OCPolarity_H
TIM_OC4Init(TIM3,
&TIM_OCInitStructure);
TIM_Cmd(TIM3,
RCC 初始化函数中加入 TIM 时钟开启:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM3,
GPIO 里面将输入和输出管脚模式进行设置。信号:AF_PP,50MHz。
使用中断的话在 NVIC 里添加如下代码:
NVIC_InitStructure.NVIC_IRQChannel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority
NVIC_InitStructure.NVIC_IRQChannelSubPriority
NVIC_InitStructure.NVIC_IRQChannelCmd
NVIC_Init(&NVIC_InitStructure);
TIM2_IRQHandler(void)
(TIM_GetITStatus(TIM2,
TIM_IT_CC4)
TIM_ClearITPendingBit(TIM2,
TIM_IT_CC4);
GPIO_WriteBit(GPIOB,
GPIO_Pin_11,
(BitAction)(1‐GPIO_ReadOutputDataBit(GPIOB,
_Pin_11)));
TIM_GetCapture4(TIM2);
TIM_SetCompare4(TIM3,
管脚的 IO 输出模式是根据应用来定,比如如果用 PWM 输出驱动 LED 则应该将相应管脚设
为 AF_PP,否则单片机没有输出。
sw 笨笨的 STM32 笔记之十一:捕捉精彩瞬间,脉冲方波长度捕获
目的:基础 PWM 输入也叫捕获,以及中断配合应用。使用前一章的输出管脚 P
B1(19 脚),直接使用跳线连接输入的 PA3(13 脚),配置为 TIM2_CH4,进行实验。
对于简单的 PWM 输入应用,暂时无需考虑 TIM1 的高级功能之区别,按照目前
我的应用目标其实只需要采集高电平宽度,而不必知道周期,所以并不采用 PWM 输入模式,
而是普通脉宽捕获模式。
初始化函数定义:
TIM_Configuration(void);
初始化函数调用:
TIM_Configuration();
初始化函数,不同于前面模块,TIM 的 CAP 初始化分为三部分——计时器基本初
始化、通道初始化和时钟启动初始化:
TIM_Configuration(void)
TIM_TimeBaseInitTypeDef
TIM_TimeBaseS
TIM_ICInitTypeDef
TIM_ICInitS
TIM_TimeBaseStructure.TIM_Period
TIM_TimeBaseStructure.TIM_Prescaler
TIM_TimeBaseStructure.TIM_ClockDivision
TIM_TimeBaseStructure.TIM_CounterMode
TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2,
&TIM_TimeBaseStructure);
TIM_ICInitStructure.TIM_Channel
TIM_Channel_4;
TIM_ICInitStructure.TIM_ICPolarity
TIM_ICPolarity_F
TIM_ICInitStructure.TIM_ICSelection
TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler
TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter
稳定 0x0~0xF
TIM_ICInit(TIM2,
&TIM_ICInitStructure);
TIM_SelectInputTrigger(TIM2,
TIM_TS_TI2FP2);
TIM_SelectSlaveMode(TIM2,
TIM_SlaveMode_Reset);
TIM_SelectMasterSlaveMode(TIM2,
TIM_MasterSlaveMode_Enable);
TIM_ITConfig(TIM2,
TIM_IT_CC4,
TIM_Cmd(TIM2,
RCC 初始化函数中加入 TIM 时钟开启:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM3,
GPIO 里面将输入和输出管脚模式进行设置。IN_FLOATING,50MHz。
使用中断的话在 NVIC 里添加如下代码:
NVIC_InitStructure.NVIC_IRQChannel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority
NVIC_InitStructure.NVIC_IRQChannelSubPriority
NVIC_InitStructure.NVIC_IRQChannelCmd
TIM_GetCapture4(TIM2);
由于我的需求只跟高电平宽度有关,所以避免了使用 PWM 输入模式,这样可以
每个管脚捕捉一路信号。如果使用 PWM 模式,每一路需要占用两个寄存器,所以一个定时
器只能同时使用两路 PWM 输入。
由于捕捉需要触发启动定时器,所以 PWM 输出与捕捉不容易在同一个 TIM 通道
上实现。如果必须的话只能增加计数溢出的相关代码。
有些程序省略了捕捉通道的初始化代码,这是不对的
在基本计时器初始化代码里面注意选择适当的计数器长度,最好让波形长度不要
长于一个计数周期,否则需要增加溢出代码很麻烦。一个计数周期的长度计算跟如下几个参
RCC 初始化代码里面的 RCC_PCLKxConfig,这是 TIM 的基础时钟源与系统时钟
TIM 初始化的 TIM_Period,这是计数周期的值
TIM 初始化的 TIM_Prescaler,这是计数周期的倍频计数器,相当于调节计数
周期,可以使 TIM_Period 尽量大,提高计数精度。
sw 笨笨的 STM32 笔记之十二:时钟不息工作不止,
systic 时钟应用 a)
目的:使用系统时钟来进行两项实验——周期执行代码与精确定时延迟。
初始化函数定义:
SysTick_Configuration(void);
初始化函数调用:
SysTick_Configuration();
初始化函数:
SysTick_Configuration(void)
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
SysTick_SetReload(250000);
计数周期长度
SysTick_CounterCmd(SysTick_Counter_Enable);
SysTick_ITConfig(ENABLE);
在 NVIC 的初始化函数里面增加以下代码打开相关中断:
NVIC_SystemHandlerPriorityConfig(SystemHandler_SysTick,
高一些会少受其他影响
在 stm32f10x_it.c 文件中找到 void
SysTickHandler
SysTickHandler(void)
简单应用:精确延迟函数,因为 systic 中断往往被用来执行周期循环代码,所以
一些例程中使用其中断的启动和禁止来编写的精确延时函数实际上不实用,我自己编写了精
确计时函数反而代码更精简,思路更简单。思路是调用后,变量清零,然后使用时钟来的曾
变量,不断比较变量与延迟的数值,相等则退出函数。代码和步骤如下:
定义通用变量:u16
Tic_Val=0;
在 stm32f10x_it.c 文件中相应定义:
定义函数名称:void
Tic_Delay(u16
Tic_Count);
精确延时函数:
Tic_Delay(u16
Tic_Count)
Tic_Val=0;
while(Tic_Val
Tic_Count){printf("");}
在 stm32f10x_it.c 文件中 void
SysTickHandler
函数里面添加
Tic_Val++;
疑问:如果去掉计时行那个没用的 printf("");函数将停止工作,这个现象很奇
sw 笨笨的 STM32 笔记之十三:恶搞,两只看门狗
了解两种看门狗(我叫它:系统运行故障探测器和独立系统故障探测器,新手往往被这个并
不形象的象形名称搞糊涂)之间的区别和基本用法。
都是用来探测系统故障,通过编写代码定时发送故障清零信号(高手们都管这个代码叫做“喂
狗” ),告诉它系统运行正常。一旦系统故障,程序清零代码( “喂狗” )无法执行,其计数器就
会计数不止,直到记到零并发生故障中断(狗饿了开始叫唤),控制 CPU 重启整个系统(不
行啦,开始咬人了,快跑......)。
独立看门狗 Iwdg——我的理解是独立于系统之外,因为有独立时钟,所以不受系统影响的系
统故障探测器。(这条狗是借来的,见谁偷懒它都咬!)主要用于监视硬件错误。
窗口看门狗 wwdg——我的理解是系统内部的故障探测器,时钟与系统相同。如果系统时钟
不走了,这个狗也就失去作用了。
(这条狗是老板娘养的,老板不干活儿他不管!)主要用于
监视软件错误。
初始化函数定义:鉴于两只狗作用差不多,使用过程也差不多初始化函数栓一起了,
用的时候根据情况删减。
WDG_Configuration(void);
初始化函数调用:
WDG_Configuration();
初始化函数
WDG_Configuration()
WWDG_SetPrescaler(WWDG_Prescaler_8);
WWDG_SetWindowValue(65);
WWDG_Enable(127);
WWDG_ClearFlag();
WWDG_EnableIT();
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_32);
IWDG_SetReload(349);
IWDG_ReloadCounter();
IWDG_Enable();
RCC 初始化:只有软件看门狗需要时钟初始化,独立看门狗有自己的时钟不需要但是
需要 systic 工作相关设置。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,
独立看门狗使用 systic 的中断来喂狗,所以添加 systic 的中断打开代码就行了。软件看
门狗需要在 NVIC 打开中断添加如下代码:
NVIC_InitStructure.NVIC_IRQChannel
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority
NVIC_InitStructure.NVIC_IRQChannelSubPriority
NVIC_Init(&NVIC_InitStructure);
中断程序,软件看门狗在自己的中断中喂狗,独立看门狗需要使用 systic 的定时中断来
喂狗。以下两个程序都在 stm32f10x_it.c 文件中。
WWDG_IRQHandler(void)
WWDG_SetCounter(0x7F);
WWDG_ClearFlag();
SysTickHandler(void)
IWDG_ReloadCounter();
有狗平常没事情可以不理,但是千万别忘了喂它,否则死都不知道怎么死的!
初始化程序的调用一定要在 systic 的初始化之后。
独立看门狗需要 systic 中断来喂,但是 systic 做别的用处不能只做这件事,所以我写
了如下几句代码,可以不影响 systic 的其他应用,其他 systic 周期代码也可参考:
第一步:在 stm32f10x_it.c 中定义变量
Tic_IWDG++;
if(Tic_IWDG&=100)
IWDG_ReloadCounter();
Tic_IWDG=0;

我要回帖

更多关于 stm32程序执行过程 的文章

 

随机推荐