历史上的今天
返回首页

历史上的今天

今天是:2024年09月29日(星期日)

正在发生

2019年09月29日 | 再造STM32---第六部分:自己写库—构建库函数雏形

2019-09-29 来源:eefocus

       本章参考资料: 《STM32F4xx 中文参考手册》 、 《STM32F429 规格书》


       虽然我们上面用寄存器点亮了 LED,乍看一下好像代码也很简单,但是我们别侥幸以后就可以一直用寄存器开发。在用寄存器点亮 LED 的时候,我们会发现 STM32 的寄存器都是 32 位的,每次配置的时候都要对照着《STM32F4xx 参考手册》中寄存器的说明,然后根据说明对每个控制的寄存器位写入特定参数,因此在配置的时候非常容易出错,而且代码还很不好理解,不便于维护。所以学习 STM32 最好的方法是用软件库,然后在软件库的基础上了解底层,学习遍所有寄存器。


本讲建议看火哥视频,很重要。

6.1 什么是 STM32 函数库:

       以上所说的软件库是指“STM32 标准函数库”,它是由 ST 公司针对 STM32 提供的函数接口,即 API (Application Program Interface),开发者可调用这些函数接口来配置 STM32的寄存器,使开发人员得以脱离最底层的寄存器操作,有开发快速,易于阅读,维护成本低等优点。


       当我们调用库 API 的时候不需要挖空心思去了解库底层的寄存器操作,就像当年我们刚开始学习 C 语言的时候,用 prinft()函数时只是学习它的使用格式,并没有去研究它的源码实现, 但需要深入研究的时候,经过千锤百炼的库 API 源码就是最佳学习范例。


       实际上, 库是架设在寄存器与用户驱动层之间的代码,向下处理与寄存器直接相关的配置,向上为用户提供配置寄存器的接口。 库开发方式与直接配置寄存器方式的区别见图6-1。

6.2 为什么采用库来开发及学习?

       在以前 8 位机时代的程序开发中, 一般直接配置芯片的寄存器,控制芯片的工作方式,如中断,定时器等。配置的时候, 常常要查阅寄存器表,看用到哪些配置位,为了配置某功能,该置 1 还是置 0。这些都是很琐碎的、机械的工作,因为 8 位机的软件相对来说较简单,而且资源很有限,所以可以直接配置寄存器的方式来开发。


       对于 STM32,因为外设资源丰富,带来的必然是寄存器的数量和复杂度的增加,这时直接配置寄存器方式的缺陷就突显出来了:

(1) 开发速度慢

(2) 程序可读性差

(3) 维护复杂

       这些缺陷直接影响了开发效率,程序维护成本,交流成本。库开发方式则正好弥补了这些缺陷。

       而坚持采用直接配置寄存器的方式开发的程序员,会列举以下原因:

(1) 具体参数更直观

(2) 程序运行占用资源少

       相对于库开发的方式,直接配置寄存器方式生成的代码量的确会少一点,但因为STM32 有充足的资源,权衡库的优势与不足,绝大部分时候,我们愿意牺牲一点 CPU 资源,选择库开发。一般只有在对代码运行时间要求极苛刻的地方,才用直接配置寄存器的方式代替,如频繁调用的中断服务函数。

       对于库开发与直接配置寄存器的方式,就好比编程是用汇编好还是用 C 好一样。在STM32F1 系列刚推出函数库时引起程序员的激烈争论,但是,随着 ST 库的完善与大家对库的了解,更多的程序员选择了库开发。 现在 STM32F1 系列和 STM32F4 系列各有一套自己的函数库,但是它们大部分是兼容的, F1 和 F4 之间的程序移植,只需要小修改即可。而如果要移植用寄存器写的程序,我只想说:“呵呵”。

       用库来进行开发,市场已有定论,用户群说明了一切,但对于 STM32 的学习仍然有人认为用寄存器好,而且汇编不是还没退出大学教材么?认为这种方法直观,能够了解到是配置了哪些寄存器,怎样配置寄存器。事实上,库函数的底层实现恰恰是直接配置寄存器

