65.9K
CodeProject 正在变化。 阅读更多。
Home

FRDM-KL25Z 板的 Arduino 兼容软件库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (16投票s)

2015 年 1 月 13 日

LGPL3

28分钟阅读

viewsIcon

72841

downloadIcon

728

本文介绍了一个用于 FRDM-KL25Z 开发板的软件库,该库允许使用 Arduino API 为该开发板编写程序。

引言

FRDM-KL25Z 是一款有趣的超低成本 32 位微控制器开发板。它之所以吸引“自己动手”社区,是因为其低廉的价格(约 13 美元)以及与 Arduino 引脚兼容性。如果你和我一样是 Arduino 用户,那么当你听说有一款开发板,其价格不到标准 Arduino 开发板的一半,却能提供运行频率为 48 MHz 的 32 位 ARM MCU,128 KB 闪存和 16 KB RAM 内存,以及板载加速度计等功能时,你可能会感到兴趣。但正如你可能预料到的那样,其中有一个“陷阱”。FRDM 开发板的引脚布局相同,因此你可以连接 Arduino 扩展板,但没有软件兼容性。你不能使用 Arduino API 函数,例如 digitalRead、delay 等,也不能通过 Arduino IDE 对开发板进行编程。我当时在想,如果能做到就好了……本文就是我朝着这个方向迈出的第一步。

预期功能

上面的文字可能会让你认为我已经完成了将 Arduino 移植到 FRDM-KL25Z 平台的所有工作。不幸的是,并非如此。我应该在一开始就清楚地说明预期功能。本文介绍了一个软件库(或框架),它允许你使用与 Arduino API 中可用的相同函数来编写程序。它不允许你使用 Arduino IDE 对 FRDM 开发板进行编程。它也不包含 Arduino 库,只包含基本 API。使用的 IDE 是飞思卡尔的 Kinetis Design Studio (KDS),它是一个基于 Eclipse 的 IDE,并且是著名的 CodeWarrior IDE 的简化版本。它是免费的,这很好。你可以从这里下载
 

目标受众

我认为主要受众是喜欢编程且不惧怕 C 语言,但不太喜欢制作硬件并喜欢 Arduino 类开发板和扩展板舒适性的用户。我会说它适用于想要使用具有强大功能和有趣特性的 Arduino 类开发板的程序员。
对于 Arduino 初学者来说,这个库可能很难使用。

 

新功能

在本文的 2015 年 6 月修订版中,我更新了文本,允许在新版本的 Kinetis Design Studio IDE (3.0.0) 中使用该库。还提供了更新后的源代码。有关详细信息,请参阅源包中的发布说明文件。

在 2015 年 2 月更新的本文和库版本中,我添加了 pulseIntone 功能。在第一个版本中我不知为何遗漏了这一点,尽管它看起来非常有用。如果你不熟悉 Arduino API,pulseIn 函数会读取引脚上脉冲的长度。tone 函数会在引脚上生成方波信号。
然而,我决定不完全“模拟”这两个函数,因为它们有一个严重的限制——pulseIn 只能一次测量一个引脚上的脉冲,从而阻塞程序的执行。此外,你只能一次在一个引脚上使用 tone 生成方波信号。这可能对于实验和玩耍来说没问题,但对于严肃的项目来说会非常受限。所以我需要一个更强大的解决方案。一旦我有了它,再用它们的限制来构建 pulseIn 和 tone 包装器似乎就没有意义了。我添加到这个库中的解决方案是一个新的高级(即易于使用)驱动程序,称为 waveio。有关更多信息,请参阅本文底部的 **pulseIn、tone 和 Servo** 一章。
以下是 waveio 驱动程序功能的概述:

  • 同时测量和生成多达 10 个引脚上的信号(输入/输出的任意组合)。
  • 输出信号可以有任意占空比,而不仅仅是 50%(方波)。
  • 用于控制伺服电机和读取 RC PPM 信号(控制 RC 模型中伺服电机的信号)的便捷函数。
  • 用于读取输入脉冲(例如,来自 Parallax Ping 超声波测距传感器)的便捷函数。

在随附的 zip 文件中,您将找到新版本的库,其中包含新的 waveio 驱动程序和 waveio 示例项目,演示了如何使用它。

背景

如果您对 FRDM-KL25Z 开发板的硬件感兴趣,可以在互联网上找到许多资源。我的早期 CodeProject 文章也包含了简要概述——在 CodeWarrior 中为 Freescale Freedom 平台编写您的第一个程序

