King MM - 和谐不存的生活的乐趣 - 第一部分, PIC32MM "Hello World!"
使用 C++ 和汇编编写 Microchip PIC32MM 微控制器
1 引言
这是关于使用 C++ 和汇编进行 PIC32MM 编程的系列文章的第一篇。
在这里,我将展示如何为微控制器 Hello World! 项目(即“闪烁 LED”应用程序)设置所需的最低硬件和软件环境。
2 背景
微控制器 (MCU) 是一个(小型的)“芯片上的计算机”,集成了 CPU、程序和数据内存、数字和模拟端口、外设(例如 UART)在一个硅片上。
我们正在考虑的 MCU 是一个适合业余爱好者的珍品:真正的 32 位架构(MIPS 核心),采用 28 引脚 SPDIP 封装(参见图 1 和图 2),价格仅为一美元半左右!

为了让 MCU 运行应用程序,我们必须遵循以下步骤:
- 在 PC上使用交叉编译器(即运行在一台机器上,能够为具有不同硬件架构的另一台机器生成代码的编译器)生成可执行文件。
- 使用名为编程器的硬件工具将生成的可执行文件传输到微控制器内部程序(FLASH)内存。

3 要求
3.1 硬件组件
即使是我们的最小项目,也需要一些硬件。
Microchip 详细说明了使 PIC32MM 运行所需的最小外部组件(参见设备数据手册的第 2 章)。在此基础上,我们添加了一个
- 面包板
- 红色 LED
- LED 限流电阻
- 一堆跳线
最终得到图 4 所示的硬件。

3.2 硬件工具
我们需要为 MCU 供电并对其进行“闪存”(编程),因此需要一些硬件工具。为了方便起见,还列出了一些可选工具。
PICKit 3 编程器(必需)
PICKit 3 用于对 PIC32 MCU 进行编程(即,将 PC 上的交叉编译器生成的可执行文件传输到 PIC32 flash 内存)。
尽管它是 Microchip 编程器中最便宜的选择,但 PICKit 3 仍需花费约 50 美元。好消息是:它可以编程几乎所有的 Microchip 微控制器(8、16 和 32 位系列)。
有更便宜的 PICKit 3克隆品可用。然而,原装的是推荐的选择。