方式的最佳例子,它代替我们完成了寄存器配置的工作,而想深入了解芯片是如何工作的话,只要直接查看库函数的最底层实现就能理解,相信你会为它严谨、优美的实现方式而陶醉, 要想修炼 C 语言,就从 ST 的库开始吧。 所以在以后的章节中,使用软件库是我们的重点,而且我们通过讲解库 API 去高效地学习 STM32 的寄存器,并不至于因为用库学习,就不会用寄存器控制 STM32 芯片。


6.3 实验:构建库函数雏形:

       虽然库的优点多多,但很多人对库还是很忌惮,因为一开始用库的时候有很多代码,很多文件,不知道如何入手。不知道您是否认同这么一句话:一切的恐惧都来源于认知的空缺。我们对库忌惮那是因为我们不知道什么是库,不知道库是怎么实现的。

       接下来,我们在寄存器点亮 LED 的代码上继续完善,把代码一层层封装,实现库的最初的雏形,相信经过这一步的学习后,您对库的运用会游刃有余。这里我们只讲如何实现GPIO 函数库,其他外设的我们直接参考 ST 标准库学习即可,不必自己写。

       下面请打开本章配套例程“构建库函数雏形”来阅读理解,该例程是在上一章的基础上修改得来的。



6.3.1 修改寄存器地址封装:


        上一章中我们在操作寄存器的时候,操作的是都寄存器的绝对地址,如果每个外设寄存器都这样操作,那将非常麻烦。我们考虑到外设寄存器的地址都是基于外设基地址的偏移地址,都是在外设基地址上逐个连续递增的,每个寄存器占 32 个或者 16 个字节,这种方式跟结构体里面的成员类似。所以我们可以定义一种外设结构体,结构体的地址等于外设的基地址,结构体的成员等于寄存器,成员的排列顺序跟寄存器的顺序一样。这样我们操作寄存器的时候就不用每次都找到绝对地址,只要知道外设的基地址就可以操作外设的全部寄存器,即操作结构体的成员即可。

       在工程中的“stm32f4xx.h”文件中,我们使用结构体封装 GPIO 及 RCC 外设的的寄存器,见代码清单 6-1。结构体成员的顺序按照寄存器的偏移地址从低到高排列,成员类型跟寄存器类型一样。如不理解 C 语言对寄存器的封的语法原理,请参考《C 语言对寄存器的封装》 小节。


代码清单6-1 封装寄存器列表


//volatile 表示易变的变量,防止编译器优化

#define __IO volatile

typedef unsigned int uint32_t;

typedef unsigned short uint16_t;

/* GPIO 寄存器列表 */

typedef struct {

__IO uint32_t MODER; /*GPIO 模式寄存器 地址偏移: 0x00 */

__IO uint32_t OTYPER; /*GPIO 输出类型寄存器 地址偏移: 0x04 */

__IO uint32_t OSPEEDR; /*GPIO 输出速度寄存器 地址偏移: 0x08 */

__IO uint32_t PUPDR; /*GPIO 上拉/下拉寄存器 地址偏移: 0x0C */

__IO uint32_t IDR; /*GPIO 输入数据寄存器 地址偏移: 0x10 */

__IO uint32_t ODR; /*GPIO 输出数据寄存器 地址偏移: 0x14 */

__IO uint16_t BSRRL; /*GPIO 置位/复位寄存器低 16 位部分 地址偏移: 0x18 */

__IO uint16_t BSRRH; /*GPIO 置位/复位寄存器 高 16 位部分地址偏移: 0x1A */

__IO uint32_t LCKR; /*GPIO 配置锁定寄存器 地址偏移: 0x1C */

__IO uint32_t AFR[2]; /*GPIO 复用功能配置寄存器 地址偏移: 0x20-0x24 */

} GPIO_TypeDef;

/*RCC 寄存器列表*/