如果您决定希望在您的微控制器开发板上拥有 Arduino 中可用的相同功能,您基本上有两种选择:

  1. 获取 Arduino 源代码并为您的开发板进行修改,或者
  2. 编写自己的函数并在顶部添加 Arduino 兼容的“包装”函数

我不想使用选项 1,尽管它可能很有趣——创建一个新的“Arduino 克隆”。但与问题相比,收益似乎很小。实际上,我能看到的唯一好处是它可以面向更广泛的受众——所有使用 Arduino 的人都可以使用它。这很有吸引力,但存在一些问题:首先,我觉得我无法独自处理所有工作,例如集成到 IDE 中,所以我只限制自己创建软件。其次,我不喜欢 Arduino API 的内部设计方式。我不想遵循我(并非我一个人)不认为好的设计。最后,Arduino IDE 的功能非常有限,主要是一点,没有调试器可用。通过打印到串行线来调试 ARM 微控制器程序似乎不合适。
因此,我决定以自己的方式编写一个库,并在顶部添加 Arduino API 作为“薄包装”。这样,您可以在需要时使用 Arduino 函数,但也可以在准备好后转向更强大的函数。我将我的库命名为 MSF Lite。MSF 代表微控制器软件框架。稍后会详细介绍。
 

使用代码

要在您的项目中使用该库,请执行以下操作:

  • 将随附的 zip 文件解压到您的磁盘。例如,解压到 c:\msf_lite。
  • 从 msf_lite\examples 中选择一些示例程序开始。

您将需要:

  • Kinetis Design Studio IDE。您可以从 Freescale 网站下载
  • 带有兼容 OpenSDA 应用程序的 FRDM-KL25Z 开发板 - 见下文。

关于开发板和 OpenSDA 应用程序的说明
我假设您拥有 FRDM-KL25Z 开发板和用于连接电脑的 USB 线缆。我不确定您的开发板将使用哪个版本的 OpenSDA 应用程序(“引导加载程序”)。最好将固件更新到最新版本。您可以从 P & E Micro 下载。页面顶部有 Open SDA Firmware 链接,以及更新开发板固件的说明。您从 P & E Micro 网站下载的 zip 包包含用于不同开发板的许多应用程序。您应该使用 MSD-DEBUG-FRDM-KL25Z_Pemicro_v114.SDA,它集调试器、虚拟串口和大容量存储于一体。

打开示例程序

请注意,有关打开示例项目的教程也可以在 msf_lite/doc 中找到 pdf 和 html 格式。它可能比本文更新。

示例程序可在 msf_lite\examples\kds\frdm_kl25z 文件夹中找到。使用 Arduino API 的示例名称以 "arduino_" 开头,其他示例是使用 MSF 库函数的“纯”C 语言程序。

以下是尝试示例程序的方法:

启动 Kinetis Design Studio,然后从文件菜单中选择导入...

在“导入”窗口中,展开通用文件夹并选择现有项目到工作区
 


在下一个窗口中,选择“选择根目录”选项,并浏览到 msf_lite\examples\kds\frdm_kl25z。选择您希望导入的示例程序文件夹,例如 **arduino_blink**,然后单击“确定”。

提示:您也可以选择父 frdm_kl25z 文件夹并一次性导入所有示例。


所选文件夹中包含的项目应该出现在“导入”窗口的“项目”区域中。


 

请确保未勾选“将项目复制到工作区”复选框!

单击“完成”按钮,项目即可导入。



您现在可以打开 Sources 文件夹中的 main.c 文件来查看代码。

要构建程序,请单击工具栏中带有锤子图标的按钮。



您也可以右键单击项目,然后从上下文菜单中选择“构建项目”。

项目应构建无误。


启动程序

我们需要一个 **调试配置** 才能下载(和调试)程序。

在此库的 KDS 3.0.0 (2015 年 6 月) 版本中,调试配置已添加到示例项目中,因此您无需创建它们。但如果您遇到包含的配置有问题,仍然可以创建一个新的,如下所述。

右键单击项目资源管理器中的项目,然后从上下文菜单中选择调试方式... > 调试配置...
在“调试配置”窗口中,选择“GDB PEMicro Interface Debugging”,然后单击窗口左上角的“新建启动配置”按钮。