稳压电源(必需)
MCU 需要稳定的电源才能正常工作(即,避免让你发疯)。
实验室 DC 电源是理想选择,但也有许多更便宜的替代方案,例如好的开关适配器甚至电池。只需确保您的电源设备能够在允许的范围内(2.0 至 3.6 V)提供稳定的 DC 源。
万用表(可选)
虽然不是必需的,但强烈推荐万用表,因为它是一个非常有用的工具,尤其是在电路出现问题时。市面上有许多便宜的万用表。
示波器(可选)
示波器是一种昂贵的仪器。对于像这里介绍的极简电路来说,它也是过度的。但是,如果您喜欢玩电子产品,那么我鼓励您购买一个(乐趣在于“示波器”!)。
镊子(可选)
镊子极大地简化了将元件插入面包板夹子的任务。
3.3 软件工具
MPLABX
MPLAB X 是 Microchip 免费提供的集成开发环境 (IDE)。
它是一个基于 Java、NetBeans 的程序:您可以在您喜欢的 OS(Windows、Linux 或 MAC)上运行它。到目前为止,一切顺利。
但坏消息更多:它很慢(我提过 Java 吗?),臃肿(我提过 Java 开发者吗?),有时不稳定且有错误。
建议安装最新版本(本文项目是使用版本 3.65 构建的)。
XC32
编译器不包含在 MPLABX 安装中,因此您必须单独下载并安装 XC32 C++ 编译器。
XC32 基于 gcc,这是一个很棒的 C++ 编译器。基于 gcc 4.8.3 的版本 1.44 使我们能够在如此微小的硬件上使用所有漂亮的 C++11 功能。这难道不令人兴奋吗?
备注
- 我们将使用免费版本的编译器,它不提供任何优化。
- 附带的 C++ 标准库可能不是最新的(请查看论坛上的相关信息)。无论如何,我们都不会使用它。 
3.4 文档
MCU 文档可在 Microchip 网站上找到,网址为 PIC32MM 系列专用网站。
最重要的文档是微控制器数据手册和“PIC32 系列参考手册”。
PIC32MM0064GPL036 系列数据手册
数据手册以及“PIC32MM0064GPL036 系列芯片勘误和数据手册澄清”是 MCU 的最终参考。
它显示了器件的确切引脚分配,详细说明了每个寄存器,并提供了电气规格等信息。
PIC32 系列参考手册
PIC32 系列参考手册 (FRM) 解释了所有 Microchip PIC32 系列微控制器的特性和外设。它包含许多部分,可单独下载。
数据手册和 FRM 互为补充:FRM 显示了如何使用外设来完成任务,通常提供示例代码,而数据手册则报告了您正在使用的 MCU 的确切详细信息。
我通常的做法是:
- 阅读相关的 FRM部分
- 查看 FRM示例代码
- 以数据手册为参考,将 FRM示例代码改编到MCU上
换句话说:仅凭数据手册信息很难做事情,因此请使用 FRM 的相关部分来理解。但是,在将 FRM 解决方案改编到您喜欢的 MCU 时要小心,并随身携带数据手册。
关于 MIPS 架构的教程或更好的书籍
需要对 MIPS 架构有基本的了解。有许多关于此类主题的教程和书籍(Google 是您的朋友)。
为了使用汇编代码,建议参考一些资料,例如 Imagination Technology 网站上提供的 MIPS® 架构程序员手册 - 第二卷-B:microMIPS32™ - 指令集。
4 构建电路
4.1 电路图
电路非常简单(参见下面的图 6)。
根据数据手册的要求(第 2 章:“32 位微控制器入门指南”),使 PIC32 运行所需的最小组件是:
- 一个 10 uF的钽电容连接VCAP到VSS
- 一个 0.1 uF的陶瓷电容连接VDD到VSS
- 另一个 0.1 uF的陶瓷电容连接AVDD到AVSS
- 一个 10 kOhm的上拉电阻,将MCLR引脚连接到VDD
“Hello World!”特定的附加硬件只是由 1 kOhm 电阻和红色 LED 组成的串联,连接在 VDD 和 RB7 数字输出之间。

备注
- 钽电容是极化的,请注意将其正极正确连接到 VCAP引脚。
- 红色 LED 也是极化的,将其阳极连接到 VDD。
- 通常,微控制器的时钟由外部晶振提供。此外,还可以使用另一个晶振提供次级(RTCC)晶振。但是,我们两者都不使用:时钟由内部快速电阻电容(FRC)振荡器提供。
- 电路图还显示了与 PICKit 3工具的连接,这是实际编程器件所必需的。
4.2 板卡,步骤 1:放置元件

4.3 板卡,步骤 2:添加 VDD 和 VSS 跳线

4.4 板卡,步骤 3:添加编程信号跳线