typedef struct {

__IO uint32_t CR; /*!< RCC 时钟控制寄存器,地址偏移: 0x00 */

__IO uint32_t PLLCFGR; /*!< RCC PLL 配置寄存器,地址偏移: 0x04 */

__IO uint32_t CFGR; /*!< RCC 时钟配置寄存器,地址偏移: 0x08 */

__IO uint32_t CIR; /*!< RCC 时钟中断寄存器,地址偏移: 0x0C */

__IO uint32_t AHB1RSTR; /*!< RCC AHB1 外设复位寄存器,地址偏移: 0x10 */

__IO uint32_t AHB2RSTR; /*!< RCC AHB2 外设复位寄存器,地址偏移: 0x14 */

__IO uint32_t AHB3RSTR; /*!< RCC AHB3 外设复位寄存器,地址偏移: 0x18 */

__IO uint32_t RESERVED0; /*!< 保留, 地址偏移: 0x1C */

__IO uint32_t APB1RSTR; /*!< RCC APB1 外设复位寄存器,地址偏移: 0x20 */

__IO uint32_t APB2RSTR; /*!< RCC APB2 外设复位寄存器,地址偏移: 0x24*/

__IO uint32_t RESERVED1[2]; /*!< 保留,地址偏移: 0x28-0x2C*/

__IO uint32_t AHB1ENR; /*!< RCC AHB1 外设时钟寄存器,地址偏移: 0x30 */

__IO uint32_t AHB2ENR; /*!< RCC AHB2 外设时钟寄存器,地址偏移: 0x34 */

__IO uint32_t AHB3ENR; /*!< RCC AHB3 外设时钟寄存器,地址偏移: 0x38 */

/*RCC 后面还有很多寄存器,此处省略*/

} RCC_TypeDef;

        这段代码在每个结构体成员前增加了一个“__IO”前缀,它的原型在这段代码的第一行,代表了 C 语言中的关键字“volatile”,在 C 语言中该关键字用于表示变量是易变的,要求编译器不要优化。这些结构体内的成员,都代表着寄存器,而寄存器很多时候是由外设或 STM32 芯片状态修改的,也就是说即使 CPU 不执行代码修改这些变量,变量的值也有可能被外设修改、更新,所以每次使用这些变量的时候,我们都要求 CPU 去该变量的地址重新访问。若没有这个关键字修饰,在某些情况下,编译器认为没有代码修改该变量,就直接从 CPU 的某个缓存获取该变量值,这时可以加快执行速度,但该缓存中的是陈旧数据,与我们要求的寄存器最新状态可能会有出入。



6.3.2 定义访问外设的结构体指针:

       以结构体的形式定义好了外设寄存器后,使用结构体前还需要给结构体的首地址赋值,才能访问到需要的寄存器。为方便操作,我们给每个外设都定义好指向它地址的结构体指针,见代码清单 6-2。

代码清单 6-2 指向外设首地址的结构体指针


/*定义 GPIOA-H 寄存器结构体指针*/

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)

#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)

#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)

#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)

#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)

#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)

#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE)

/*定义 RCC 外设 寄存器结构体指针*/

#define RCC ((RCC_TypeDef *) RCC_BASE)

        这些宏通过强制把外设的基地址转换成 GPIO_TypeDef 类型的地址,从而得到 GPIOA、GPIOB 等直接指向对应外设的指针,通过结构体的指针操作,即可访问对应外设的寄存器。利用这些指针访问寄存器,我们把 main 文件里对应的代码修改掉,见代码清单 6-3。

代码清单 6-3 使用结构体指针方式控制 LED 灯

 


/**

* 主函数

*/

int main(void)