在窗口的右侧出现一些选项卡。首先选择选项卡。单击“C/C++ 应用程序框”下方的“搜索项目...”按钮,然后选择您将看到的 .elf 文件——例如,**arduino_blink.elf**。

您现在应该在“C/C++ 应用程序”字段中看到选定的 .elf 文件(可执行文件)。

接下来,选择调试器选项卡。
在“接口”组合框中选择“OpenSDA Embedded Debug - USB Port”。
在“设备名称”中选择“KL25Z128M4”。



确保您的 FRDM-KL25Z 已连接到您的计算机。

现在单击右下角的“调试”按钮开始调试。

如果一切顺利,KDS 应该切换到调试透视图,您应该看到程序停在 main 函数的开头。您现在可以单击“继续”按钮来启动程序。


对于 arduino_blink 示例项目,您应该会看到开发板上的蓝色 LED 闪烁。

要停止程序,请使用红色“停止”按钮。然后,您将需要使用 IDE 窗口右上角的“C/C++”按钮,从调试透视图切换回 C/C++ 透视图。

请注意,如果您想尝试另一个示例,您将需要按照上述相同的方式创建新的调试配置。

您可以在 main.c 文件顶部的注释中找到每个示例程序的描述。IDE 默认会将此注释显示为“折叠”状态,因此要查看注释,请单击编辑器中的加号 (+) 按钮。

创建您自己的项目

有关如何使用 MSF lite 创建您自己的项目的说明,请参阅 msf_lite\doc\kds_howto3.0.0.txt 文件。

配置您的项目

您的项目中可以配置许多选项。您可以通过编辑项目“Sources”文件夹中的 **msf_config.h** 文件来完成此操作(从 KDS IDE 打开)。

您将在该文件中找到几个宏。最重要的是 F_CPUF_BUS。通过它们,您可以定义将使用的 CPU 速度。

**请注意,这些宏的目的是向 MSF 库告知您已设置的 CPU 频率。您不会通过更改这些值来定义 CPU 速度。您只是通知库您正在使用的 CPU 速度。**

CPU 速度可以通过将 CLOCK_SETUP 符号定义为 0 到 4 之间的值来设置。在 KDS 3.0.0 的新版本中,此方法有所改变。KDS 中提供的源代码不再定义 CLOCK_SETUP 符号。如果您不自行定义,将使用默认设置 0(20.97 MHz CPU 时钟)。您可以在项目设置中定义此符号:C/C++ Build > Settings > Cross ARM C Compiler > Preprocessor。

在“已定义符号”列表中输入:CLOCK_SETUP=1

要查看 KDS 中定义的时钟选项,请打开您的项目/Includes 文件夹 > system_MKL25Z4.h 文件。预定义的时钟设置位于文件顶部附近的注释中。

本文提供的示例项目使用时钟设置 1,即 48 MHz,但它们应该适用于所有可用的频率。只需确保通过定义 CLOCK_SETUP 符号设置的值与 msf_config.h 中定义的值同步。您将在 msf_config.h 文件中找到所有可用(和支持)的值被注释掉,因此您只需取消注释您需要的值即可。请注意,这适用于 KDS 3.0.0 和 1.1.1 版本中可用的 CLOCK_SETUP 选项。我希望这些值在 IDE 的未来版本中不会改变。

在旧版 KDS (1.1.1) 中,CLOCK_SETUP 在 system_MKL25Z4.c 文件中定义,您可以在项目中的 Project_Settings\Startup_Code 文件夹中找到它。在该文件顶部有 CLOCK_SETUP 符号的定义,例如 #define CLOCK_SETUP 1。

 

文档

msf_lite/doc/doxygen 文件夹中包含 doxygen 生成的源代码文档。打开位于该处的index.html 文件。有关 Arduino 兼容 API,请参阅下一节。

msf_lite/doc/kds_howto3.0.0.txt 中还有一个关于使用 MSF Lite 创建新项目的教程,以及 msf_lite/doc/import_example_project.pdf 中有一个关于导入示例项目的教程。

 

Arduino 兼容 API

我不想重复原始 Arduino API 的文档,因此我将只列出支持的函数并在适当的地方添加一些注释。

数字输入/输出

pinModedigitalReaddigitalWrite 可用,其接口与 Arduino 相同。以下是原型:

void digitalWrite(int pin, int val);
int digitalRead(int pin);
void pinMode(int pin, int mode);