好了,您现在可以看看产生的意大利面条式的混乱,这是每个无焊业余爱好者的骄傲和喜悦。
现在可以将板卡连接到电源和 PICKit 3 编程器。
PICKit 3 的引脚分配在 Microchip 网站上提供的 “PICkit™ 3 - 在线调试器/编程器 - 用户指南”中显示。
- MCRL(由白箭头标记)-> 连接到橙色线
- VDD-> 连接到红色线
- VSS-> 连接到黑色线
- PGD-> 连接到黄色线(- MCU引脚- 14)
- PGC-> 连接到绿色线(- MCU引脚- 15)
5 最终,让我们来编程!
5.1 创建新项目
如果您还没有这样做,现在是时候安装所需的软件工具:MPLAB X IDE 和 XC32 交叉编译器。
现在打开 MPALB X 并
- 在“选择项目”窗口中,启动向导以创建新项目,选择 Microchip Embedded类别和Standalone Project。
- 请自豪地选择 PIC32MM0064GPL028器件。
- 选择 PICKit 3硬件工具。
- 选择 XC32编译器工具链。
- 为您的项目起一个有意义的名字,例如“PigsOnTheWing”。
5.2 添加代码
配置位
配置位是标志(存储在 FLASH 内存的特殊位置),提供微控制器的静态配置。
它们控制 PIC32 的许多功能,例如时钟源的选择。
配置位的设置使用 XC32 的专用指令 pragma config 实现。幸运的是,MPLAB X IDE 的配置位窗口提供了一种方便的方式来设置这些标志,只需选择所需选项即可。然后 IDE 将为我们生成相应的代码。
我们的基本选择是:
- 使用 PLL模块从FRC(快速RC,8 mhz)内部振荡器(这样,我们就不需要外部石英)运行,以达到24 mhz时钟。
- 禁用所有有用的漂亮功能(例如看门狗),这些功能在最终产品中有用,但在尝试使用板卡进行实验时却很烦人。

向项目中添加一个新的 C++ 文件(称之为confbits.cpp),并将生成的源代码复制到其中。
I/O 引脚配置
微控制器提供了一些寄存器,用于配置 I/O 引脚。通常,我们必须决定一个引脚是:
- 模拟或数字,使用 ANSEL寄存器
- 输入或输出,使用 TRIS寄存器
还有其他细节我们暂时跳过。引脚被分组到端口中。PIC32MM 提供三个端口:A、B、C。
有一组相应的 ANSEL 和 TRIS 寄存器。这里,我们将不使用 MCU 的任何模拟功能,因此 ANSELA=ANSELB=ANSELC=0;
我们将只使用一个输出引脚(即 RB7),所有其他引脚保持未使用。Microchip 建议将未使用的引脚配置为输出并将其驱动为低电平(或高电平)。因此,我们设置 TRISA = TRISB = TRISC = 0;(全部为输出)并且 LATA=LATB=LATC=0;(全部驱动为低电平)。
就这样:将以上语句写入(新创建的)config.cpp 源文件。
#include <xc.h> // definitions of ANSELA, ANSELB, ...
// configure all the pin as digital output, drive all of them low
void config()
{
  ANSELA = ANSELB = ANSELC = 0;
  TRISA = TRISB = TRISC = 0;
  LATA = LATB = LATC = 0; 
}
实际应用程序代码
在 main 函数中,我们将在无限循环中依次切换 LED 的开和关(毕竟微控制器 main 之后不应该返回,因为外面没有 OS 在等待它)。为了切换引脚值,我们可以使用 LATINV 寄存器:LATBINV = 0x80 可以完成此操作。
由于人眼无法感知过快的开/关切换,因此我们必须在指令后添加延迟。我们可以简单地在空循环中浪费 MCU 周期。由于 PIC32 以 24 mhz 运行,24,000,000 次迭代应该足够了。所以我们尝试以下 main 函数:
#include <cstdint>
#include <xc.h>
#include "cfg.hpp"
void delay()
{
  for (uint32_t n=0; n<24000000;++n){} // waste MCU cycles
}
int main()
{
  config();
  for (;;)
  {
    LATBINV = 0x80; // toggle the RB7 pin state
    delay();
  }
}
您可以使用 MPLAB X 的“制作并编程设备主项目”命令来编译和闪存代码。
然而,代码执行有点令人失望。闪烁非常慢,LED 亮起约 11(十一!)秒,然后熄灭相同的时间。
发生了什么?
我们的空循环真的空吗?
它是否充满了饱和脂肪?
另一个 MPLAB X 窗口有助于获得洞察。在执行内存窗口中搜索delay,我们最终找到