{

RCC->AHB1ENR |= (1<<7);

/* LED 端口初始化 */

/*GPIOH MODER10 清空*/

GPIOH->MODER &= ~( 0x03<< (2*10));

/*PH10 MODER10 = 01b 输出模式*/

GPIOH->MODER |= (1<<2*10);

/*GPIOH OTYPER10 清空*/

GPIOH->OTYPER &= ~(1<<1*10);

/*PH10 OTYPER10 = 0b 推挽模式*/

GPIOH->OTYPER |= (0<<1*10);

/*GPIOH OSPEEDR10 清空*/

GPIOH->OSPEEDR &= ~(0x03<<2*10);

/*PH10 OSPEEDR10 = 0b 速率 2MHz*/

GPIOH->OSPEEDR |= (0<<2*10);

/*GPIOH PUPDR10 清空*/

GPIOH->PUPDR &= ~(0x03<<2*10);

/*PH10 PUPDR10 = 01b 上拉模式*/

GPIOH->PUPDR |= (1<<2*10);

/*PH10 BSRR 寄存器的 BR10 置 1,使引脚输出低电平*/

GPIOH->BSRRH |= (1<<10);

/*PH10 BSRR 寄存器的 BS10 置 1,使引脚输出高电平*/

//GPIOH->BSRRL |= (1<<10);

while (1);

}

       乍一看,除了最后一部分,把 BSRR 寄存器分成 BSRRH 和 BSRRL 两段,其它部分跟直接用绝对地址访问只是名字改了而已,用起来跟上一章没什么区别。这是因为我们现在只实现了库函数的基础,还没有定义库函数。

       打好了地基,下面我们就来建高楼。接下来使用函数来封装 GPIO 的基本操作,方便以后应用的时候不需要再查询寄存器,而是直接通过调用这里定义的函数来实现。我们把针对 GPIO 外设操作的函数及其宏定义分别存放在“stm32f4xx_gpio.c”和“stm32f4xx_gpio.h”文件中。

       定义位操作函数

       在“stm32f4xx_gpio.c”文件定义两个位操作函数,分别用于控制引脚输出高电平和低电平,见代码清单 6-4。

代码清单 6-4 GPIO 置位函数与复位函数的定义

 


/**

*函数功能:设置引脚为高电平

*参数说明: GPIOx:该参数为 GPIO_TypeDef 类型的指针,指向 GPIO 端口的地址

* GPIO_Pin:选择要设置的 GPIO 端口引脚,可输入宏 GPIO_Pin_0-15,

* 表示 GPIOx 端口的 0-15 号引脚。

*/

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

{

/*设置 GPIOx 端口 BSRRL 寄存器的第 GPIO_Pin 位,使其输出高电平*/

/*因为 BSRR 寄存器写 0 不影响,

宏 GPIO_Pin 只是对应位为 1,其它位均为 0,所以可以直接赋值*/

GPIOx->BSRRL = GPIO_Pin;

}

/**

*函数功能:设置引脚为低电平

*参数说明: GPIOx:该参数为 GPIO_TypeDef 类型的指针,指向 GPIO 端口的地址

* GPIO_Pin:选择要设置的 GPIO 端口引脚,可输入宏 GPIO_Pin_0-15,

* 表示 GPIOx 端口的 0-15 号引脚。

*/

void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

{

/*设置 GPIOx 端口 BSRRH 寄存器的第 GPIO_Pin 位,使其输出低电平*/

/*因为 BSRR 寄存器写 0 不影响,

宏 GPIO_Pin 只是对应位为 1,其它位均为 0,所以可以直接赋值*/

GPIOx->BSRRH = GPIO_Pin;

}

       这两个函数体内都是只有一个语句,对 GPIOx 的 BSRRL 或 BSRRH 寄存器赋值,从而设置引脚为高电平或低电平。其中 GPIOx 是一个指针变量,通过函数的输入参数我们可以修改它的值,如给它赋予 GPIOA、 GPIOB、 GPIOH 等结构体指针值,这个函数就可以控制相应的 GPIOA、 GPIOB、 GPIOH 等端口的输出。

       对比我们前面对 BSRR 寄存器的赋值,都是用“|=”操作来防止对其它数据位产生干扰的,为何此函数里的操作却直接用“=”号赋值,这样不怕干扰其它数据位吗?见代码清单 6-5。

代码清单 6-5 赋值方式对比


/*使用 “|=” 来赋值*/

GPIOH->BSRRH |= (1<<10);

/*直接使用 "=" 号赋值*/