pin 是一个简单的整数,与 Arduino 中相同。这些数字 I/O 函数的实现方式与我的Arduino 数字 I/O 快速版本类似,因此它们应该工作得非常快。如果您需要最佳性能,可以使用原生 MSF 函数,例如 msf_pin_write。有关详细信息,请参阅下面的 MSF Lite API 部分。

模拟功能

也支持模拟功能。我这里也包含了 analogWrite 函数,尽管我们都知道它实际上不是模拟输出,而是 PWM 输出。

void analogReference(int type)

analogReference 的工作方式与原始版本相同,但仅支持 DEFAULT 和 EXTERNAL 参考类型。我们的 MCU 的 ADC 中没有内部参考。除非您对硬件进行更改,否则对于 FRDM-KL25Z 开发板,这两个值具有相同的效果——参考是电源电压,约为 3V。这是因为,如文档所述,VREFH (AREF) 引脚在板上连接到电源(3V),如果您想使用外部电压,则必须切断走线 SH1 并在 R3 的位置放置导线(0R 电阻)。

int analogRead(int pin);

与原始函数相同,但值为 16 位(范围 0 到 65535)。

void analogWrite(int pin, int value);

pin 是 Arduino 中使用的数字引脚编号。有关 Arduino 引脚编号到实际 MCU 引脚(端口 + 引脚)的映射,请参阅 msf_lite\board\frdm_kl25z 中的 arduino.h 文件。您将找到类似以下的定义:

#define PD2 (GPIO_D4)

这意味着 Arduino 数字引脚 2 实际上是微控制器的引脚 D4。
value 是 PWM 信号的占空比,介于 0 到 255 之间。这与原始接口相同,但功能大大扩展。与标准 Arduino 开发板不同,在标准 Arduino 开发板上只有少数引脚可以真正生成 PWM 信号,而 FRDM-KL25Z 上几乎所有引脚都可以做到(除了引脚 14 和 15)。PWM 信号的频率可以通过配置文件 msf_config.h 中的 #define 设置,范围从大约 250 Hz 到 32 kHz。您将在每个示例项目的 Sources 文件夹中找到此配置文件。搜索 MSF_AWRITE_500HZ
对于不能生成 PWM 的引脚(14 和 15),该函数不执行任何操作。这与 Arduino 不同,Arduino 对于此类引脚,如果请求的占空比在 0 到 127 之间,则将输出引脚设置为低电平,如果高于 127,则设置为高电平。我认为此类功能无用,只会使代码复杂化。

时间函数

uint32_t millis();

uint32_t micros();

void delay(uint32_t millis);

void delayMicroseconds(uint32_t micros);

与原始功能相同。micros 函数的分辨率为 1 微秒。
请注意,delay 不应从中断服务例程 (ISR) 调用。从 ISR 调用 delayMicroseconds 是安全的。delayMicrooseconds 对于大约 2 微秒及以上的延迟可靠工作。您还可以使用 MSF_DELAY_1USMSF_DELAY_5US 宏来实现短的固定时间延迟。

Serial 类

Serial 类在 Arduino 中提供串口通信 (UART)。我不想使用 C++,因此 MSF lite 中的 Serial 不是一个真正的类,而是一个带有函数指针的结构体。这听起来可能有点吓人,但对于用户来说没有区别——它的使用方式与 Arduino 中相同。例如,你写:

Serial.begin(9600);

与 Arduino 版本相比,该类的功能非常有限。这既是由于决定不使用 C++,也是由于我缺乏实现高级功能的强烈意愿。
主要区别在于没有一个单一的打印函数可以打印您给它的任何内容。这在 C 语言中(不像 C++ 中)不容易做到。这就是为什么我创建了单独的函数来打印最常见的数据类型。
此外,它只能与 UART0(引脚 0 和 1)一起使用。没有 Serial1 或 Serial2 “对象”,尽管微控制器中实际上有 3 个 UART 模块。对于其他 UART,可以使用“原生”MSF lite 驱动程序。
以下是 MSF lite Serial“类”函数的完整列表:

void  begin( uint32_t baudrate);

支持波特率 2400、4800、9600、19200、38400、57600、115200。对于无效波特率,将默认为 9600。始终使用 8 个数据位和 1 个停止位,无奇偶校验。这无法更改。如果需要此类高级功能,可以使用原生 MSF lite 驱动程序 Driver_UART0 代替 Serial。

void  end(void);

