你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

从滴答时钟了解STM32库操作

[复制链接]
kqh1120 发布时间:2015-1-7 12:50

# v/ X4 u. ?+ _  b* s1 j
基于库函数的开发模式,与基于API(Application Programming Interface)的软件开发有着异曲同工之处,程序员通过调用 API 函数对应用程序进行开发,而又无需访问源码,或理解内部工作机制的细节,可以减轻编程任务。STM32的基于函数库的开发模式也是一样的道理,因此对于有单片机开发经验的工程师来说,学习STM32,很容易就可以上手。虽然可以不考虑库函数内部的细节,不考虑如何实现硬件寄存器的配置,但是深入了解库函数对于提高编程能力是很有好处的,下面以系统滴答时钟为例,详解其工作流程。
滴答时钟是STM32内部的一个24位定时器,其操作相对简单,配置寄存器较少。大体的工作流程是这样的,定时器首先要有时钟源,时钟源配置好之后,设置定时时间,然后定时器启动,当定时时间到时,置位标志位,重载定时器初值,系统可采用查询标志位和中断两种工作方式做出相应的响应,下面来看看程序如何实现延时功能。
//初始化配置函数
Void Delay_Init()
{
RCC_ClocksTypeDef RCC_ClocksStatus;
RCC_GetClocksFreq(&RCC_ClocksStatus);//获取时钟频率
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//时钟源配置为系统主时钟频率/8
SysTick_ITConfig(DISABLE);//不使能中断,采用查询方式
delay_fac_us = RCC_ClocksStatus.HCLK_Frequency / 8000000;// 1us的定时初值
}
//实现延时Nus的延时功能
void Delay_us(u32 Nus)
{
SysTick_SetReload(delay_fac_us * Nus);//载入初值
SysTick_CounterCmd(SysTick_Counter_Clear);//计数器清零
SysTick_CounterCmd(SysTick_Counter_Enable);//计数器开始计数
do
{
Status = SysTick_GetFlagStatus(SysTick_FLAG_COUNT);
}while (Status != SET);//不断查询标志位,当载入初值与计数器相等时,标志位置位。
SysTick_CounterCmd(SysTick_Counter_Disable);//关闭计数器
SysTick_CounterCmd(SysTick_Counter_Clear);//清零计数器
}
//实现闪灯
Delay_Init();
While(1)
{
LED1(ON);
Delay_us(500000);//延时500ms
LED1(OFF);
}
下面来看看库函数如何实现相应的寄存器配置。
void SysTick_ITConfig(FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
SysTick->CTRL |= CTRL_TICKINT_Set;
}
else
{
SysTick->CTRL &= CTRL_TICKINT_Reset;
}
}
这个函数的作用是配置寄存器开启/关闭中断,FunctionalState是自定义的数据类型,是一个枚举类型,typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
枚举类型是一种基本数据类型而不是构造类型,它用于声明一组命名的常数,将变量的值一一列出来,变量的值只限于列举出来的值的范围内,因此当一个变量有几种可能的取值时,可以将它定义为枚举类型。
assert_param(IS_FUNCTIONAL_STATE(NewState));
这句话的作用是判断参数NewState的值是否正确,如果发现参数出错,它会调用函数assert_failed()向程序员报告错误。
void assert_failed(uint8_t* file, uint32_t line)
{
while (1)
{
}
SysTick->CTRL |= CTRL_TICKINT_Set;这句话就是用来配置寄存器的语句, SysTick是系统定义的一个结构体如下,SysTick->CTRL即为滴答时钟的控制寄存器。
( [3 k/ }% c. I  R: \8 O
收藏 评论2 发布时间:2015-1-7 12:50

举报

2个回答
kqh1120 回答时间:2015-1-7 12:50:48
typedef struct
0 Q" S  z2 s1 |/ I% J# q4 D* Q7 I; }0 ~0 P
{. P5 ^0 o5 j( E3 n7 j* T; O" O
. q2 C* x5 e( H
__IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */8 b* z# U- X; b/ Y* o
; b# J  [! g# v
__IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
& _1 u0 a6 A1 X' r% i9 K4 L$ _; b$ s! o
__IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
7 ^/ p/ `# O6 Z" \5 A, z* S2 s# o4 x) Y; M" B1 o- L0 w- H/ h
__I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
! E- C3 w5 D# Y! ~7 p  g, k( ]
3 Z  f) S0 f! `' a} SysTick_Type; //声明一个SysTick_Type型的结构体。$ Q: i( K* u, t, A+ E
" Z7 @; t3 ]! s# |6 R4 o% I
#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */9 l; |2 g& h) g7 L
8 K0 e& {' l. c  W, u8 r* T% Z# h7 D
#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */3 H& T) s3 Y. z) H
5 f' n; U! ^' R! `
#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */
, {# G1 l* O( M& B" W
; ?8 c! u& t& h' R  C定义一个SysTick_Type类型的结构体实例SysTick,而从根本上来说这是一个地址,就是STM32芯片内部分配给滴答时钟的实际地址0xE000E000UL+0x0010UL。7 v( Q5 _7 @' X: S1 e
8 H3 @8 t1 @% J. ~7 W7 u; f* b
CTRL_TICKINT_Set是一个宏定义,定义如下
0 ]7 x  Q3 a4 V" H1 D5 M' t6 z  E* p! E' [# m
/* CTRL TICKINT Mask */
+ t, I7 `* F/ |1 }8 f) M7 x2 x+ J% i
( G) s6 F+ U+ G) R#define CTRL_TICKINT_Set ((u32)0x00000002)
* e4 B" n% _4 V# C4 I/ l( }  M+ b4 V1 T$ q/ [6 ~
#define CTRL_TICKINT_Reset ((u32)0xFFFFFFFD)
7 P' v( H2 t* r( W  b5 e4 m" E/ ?5 N5 C  A3 ]
至此,SysTick->CTRL |= CTRL_TICKINT_Set;这句话的意义已经很清晰了,就是给地址0xE000E000+0x0010 +0x000赋一个0x00000002的值,对应滴答时钟的CTRL寄存器的第2位置1。即为开启中断的意思。
5 Z) x+ D1 x* ~% ?2 a: P5 N5 a$ ^8 Q; o6 w: ~7 c# s
上面讲的是用查询的方式,下面再说下中断触发。只需调用下面这个函数即可完成中断的设置。
  [2 e& G; @* Y5 m9 D8 N
% Q% O) \9 c" L, o# USysTick_Config(uint32_t ticks);具体实现如下:
) S" z( `, K8 p# M& }6 ~. J0 q0 w. t" u6 X* Q/ x2 s* t; D
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)  \' r. x* d$ A) |- @