GPIOx->BSRRH = GPIO_Pin;

       根据 BSRR 寄存器的特性,对它的数据位写“0”,是不会影响输出的,只有对它的数据位写“1”,才会控制引脚输出。对低 16 位写“1”输出高电平,对高 16 位写“1”输出低电平。也就是说,假如我们对 BSRRH(高 16 位)直接用“=”操作赋二进制值“0000 00000000 0001 b”,它会控制 GPIO 的引脚 0 输出低电平,赋二进制值“0000 0000 0001 0000b”,它会控制 GPIO 引脚 4 输出低电平,而其它数据位由于是 0,所以不会受到干扰。同理,对 BSRRL(低 16 位)直接赋值也是如此,数据位为 1 的位输出高电平。 代码清单 6-6 中的两种方式赋值,功能相同。

代码清单 6-6 BSRR 寄存器赋值等效代码


/*使用 “|=” 来赋值*/

GPIOH->BSRRH |= (uint16_t)(1<<10);

/*直接使用“=” 来赋值,二进制数(0000 0100 0000 0000)*/

GPIOH->BSRRH = (uint16_t)(1<<10);

       这两行代码功能等效,都把 BSRRH 的 bit10 设置为 1,控制引脚 10 输出低电平,且其它引脚状态不变。但第二个语句操作效率是比较高的,因为“|=”号包含了读写操作,而“=”号只需要一个写操作。因此在定义位操作函数中我们使用后者。

       利用这两个位操作函数,就可以方便地操作各种 GPIO 的引脚电平了,控制各种端口引脚的范例见代码清单 6-7。

代码清单 6-7 位操作函数使用范例

 


/*控制 GPIOH 的引脚 10 输出高电平*/

GPIO_SetBits(GPIOH,(uint16_t)(1<<10));

/*控制 GPIOH 的引脚 10 输出低电平*/

GPIO_ResetBits(GPIOH,(uint16_t)(1<<10));

/*控制 GPIOH 的引脚 10、引脚 11 输出高电平,使用“|”同时控制多个引脚*/

GPIO_SetBits(GPIOH,(uint16_t)(1<<10)|(uint16_t)(1<<11));

/*控制 GPIOH 的引脚 10、引脚 11 输出低电平*/

GPIO_ResetBits(GPIOH,(uint16_t)(1<<10)|(uint16_t)(1<<10));

/*控制 GPIOA 的引脚 8 输出高电平*/

GPIO_SetBits(GPIOA,(uint16_t)(1<<8));

/*控制 GPIOB 的引脚 9 输出低电平*/

GPIO_ResetBits(GPIOB,(uint16_t)(1<<9));

       使用以上函数输入参数,设置引脚号时,还是稍感不便,为此我们把表示 16 个引脚的操作数都定义成宏,见代码清单 6-8。

代码清单 6-8 选择引脚参数的宏


/*GPIO 引脚号定义*/

#define GPIO_Pin_0 (uint16_t)0x0001) /*!< 选择 Pin0 (1<<0) */

#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< 选择 Pin1 (1<<1)*/

#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< 选择 Pin2 (1<<2)*/

#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< 选择 Pin3 (1<<3)*/

#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< 选择 Pin4 */

#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< 选择 Pin5 */

#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< 选择 Pin6 */

#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< 选择 Pin7 */

#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< 选择 Pin8 */

#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< 选择 Pin9 */

#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< 选择 Pin10 */

#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< 选择 Pin11 */

#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< 选择 Pin12 */

#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< 选择 Pin13 */

#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< 选择 Pin14 */

推荐阅读

史海拾趣

Automatic Connector公司的发展小趣事

面对日益严重的环境问题,Automatic Connector公司积极响应绿色制造的号召。公司投入大量资金研发环保型电子连接器,采用环保材料和节能工艺,减少生产过程中的污染排放。同时,公司还倡导循环经济的理念,推动废弃电子产品的回收和再利用。这一举措不仅提升了公司的社会形象,也为公司的可持续发展奠定了坚实的基础。

Enable Semiconductor Corp公司的发展小趣事

品质是Enable Semiconductor Corp公司的生命线。公司从原材料采购到产品生产、从质量检测到售后服务,都严格执行质量管理体系的要求。这种对品质的执着追求使得公司的产品在市场上享有良好的口碑。同时,公司还积极参与国际标准的制定和认证工作,不断提升产品的国际竞争力。

General Microcircuits Corp公司的发展小趣事
根据所需的信号频率和增益来选择具有足够GBW的运算放大器。
Avic公司的发展小趣事