实际上什么都不做。

void print(const char* str);

将文本(字符串)打印到串口。

void println(const char* str);

与 print 相同,但在文本后也打印一个新行。您也可以在给 print 的字符串中包含 "\n" 来创建新行。

void printint(int val, int format);

以给定格式打印整数。格式可以是 DEC、HEX、OCT。不支持 BIN 格式。

void printfloat(float val, int decplaces);

打印实数,小数点后保留指定位数。请注意,浮点数必须为 (s)printf 启用。为此,您需要将此标志添加到

-u _printf_float

在您的项目属性中,C/C++ Build > Settings > Cross ARM C++ Linker > Miscellaneous > Other Linker Flags。

 

int available(void);

获取可从串口读取的字节(字符)数量。这些数据已经到达并存储在串口接收缓冲区中(可容纳 64 字节)。

int peek(void);

返回传入串口数据的下一个字节(字符),但不将其从内部串口缓冲区中移除。也就是说,连续调用 peek() 将返回相同的字符,下一次调用 read() 也将返回相同的字符。如果没有数据可用,则返回 -1。

void flush(void);

等待传出串行数据传输完成;

int read(void);

读取传入的串口数据。返回传入串口数据的第一个可用字节(如果无数据可用,则返回 -1)。

int readBytes(char* buffer, int length);

将串口驱动内部缓冲区的字符读入给定缓冲区。当读取的长度达到预设值时,函数终止。返回缓冲区中放置的字符数。0 表示未找到有效数据。

int readBytesUntil(char terminator, char* buffer, int length);

将串口驱动内部缓冲区的字符读入给定缓冲区。当找到给定的终止符字符或读取给定长度时,函数终止。返回缓冲区中放置的字符数。0 表示未找到有效数据。

void write(int val);

将字节写入串行线。这是 Arduino 函数的受限版本;它只发送 1 个字节。如果您想发送字符串,请使用 Serial.print()

其他功能

mapconstrain 也受支持,其工作方式与原始版本相同。

pulseIntone 函数(以及 Servo 类)功能通过本文 2015 年 2 月更新中添加的新 waveio 驱动程序间接支持。有关详细信息和示例,请参见下文。

 

MSF Lite

如前所述,我的库名为 MSF lite,MSF 代表微控制器软件框架。我承认,对于一个相当简单的 MCU 库来说,这个名字确实有些夸张。但这个名字来源于我的“长期”项目(或梦想),即一个统一的微控制器 API。这将是另一篇文章的主题,但只是给您一些我所说的概念:想象一下,如果有一组函数可以在任何微控制器上使用……MCU 制造商似乎并没有为此付出太多努力。我可以理解他们——他们喜欢将客户绑定到他们的 MCU 上。而没有 API、特定私有 API 甚至更好的图形开发工具,是实现这一目标的有效方式。如果您的项目需要迁移到另一个 MCU 品牌,而您必须将所有代码重写到硬件级别,那将是非常痛苦的。这对客户有利吗?可能没有,但谁在乎呢?
但时代正在改变。Arduino 就是一个很好的例子。MCU 制造商从未能/想要为评估板制定一些标准布局;用户自己通过 Arduino 做到了这一点。现在,MCU 制造商开始支持 Arduino;他们制造的评估板具有 Arduino 兼容的引脚布局,以便可以连接 Arduino 扩展板。
另一个例子是 ARM 处理器架构。它正在迅速成为 MCU 的标准。似乎很快一切都将是 ARM。因此,在硬件方面,标准化似乎正在实现。
在软件方面,情况并不那么乐观。当然,Arduino API 的使用程度与 Arduino 硬件一样广泛,但它存在于自己的社区中,对高级程序员或专业人士几乎没有吸引力。它过于简单且设计糟糕(且效率低下),无法成为广泛使用的标准。ARM 也有 CMSIS 标准,它在一定程度上建议了软件 API,包括外设驱动程序定义。但它仅适用于基于 ARM 的设备,并且并非所有 MCU 制造商都真正实现。我认为需要一个开源标准。
我创建了 MSF 的一些基本设计,并为 FRDM-KL25 开发板实现了一个版本,您将在本软件包中的 Arduino API 下方找到它。这就是为什么名称中有“lite”的原因——这是 MSF 应该具有的真实 API 的轻量级版本。

库的内部设计

下图显示了库的层次结构。