虽然不必深入研究生成的汇编代码,但我们仍然可以欣赏编译器的低效:一个空的 for 循环扩展为 18(十八!)条汇编指令。
这就是您所支付的:免费编译器版本根本不提供优化(恶意人士说,免费编译器版本故意在生成的代码中包含膨胀代码)。
现在,由于我们业余爱好者不愿意支付 Microchip 所需的大约 1000 美元购买编译器的 PRO 许可证,或者,用更理想化的方式说,“我们努力理解事物的内部细节”,让我们将注意力转向 MicroMIPS 汇编语言,看看我们是否能做得更好。
当然,我们可以:将以下代码写入新创建的 util.S 文件,然后将其添加到项目中。
  #include <xc.h>
    
   .section .text, code
   .set noreorder
   
   .global asm_delay_1_sec
asm_delay_1_sec:
   li t0, 12000000      // 12 millions cycles
asm_delay_1_sec_l1:
   bnez t0, asm_delay_1_sec_l1 
   addi t0, t0, -1      // this is the MIPS branch delay slot instruction, 
                        // always executed after the previous one
   jrc ra
代码很简单:执行一个具有两个周期体的 12 百万次迭代。然后修改 main 函数如下:
#include <cstdint>
#include <xc.h>
#include "cfg.hpp"
extern "C" 
{
  void asm_delay_1_sec();
}
int main()
{
  config();
  for (;;)
  {
    LATBINV = 0x80; // toggle the RB7 pin state
    asm_delay_1_sec();
  }
}
再次,使用 PICKit 3 工具将其闪存。
这次,闪烁效果很好:红色 LED 的状态正好保持一秒钟。图 12 下方的示波器跟踪图对此进行了很好的确认。

RB7 输出引脚上的电压水平。当然,可以通过修改 C++ 代码的迭代次数来凭经验获得类似的结果,但这并非重点。这里的重点是控制:汇编代码提供了 C++ 代码无法提供的控制。例如,在此项目中,只有 MicroMIPS 汇编代码具有可预测的执行时间。
关注点
无和谐笑话
Harmony 是 Microchip 的一个框架,用 Microchip 的话说(Harmony 网站):“MPLAB® Harmony 是 PIC32 微控制器的灵活、抽象、完全集成的固件开发平台。它采用了模块化和面向对象设计的关键要素,增加了使用实时操作系统(RTOS)或不使用 RTOS 的灵活性,并提供了一个易于使用、可针对您的特定需求进行配置并能和谐工作的软件模块框架。”
事实上,Harmony 生成的代码非常复杂,充满错误,并且调试起来很麻烦。Microchip 论坛上的许多开发者至少在一件事上达成共识:Harmony 只是处理 USB 等复杂硬件外设时必需的、不可避免的邪恶。幸运的是,PIC32MM 微控制器系列只有简单的外设。所以我们不必处理 Harmony,我们的生活将充满欢乐!
MicroMIPS 的优缺点
原始的 MIPS 是一个简洁的 RISC 架构(您还记得 DLX 吗?)。另一方面,microMIPS 架构(PIC32MM 的架构)致力于减小生成代码的大小,并且在这方面做得相当好:根据 Microchip 的说法,MicroMIPS 的可执行代码大小比标准的 MIPS 小约 35%,而执行速度保持相似。
为了实现这个奇迹,我们付出的代价是指令集混乱:对于业余爱好者来说,MicroMIPS 有点难掌握。
结论
这是关于使用 C++ 和 汇编进行 PIC32MM 微控制器编程的系列文章(正在进行中)的第一篇。
本文概述了硬件和软件要求的根本基础,并强调了汇编语言的有用性。不幸的是,没有为 C++ 代码或使用花哨外设的实验留出空间。所有这些应该在系列的下一篇文章中得到弥补。
历史
- 罗马,2018 年 7 月 7 日:已修复电路图图片,感谢 Francisco。
- 罗马,2017 年 7 月 26 日:首次发布