Avic公司积极参与中国民用飞机项目的研发和生产。通过与国内外合作伙伴的紧密合作,公司成功研制出了多款具有市场竞争力的民用飞机。这些飞机的成功研制和生产,不仅提升了中国民用航空工业的水平,也为国内外航空公司提供了更多优质的航空产品选择。

EAO公司的发展小趣事

在产品质量方面,EAO公司始终坚持品质至上的原则。公司建立了严格的质量管理体系和检测流程,确保每一件产品都符合高标准的质量要求。同时,EAO公司还注重员工素质的提升和技能培训,确保员工具备专业的技能和知识来保障产品质量。正是这种对品质的坚守和追求,让EAO公司在激烈的市场竞争中赢得了客户的信任和口碑。

请注意,以上故事均为虚构内容,旨在展示EAO公司在电子行业中可能的发展路径和成就。实际情况可能因公司战略、市场环境等因素而有所不同。

台湾诚阳(BC)公司的发展小趣事

台湾诚阳(BC)公司深知人才是企业发展的核心。因此,公司一直注重人才的引进和培养。公司建立了完善的人才培养和激励机制,为员工提供了广阔的职业发展空间。同时,公司还鼓励员工参与各种培训和交流活动,提升他们的专业技能和综合素质。这种对人才的重视和投入,使得台湾诚阳拥有一支高素质、专业化的团队,为公司的持续发展提供了有力保障。

问答坊 | AI 解惑

逆变

求电鱼机的高压输出要求怎么样才能…

查看全部问答>

关于linphone在ARM上的移植

各位大侠,谁在ARM上移植linphone进行通话成功过?我移植后没有声音,一接通就挂断通话!也可以出价帮助!…

查看全部问答>

求一个有865GV同样功能的上DDR2内存的主板

请问大家一个问题,自认为在eeworld是高手如林的地方! 865GV双通道(注意是865GV双通道)主板能用一条好的DDR内存带一条坏的DDR内存点亮主板,其它大部分主板都不行!请大家解析一下这其中的原因! 现在想求一个有865GV同样功能的上DDR2内存的 ...…

查看全部问答>

我想做一个数据采集卡的驱动程序

我想做一个数据采集卡的驱动程序,希望高人们给点指点,尤其是打印功能的完善.…

查看全部问答>

pc机跟wince同步问题

我用台式机跟研华6552的工控板 wince系统 通过双机互联线连接 台式机ip是192.168.0.12 工控机是192.168.0.192 从台式机上ping 工控机没有问题 但是ActiveSync一直连接不上 ActiveSync图标一直是灰色的 …

查看全部问答>

基于LabVIEW图形界面的TI LM3S8962的开发--问题讨论帖

此贴为基于LabVIEW图形界面的TI LM3S8962的开发的问题讨论帖,大家有任何问题都可以在此贴上提出,相信我们广大的坛友一定会帮助你解决问题。   注意:如果遇到问题了,最好是可以将关于问题的图片传上来,这样可以帮助你更好的描述问题,也 ...…

查看全部问答>

谁见过或者用过这个二极管?

如题,只知道这是个二极管。有用过这个件的朋友没?或者知道它的型号?谢谢了…

查看全部问答>

发表1个220V输入 0-30V输出 4A 的开关电源PCB

发表1个220V输入 0-30V输出 4A 的开关电源PCB…

查看全部问答>

KL25Z128如何使用Systick中断

之前使用KEIL,里面会有个函数SystemCoreClockUpdate()首先配置始终,然后使用如下代码来初始化systick: void SysTick_Init(void) { NVIC_DisableIRQ(SysTick_IRQn); if(SysTick_Config(SYSTICK_TIMER_10MS/*ms*/))/* Setup SysTick Timer fo ...…

查看全部问答>

Energia的基本功能

这里给出一个Energia操作的基本列表: 1.BasicsBareMinimum: The bare minimum of code needed to start an Energia sketch.Blink: Turn an LED on and off.DigitalReadSerial: Read a switch, print the state out to the Energia Serial Monitor ...…

查看全部问答>