在微控制器硬件之上,底部使用了 CMSIS-Core 组件。该代码由 MCU 供应商(在本例中为 Freescale)提供。CMSIS 标准由 ARM 设计,定义了低级事物,例如硬件寄存器如何在 C 语言中定义(寄存器覆盖结构)以及 SysTick 定时器等一些代码函数。

在此级别之上,我希望使用 CMSIS-Driver 组件,这是 ARM 定义的 CMSIS 标准的另一部分。不幸的是,Freescale 不提供此组件。它仅在开发板的示例代码包中提供一些基本驱动程序。由于我不喜欢这些驱动程序的编写方式,并且倾向于尽可能地遵循标准,因此我决定创建我自己的 CMSIS 风格驱动程序版本。我在这里强调“CMSIS 风格”,因为我并没有真正承担 MCU 制造商实现 CMSIS 驱动程序层的工作。我只是尝试使用相同的 API。但请不要期望我的驱动程序具有 CMSIS 标准定义的所有功能。
对于 CMSIS-Driver 标准未涵盖的外设(例如 ADC 或定时器),我以类似风格创建了自己的驱动程序接口定义。

在更高的层次上是 MSF 驱动程序,它们应该为所有平台提供统一的基本功能。

在更高的层次上是 MSF 便捷的全局函数,例如 msf_delay_ms(),它们应该允许轻松实现简单的、常用的操作。这有点像我替代 Arduino API 的方式。

最顶层是 Arduino API 包装器。在许多情况下,它只是一个简单的宏或内联函数,调用下面的 msf 函数。但在某些情况下,例如 Serial“对象”,也有相当多的代码。
 

原生 MSF API

如前所述,Arduino API 实际上只是 MSF 库及其驱动程序原生 API 的一个薄包装器。在这里描述所有驱动程序的 API 会占用太多空间。请参阅 msf_lite 中的 \doc 目录和/或源代码本身。doc/doxygen/index.html 中也有从代码生成的 Doxygen 文档。
以下是主要功能的概述:

MSF 全局函数
 

msf_init(uint32_t param)

初始化 MSF。应在程序开头调用。


GPIO(数字输入/输出)

msf_pin_direction(pin, dir)

将给定引脚的方向设置为输入或输出。不改变上拉电阻设置。pin 参数是 msf_mkl25z.h 中定义的值之一。引脚名称基于微控制器中的原始名称。格式为 GPIO_[引脚名称],例如 GPIO_A0GPIO_E29
方向为输入输出。详情请参见 gpio.h。
 

msf_pin_pullmode(pin, mode)

在给定引脚上启用/禁用上拉电阻。不检查或更改引脚方向。该引脚应为输入。pin 参数在 msf_pin_direction 中描述。mode 可以是 pull_uppull_none
 

msf_pin_write(pin, value)

将引脚设置为高电平或低电平状态。valuetruefalse。不检查或更改引脚方向。引脚应配置为输出!
 

msf_pin_read(pin)

读取给定引脚的逻辑电平。如果电平为高,则返回 true;如果为低,则返回 false。不检查或更改引脚方向。该引脚应配置为输入!
 

msf_pin_toggle(pin)

切换给定输出引脚的状态。引脚应配置为输出!


时间测量和延迟函数

uint32_t msf_millis(void);
uint32_t msf_micros(void);
void msf_delay_us(uint32_t micros); 
void msf_delay_ms(uint32_t millis);

这些时间函数类似于 Arduino。
 

MSF_DELAY_1US(void)
MSF_DELAY_5US(void)

非常短的延迟函数 - 在 delay_util.h 中实现为内联函数。

串口通信函数

void msf_print(const char* str); // print string 
void msf_print_char(char c); // print single character
/* Easy to use functions */
/* Print simple integer in decimal and hex */
void msf_printnum(uint32_t number); 
void msf_printhex(uint32_t number); 

/* More flexible functions */
/** print string and one formatted number */
void msf_printf16(const char* str, const char* format, uint16_t data);
void msf_printf32(const char* str, const char* format, uint32_t data);
void msf_printf_real(const char* str, const char* format, double data);

/* Reading characters */
char msf_read_char(void); 
bool msf_char_available(void);


模拟功能

uint16_t msf_analog_read(Analog_pin_t apin)