8 P, j; d! D2 d( t- `{! ]9 O2 ?- e& ]$ ]. l# B  ]

/ }! G& h2 \* m. M6 _4 ?& h& p* M; Cif ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1);
6 S7 b; x, a$ ]$ A! G8 [' g$ J- w* z. Q* O6 H* D! V) X) H3 t
SysTick->LOAD = ticks - 1;
) s9 E1 G- w" T/ D( p# U7 l. a! \' c: `7 d
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
& s1 s- a9 y& j. r" `' A) Z( ^5 q
SysTick->VAL = 0;
; D% @: Z( \% v& x- x2 Q% {5 Z
, O1 u0 E1 }7 O. G( n7 m# cSysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |SysTick_CTRL_TICKINT_Msk |SysTick_CTRL_ENABLE_Msk;
- F0 f* H, f9 i4 U  ?' c4 |* E
% t9 r! }& r0 V2 Ereturn (0);( p& W8 I( ^7 p  d- _8 b
+ J6 S* K) K4 r# O: u+ f
}
( {! h5 L. u& d/ X) W, N3 b5 h/ p% P' \( s4 S
函数的参数为ticks,是要装入寄存器SysTick->LOAD的计数值,如果系统时钟为72M,把ticks赋值为SystemFrequency/10000,表示计数到720个时钟周期产生一次中断,而一个时钟周期的时间为(1/72)us,所以720x(1/72)=10us,也就实现了定时10us的功能。
2 {: u- v+ ?5 U6 O
+ }4 v) f; X5 V- N0 y8 FNVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);为SysTick中断设置优先级。将寄存器SysTick->VAL的值清0。然后使能中断,使能SysTick定时器,时钟源选择为AHB时钟。当定时时间到时,进入中断函数$ q; N9 n! m7 B& |; @, Z+ {
4 `0 W) A  l5 S6 t+ ]
void SysTick_Handler(void)
6 B$ D. J" e5 d: I" C8 j, U" Q7 z
{
7 c2 b  z2 u" ^. A- `1 ?- C: E3 [7 m: a  ?6 m, p
//具体函数实现由用户编写。
: V$ x% Z! S% @( V
1 `" a! g( z$ D/ l* c}. y1 W, v- c# x6 Y: K

5 W, P9 S1 `, i1 D/ n* @通过对这样一个简单定时器的操作,我们可以初步了解到STM32库函数的使用方法
+ O* T  e  X/ V0 U+ y. x. ?! `+ y* s* W
AndyYau-268557 回答时间:2015-1-7 13:43:18
嗯,库函数比较简单直观

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版