读取模拟值。apin 参数是 msf_mkl25z.h 中定义的值之一。引脚名称为 AIN_[MCU 上的实际引脚],例如 AIN_E20。(E20 表示 PTE20)。请注意,并非所有引脚都可用作模拟输入。有关可用值,请参阅 msf_mkl25z.h 中的 Analog_pin_t 枚举。

 

pulseIn、tone 和 Servo(waveio 驱动程序)

如上所述,pulseIntone 函数(以及 Servo 类)在 MSF Lite 中不可直接使用,但它们的功能由 waveio 驱动程序涵盖。您可以在 _waveio_ 示例项目中查看各种用法示例。有以下示例:

  • 在一个输出引脚上生成信号
  • 同时在多个引脚上生成信号
  • 控制 RC 舵机(类似于 Arduino 中的 sweep 示例)
  • 使用 Parallax Ping 超声波传感器测量距离(读取输入脉冲)
  • 读取 RC 发射器的信号(同时最多 10 个通道)


以下是一些典型的使用示例。

在一个引脚(或多个引脚)上生成信号(具有任意占空比)

waveio_init(WAVEIO_RANGE_0_5);  
waveio_out_start(WAVEIO_C0, 500, 500);    // 1 kHz, 50% duty; D0  (Arduino 10)
waveio_out_start(WAVEIO_C1, 100, 900);    // 1 kHz, 10% duty; A4    (Arduino 4)
waveio_out_start(WAVEIO_C3, 250, 250);    // 2 kHz, 50% duty; D3    (Arduino 12)

waveio_init 函数中,您提供希望使用的输入范围。这告诉 waveio 驱动程序需要初始化哪些低级定时器驱动程序。范围来自 MCU 中可用物理定时器中的可用通道。对于 FRDM-KL25Z 开发板中使用的 Kinetis KL25Z MCU,有三个定时器,分别称为 TPM0、TPM1 和 TPM2。TPM0 定时器有 6 个通道,另外两个定时器各有 2 个通道。这就是为什么 waveio_init 的可用范围是 0 到 5(属于 TPM0 的通道)、6 到 7(TPM1 通道)和 8 到 9(TPM2)。您可以组合任何范围,例如:

 waveio_init(WAVEIO_RANGE_0_5 | WAVEIO_RANGE_6_7 | WAVEIO_RANGE_8_9);

初始化后,只需调用 waveio_out_start 即可开始输出信号。

第一个参数(上例中的 WAVEIO_C0)是您希望使用的通道。通道直接映射到引脚。在本例中,通道 0 是 MCU 的引脚 PTD0,在 Arduino 引脚布局中是引脚 10。请注意,映射可以在项目配置文件 msf_config.h 中更改。

另外两个参数是您希望生成的信号半波的长度(以微秒为单位)。我使用这种方法而不是频率和占空比,因为它看起来更灵活,并且在代码中更容易处理。我希望它仍然易于使用。例如,要生成频率为 1 kHz 的信号,您需要获得 1 毫秒或 1000 微秒 (us) 的周期。因此,给 waveio_out_start 的两个值总和必须为 1000。如果您想要方波(50% 占空比),则两个部分相同,因此您将给函数 500 和 500。如果您喜欢方程,它们是:

Period_in_us = 1 000 / frequency_in_kHz
Frequency_in_kHz = 1 000 / period_in_us

测量引脚上的信号

waveio_init(WAVEIO_RANGE_0_5);
waveio_in_attach(WAVEIO_C0);
waveio_in_start(WAVEIO_C0);

while(1) {
    // Read measured values
    err = waveio_in_read(WAVEIO_C0, &a, &b);
    if ( err == WAVEIO_NO_ERROR )
      ; // print the length of the half-waves from a and b.
    
    msf_delay_ms(1000);
}

测量输入信号时,您也从初始化 waveio 驱动程序开始。接下来,您需要调用 waveio_in_attach 将引脚配置为定时器输入模式。这在程序中只需一次。引脚连接后,您可以通过调用 waveio_in_start 开始测量该引脚上的输入。驱动程序将在后台测量输入信号,您可以随时调用 waveio_in_read 来读取最新的测量值。该函数将为您提供输入上波的两个部分的长度。示例中的 ab 变量接收这些长度。它们是简单的 16 位无符号整数,通过指针传递给函数,以便它可以将输出值存储到其中。

测量引脚上的脉冲(等待脉冲)

waveio_init(WAVEIO_RANGE_0_5);
waveio_in_attach(WAVEIO_C0);
// Note: no need to start measurement by calling waveio_in_start(n) when using waveio_in_pulse_wait().
while(1) {
    // Wait for a pulse on the input with 1000 ms timeout
    a = waveio_in_pulse_wait(WAVEIO_C0, 1000);
    if (a == 0)
       msf_print("Timeout.\n");
    else
    {
       msf_print("Pulse: ");
       msf_printnum(a);
       msf_print(" us \n");
     }
     msf_delay_ms(1000);
 }

此示例展示了 waveio_in_pulse_wait 函数,它等待输入引脚上的脉冲(带超时)。然后返回脉冲的长度(以微秒为单位)。请注意,您无需在调用此函数之前调用 waveio_in_start。只需初始化驱动程序并连接您要使用的通道,然后调用 waveio_in_pulse_wait 即可。

 控制舵机(扫描示例)

waveio_init(WAVEIO_RANGE_0_5 );
for ( angle = 0; angle < 180; angle++ ) {
   waveio_out_servo(WAVEIO_C0, angle);
   msf_delay_ms(20);
}

waveio_out_servo 函数可用于控制舵机。它实际上只是 waveio_out_start 函数的简单包装器,它接受舵机的期望角度(介于 0 到 180 之间),就像 Arduino 舵机类一样,并将其转换为适当的脉冲长度。
 

如果需要比 waveio 驱动程序提供的更精细的输入或输出控制,您可以直接使用低级 TPM 定时器驱动程序。在新版 MSF Lite 中,_input_capture_ 示例展示了这种用于捕获输入信号的用法。

MSF CMSIS 风格驱动

本节介绍 MSF 中使用的低级 CMSIS 风格驱动程序的一些功能。这些驱动程序对于高级用户可能很有用。

您可以将每个驱动程序视为一个 C++ 类(实际上它是一个带有函数指针的 C 结构体)。UART 的驱动程序就是这样一个类的示例,定时器的驱动程序是另一个示例。
驱动程序类可能有多个实例。例如,如果 MCU 中有三个 UART 模块,则将有三个 UART 驱动程序实例。您通过编写 [驱动程序实例名称].[函数名称] 来调用驱动程序函数。例如,Driver_UART0.Initialize。这就是 CMSIS 定义驱动程序的方式,我也使用这个概念。
要查找每个驱动程序可用的函数,请查看 drv_[name].h 文件(例如 drv_uart.h 或 drv_adc.h)。在该文件中,您将找到驱动程序访问结构的定义,例如:

typedef struct _MSF_DRIVER_USART { 
 uint32_t (*Initialize) (UART_speed_t baudrate, MSF_UART_Event_t cb_event); 
 uint32_t (*Uninitialize) (void); 
 uint32_t (*PowerControl) (MSF_power_state state); 
 ... 
} const MSF_DRIVER_USART;

InitializeUninitializePowerControl 是驱动程序函数的名称。
您还需要知道驱动程序“对象”的名称。您可以在同一文件的底部找到它。有类似这样的行:

extern MSF_DRIVER_USART Driver_UART0;
extern MSF_DRIVER_USART Driver_UART1;
extern MSF_DRIVER_USART Driver_UART2;

例如,要初始化 MCU 中的 UART0 模块,您可以编写:

Driver_UART0.Initialize();

要查找每个驱动程序函数的详细信息,请查看“uart_kl25.c”文件。您可以在此处找到带有完整注释的函数,例如 UART_Initialize

UART 驱动
提供了微控制器中所有三个 UART 的实例。它们的名称是
Driver_UART0Driver_UART1Driver_UART2。驱动程序支持阻塞(轮询)操作和基于中断的操作。
详见 drv_uart.h。

定时器驱动
微控制器中有 TPM 定时器模块的驱动程序。驱动程序的名称为 Driver_TPM0Driver_TPM1Driver_TPM2。支持定时器的许多功能,例如 PWM、输入捕获和输出比较。
详见 drv_tmp.h。

ADC 驱动
此驱动程序用于模数转换器 (ADC)。KL25 微控制器中只有一个 ADC,因此只有一个驱动程序实例:Driver_ADC0。详见 drv_adc.h。

 

历史

2015-01-13 第一个版本。

2015-02-17 添加了 waveio 驱动程序和示例 - pulsein 和 tone 功能。

2015-06-19 示例项目已修改为适用于 KDS IDE 3.0.0 版本。

 

 

© . All rights reserved.