PIC32 和 MPLAB®X 入门学习包

介绍

Microchip 提供广泛的 32位微控制器产品组合。本指南是为了使用 PIC32MX360F512L 微控制器创建的。虽然有很多品种,但 Microchip 已经方便地为每个32位微控制器家族创建了文档。这些文档将在本指南后面进行解释。本指南的目的是帮助刚刚开始接触Microchip PIC32 的人。本页将指导您使用 MPLABX IDE (集成开发环境)软件,并在示例代码的帮助下向您展示如何设置一些主要外设。需要对嵌入式系统和C编程语言有基本的了解。

工具

硬件

使用入门套件或开发板将使学习 PIC32 更容易。使用入门套件也消除了购买调试器/编程器的需要,因为它们已经内置了一个。下面列出了编写本指南所使用的硬件。需要注意的是,还有其他入门套件也应该可以使用。

产品 描述 包括 型号
PIC32 开发套件 一个小型开发板,旨在让新用户掌握PIC32架构的硬件和软件。该板包括3个led和3个按钮,因此仅使用该板就可以实现非常基本的程序。 PIC32板集成调试器,USB线,3个led板,3个按钮板 DM320001-ND
PIC32扩展板 用于与PIC32套件之一一起使用,包括上面的那个。这不包括微控制器,不会自行工作。该板将使用户访问所有引脚,以便可以实现更多涉及的程序。 扩展板,访问微控制器引脚的多个选项,连接PIC32入门套件的连接器,电源插孔(不包括电源) DM320002-ND
Adafruit 加速度计、陀螺仪、磁力计扩展板 该芯片包括3个传感器,用于添加运动,角度和方向到您的项目。它可以测量加速度(包括重力)、磁力和旋转运动。 扩展板带有LSM9DS0传感器芯片,Pin插头,I2C和SPI接口 1528-1205-ND
Parallax 2轴操纵杆 操纵杆,可用于添加模拟输入到您的项目。两个独立的电位器(每轴一个)控制模拟输出到微控制器。 操纵杆在板上,头已经连接方便面包板 27800-ND
USB转UART接口板 简单的板便于USB到UART通信到您的计算机。 接口板,USB线 336-2613-ND

本教程不需要加速计板,操纵杆和USB到UART(通用异步接收器/发射器)接口板,但稍后将作为很好的示例。它们都相当简单,可以通过外设与PIC32通信。

软件

您将需要编写代码,编译它,并将其编程到PIC上。要做到这一点,你需要一些软件,但幸运的是,Microchip提供免费软件。下面列出了所需软件的列表,以及在Microchip网站上可以找到的链接。

Microchip 还提供 MPLAB IPE (Integrated Programming Environment,集成编程环境) 和 MPLAB Harmony 等其他软件,但这两种软件在本教程中都不会用到。

文档 / 额外的支持

关于PIC32有太多的知识要学习,所以这里有一些其他的资源,你可以用来帮助你使用 PIC32。

标题 描述
PIC32MX 系列参考手册 这是你可能遇到的任何问题的“必修”文档。它深入介绍了 PIC 可以做的所有事情,并列出了所有的寄存器。一开始它可能会让人不知所措,但在习惯它之后,它将成为你最好的朋友。本手册分为52个部分,可以在本表下方的链接中找到。
PIC32MX3XX/4XX数据手册 这将涉及引脚分配,如何将PIC32实现到您自己的板中,并且还涵盖PIC的功能,但不像家庭手册那样深入。本手册适用于零件号以PIC32MX3XX或PIC32MX4XX开头的微控制器。这也可以在下面的链接中找到。
MPLAB®X IDE用户指南 关于使用MPLAB®X IDE你需要知道的一切
PIC32 入门套件用户指南 如果你正在使用它,入门套件的用户指南中有很多关于电路板的有用信息。如果你决定用PIC32构建自己的电路板,本指南有一些很棒的原理图可供参考。
I/O扩展板信息表 如果您正在使用扩展板的入门套件,您将需要这个原型。它有有用的原理图,告诉你板上的每个引脚是什么。
Microchip 开发者帮助 Microchip创建这个教程是为了培训刚接触他们产品的开发人员。这个网站上有很多有用的信息,关于与PIC微控制器有关的一切。这里也有自定节奏和实时在线培训。
用 C 编程 32位微控制器,Lucio Di Jasio著 这本书产生了很多关于这个入门指南的信息。卢西奥从最基础的开始,通过实践培训教你 PIC32 的所有必需品。这本书是在 MPLAB 的旧版本发布时写的,所以并不是所有的代码都能在 MPLAB®X 上正确编译。

32位PIC微控制器的所有文档,包括上面的家庭参考手册和数据手册,都可以在这个网站上找到:32位微控制器(MCU) | Microchip Technology

MPLAB®X IDE

MPLAB®X IDE是用于编写、编译C代码和对 PIC32 进行编程的开发环境。它还有一个模拟器,可以很好地用于调试尚未编程到PIC上的代码。MPLAB®X 有大量的选项和设置,但我们将只是介绍基础知识,让您开始使用它。

创建一个新项目

下载MPLAB和XC32编译器后,现在可以打开MPLAB了。应该如下图所示。

首先要做的是通过进入File>> new project来启动一个新项目。在类别部分点击“Microchip Embedded”,在项目部分点击“Standalone Project”。这些选项将在整个指南中使用。单击Next。

下一页是设备选择页面。在Family部分点击“32位MCU (PIC32)”,在Device部分找到您的设备。如果您使用的是 PIC32 Starter Kit,那么该设备将是PIC32MX360F512L。单击Next。

向导将带您进入下一步的工具选择页面。目前我们将只使用模拟器工具。该工具不允许您对PIC进行编程,但它可以让您编译和调试代码。单击Next。

接下来您将选择编译器。只需点击 XC32,然后点击Next。

最后一个画面是选择项目名称和文件夹画面。在这里你可以给你的项目命名,并选择保存的位置。不要担心Encoding选项。选择Finish,你就创建了一个新项目!

现在您的MPLAB®IDE 窗口应该如下所示。

你会注意到你的项目是空的。要开始编写代码,你需要一个源文件,可以通过右键单击“源文件”选项卡并选择New>>C source file来添加源文件。命名你的源文件,然后选择Finish。现在可以开始编写代码了!

写入寄存器

有多种方法可以写入PIC的寄存器。如何写的选择主要取决于个人喜好或代码的阅读难易程度。本指南中并没有提到每一种方法,但这里有几种常见的方法。

直接写入整个寄存器。这将把寄存器中的每个位配置成你想要的。示例:TRISD = 0x0000

使用SET、CLR、INV寄存器。只有用’ 1 ‘指定的位才会被修改。以’ 0 '指定的位不会被修改

SET将设置这些位。例如:TRISASET = 0x0001只会设置寄存器TRISA中的0位

CLR会清除这些位。例如:PORTDCLR = 0x0002只会清除寄存器PORTD中的1位

INV将反转这些位。例如:LATCINV = 0x0003只会反转寄存器LATC中的0位和1位

分别写入每个位。这和写入整个寄存器将是本教程中最常见的方法。这个方法有一个公式REGISTERbits.BIT,其中REGISTER是你输入位所在寄存器的名称,BIT是位的名称。例子:LATCbits。LATC6 = 1将在寄存器LATC中写入一个1到位的LATC6。

PIC32 的编程

要对PIC进行编程,您需要从模拟器工具切换到使用调试器。要做到这一点,请单击仪表板窗口左侧的扳手。Dashboard窗口是屏幕左下方的窗口。当项目属性窗口出现时,在“硬件工具”列表下选择您正在使用的调试器。如果您使用的是PIC32 Starter Kit,则调试器工具将在“Microchip Stater Kits”文件夹中找到,然后在“Legacy Starter Kits”文件夹中找到。它应该被列为“SKDE PIC32”。

一旦选择了正确的调试器并连接了PIC,最好在对PIC进行编程之前清理以构建项目。这将确保你的代码是正确编写的,没有语法错误。清理和构建按钮位于屏幕顶部,看起来像一把锤子,前面放着一把扫帚。项目建成后,没有错误,你可以通过点击按钮来编程你的PIC,按钮上有一个向下指向IC的绿色箭头。这是“制作和编程设备主项目”按钮。

时钟配置

在PIC32上设置时钟一开始可能会让人不知所措,因为提供了许多选项。从选择内部振荡器或可选的外部振荡器到对时钟进行除法和乘法以获得您想要的确切频率,选择系统时钟的选项并不缺乏。

振荡器的选项

有四个选择振荡器的选项。有一个快速内部RC振荡器,一个低功率内部RC振荡器和连接两个外部振荡器的选项。在本指南中,我们将使用快速内部RC振荡器,其频率为8MHz。PIC32 Starter Kit也有一个主外部8MHz振荡器。

低功耗内部和辅助外部振荡器通常具有低频,用于实时时钟/日历(RTCC)应用,看门狗定时器源和上电定时器等用途。

主振荡器时钟链

时钟在被用作系统时钟之前要经过许多阶段。这些阶段让你选择你想要为你的应用程序使用的频率。这张图来自Lucio Di Jasio的书,展示了时钟经历的三个阶段。

第一阶段是输入分频器。接下来是锁相环电路(PLL)。为了工作,在进入PLL之前,频率必须在4MHz到5MHz之间,所以我们必须使用输入分频器将8MHz的内部振荡器除以2。PLL电路会将频率相乘。最后一级是输出分频器,之后是系统时钟。

配置时钟

要设置时钟,你可以去查阅庞大的家族参考手册,搜索正确的寄存器,通读所有选项,然后编写自己的代码,或者你可以使用Microchip提供的漂亮的“配置位”选项。

要找到这个菜单,点击MPLAB IDE窗口顶部的“窗口”,然后向下进入“PIC内存视图”。侧边会弹出一个菜单。点击“配置位”。源代码窗口下方会弹出一个如图所示的窗口。

这个菜单非常有用,会加快时钟配置过程。这个菜单中最重要的两列是最右边的两列。“类别”告诉你要更改什么,“设置”告诉你已经更改为什么。在本指南中,我们将使用快速内部RC振荡器使系统时钟运行在20MHz。我们也将使用这个频率来运行外围设备。下面列出了我们将从默认设置更改的选项
• 振荡器运行在8MHz,但我们需要它在4MHz和5MHz之间才能进入锁相环,所以我们必须为输入分频器选项选择“2x分频器”。
• 选择“20倍倍增器”为锁相环倍增器选项。
• 输出分频器选项选择“PLL DivIDE by 4”。输入分频器后,频率为4MHz。经过PLL乘法器后,频率是80MHz,所以我们要除以4得到20MHz。
• 振荡器选择选项选择“Fast RC Osc with PLL”。
• 禁用副振荡器。
• 禁用内部/外部切换。
• 外设时钟除数选项选择“Pb_Clk is Sys_Clk/1”。这将使外设时钟以与系统时钟相同的频率运行。
• 禁用看门狗定时器。
“配置位”窗口现在应该是这样的。

这些设置将用于本指南中使用的每个示例。要把这些放到你的代码中,你需要点击“生成源代码输出”。会出现一个新窗口。只需高亮显示所有文本,并将其复制粘贴到源代码的开头,就可以了!代码应该看起来像下面的代码。在本教程的其余部分中,这将是所有示例代码的顶部。

// PIC32MX360F512L Configuration Bit Settings
 
// 'C' source line config statements
 
// DEVCFG3
// USERID = No Setting
 
// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20         // PLL Multiplier (20x Multiplier)
#pragma config FPLLODIV = DIV_4         // System PLL Output Clock Divider (PLL Divide by 4)
 
// DEVCFG1
#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = ON            // CLKO Output Signal Active on the OSCO Pin (Enabled)
#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
 
// DEVCFG0
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
#pragma config ICESEL = ICS_PGx2        // ICE/ICD Comm Channel Select (ICE EMUC2/EMUD2 pins shared with PGC2/PGD2)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)
 
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
 
#include <xc.h>

GPIO 配置

通用输入输出(GPIO)在嵌入式系统中是必不可少的。它们是微控制器最基本的功能之一,但也是最重要的功能之一。GPIO引脚可以让你控制和监控数字电子产品。

pic32上有多个I/O端口。要知道你的设备有多少个,你必须去文档部分的PIC32MX3XX/4XX数据手册。大多数I/O引脚也有其他用途,所以要小心使用哪些引脚。使用PIC32入门套件,电路板上有3个led和3个按钮,我们将使用它们作为我们的输出和输入组件。

重要的寄存器

有三个重要的I/O寄存器;TRIS(三状态)、PORT和LAT寄存器。使用这三个寄存器,您可以将引脚设置为输入或输出,并对该引脚进行读写操作。

TRIS寄存器可以让你选择引脚是输入还是输出。在使用I/O引脚之前,必须先配置这个寄存器。上电复位后的默认设置是它们都被定义为输入。

TRIS bit = 1→pin配置为输入

TRIS bit = 0→pin配置为输出

一个方便的记住这个的方法是,1看起来像一个“I”,0看起来像一个“O”

PORT和LAT寄存器允许您对I/O引脚进行读写。它们彼此非常相似,所以这可能会令人困惑,但是在读写这些寄存器时要遵循一些规则。

始终向LAT寄存器写入

总是从PORT寄存器读

写入PORT寄存器也会更新LAT寄存器,但最好是写入LAT寄存器。当读取PORT寄存器时,它会告诉你该引脚在任何给定时间的实际状态。读LAT寄存器只会告诉你最后写的东西。如果您将LAT位设置为高,但引脚意外接地,则LAT位将读取高,因为这是您写入的内容,但PORT位将读取低,因为这是该引脚上的实际电压。

例子

在本例中,将使用PIC32入门套件led和按钮。我们将简单地使led在其对应的按钮被按下时打开。led连接到引脚RD0, RD1和RD2。按钮连接到RD6、RD7和RD13。示例代码如下所示。需要注意的是,输入引脚上有上拉电阻,所以要判断按钮是否被按下,需要检查信号是否变低。
GPIO****的例子

// PIC32MX360F512L Configuration Bit Settings
 
// 'C' source line config statements
 
// DEVCFG3
// USERID = No Setting
 
// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20         // PLL Multiplier (20x Multiplier)
#pragma config FPLLODIV = DIV_4         // System PLL Output Clock Divider (PLL Divide by 4)
 
// DEVCFG1
#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = ON            // CLKO Output Signal Active on the OSCO Pin (Enabled)
#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
 
// DEVCFG0
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
#pragma config ICESEL = ICS_PGx2        // ICE/ICD Comm Channel Select (ICE EMUC2/EMUD2 pins shared with PGC2/PGD2)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)
 
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
 
#include <xc.h>
 
#define LED1        LATDbits.LATD0
#define LED2        LATDbits.LATD1
#define LED3        LATDbits.LATD2
#define SW1         PORTDbits.RD6
#define SW2         PORTDbits.RD7
#define SW3         PORTDbits.RD13
  
 
main()
{
    /*Configure tri-state registers*/
    TRISDbits.TRISD6 = 1;   //SW1 as input
    TRISDbits.TRISD7 = 1;   //SW2 as input
    TRISDbits.TRISD13 = 1;  //SW3 as input
     
    TRISDbits.TRISD0 = 0;   //LED1 as output
    TRISDbits.TRISD1 = 0;   //LED2 as output
    TRISDbits.TRISD2 = 0;   //LED3 as output
     
    while(1)
    {
        LED1 = !SW1;    //LED1 turns on when SW1 is pressed
        LED2 = !SW2;    //LED2 turns on when SW2 is pressed
        LED3 = !SW3;    //LED3 turns on when SW3 is pressed
    }
}

UART 配置

通用异步接收/发送器(UART)是一种不使用时钟而负责实现串行通信的设备。它不使用时钟来跟踪发送和接收的数据位,而是使用指定的数据发送速率(称为波特率)。PIC32有两个UART模块。从家族参考手册第21节第26页开始,有一个很好的UART配置指南。像PIC32上的大多数其他东西一样,在配置UART时有很多选项,但我们将介绍操作它所需的选项。

要使用PIC32的UART,你需要一些可以通信的东西。在这个例子中,硬件部分提到的USB to UART接口板是通过I/O扩展板连接到PIC32 Starter Kit上的。接口板的USB端连接到运行终端程序(HyperTerm、PuTTY等)的计算机上。如果你是终端程序新手,这里有一个很好的资源:Serial terminal Basics - SparkFun Learn

重要的寄存器

用于配置和读写UART的重要寄存器是UxMODE、UxSTA、UxBRG、UxTXREG和UxRXREG寄存器。上面每个寄存器中的’ x '表示您正在使用哪个UART模块。在这个演示中,我们将使用模块1,因此UxMODE将是U1MODE。

首先,我们将设置波特率。要做到这一点,我们需要在U1MODE寄存器中配置一个位,准确地说是BRGH位,并将一个值写入U1BRG寄存器。下面的两个方程描述了如何配置波特率。

可以看到,BRGH位只改变了外设总线时钟频率(记住这等于20MHz的系统时钟)除以的因子。无论您将UxBRG寄存器设置为什么值,都将被放置到这个方程中。以下是我们将用于获得9600波特率的值:
• U1MODEbits。BRGH = 0,因为9600是一个很小的波特率所以最好把外围总线时钟频率尽可能的分下去。
• U1BRG = 129,这样我们得到的波特率是9615.4,只比9600高0.16%,所以已经足够接近可以工作了。
UxMODE寄存器是UART模块的控制寄存器。大部分配置都是通过写入这个寄存器来完成的。在本教程中,我们将使用波特率为9600,8位数据和1个停止位。以下是需要在U1MODE寄存器中配置的位,用于本演示:
• SIDL = 0, UART操作在睡眠模式期间继续进行
• IREN = 0,关闭红外编码器和解码器
• RTSMD = 0, RTS引脚处于流量控制模式
• UEN = 0,使用收发数据的引脚(而不是随机选择GPIO引脚),CTS和RTS引脚由PORT锁存器控制
• WAKE = 1,在睡眠模式中检测到start-bit时,UART被唤醒
• LPBACK = 0,禁用环回模式
• RXINV = 0,接收引脚的空闲状态为“1”
• PDSEL = 0, 8位数据,无奇偶校验位
• STSEL = 0,1位停止位
• ON = 1, UART模块开启
UxSTA寄存器是状态寄存器。这个寄存器中有几个位需要配置,但这个寄存器主要用于确定发送或接收缓冲区是否已满,以及其他给出UART状态的东西。下面是几个需要配置的位:
• UTXINV = 0,发送引脚的空闲状态为“1”
• URXEN = 1, UART接收端使能
• UTXEN = 1, UART发送器使能
UxTXREG和UxRXREG寄存器是数据实际通信的地方。你把数据写入UxTXREG寄存器发送出去,你从UxRXREG寄存器中读取,看看已经发送给你的是什么。下面的例子将展示如何使用这些寄存器。
例子
这些示例包括配置UART模块的代码,您可以在自己的代码中使用的几个函数,以及它们的实现示例。
第一个函数演示了如何轻松配置UART模块。

initUART

void initUART(void)
{
    U1MODEbits.BRGH = 0;                // Baud Rate = 9600
    U1BRG = 129;
     
    U1MODEbits.SIDL = 0;                // Continue operation in SLEEP mode
     
    U1MODEbits.IREN = 0;                // IrDA is disabled
     
    U1MODEbits.RTSMD = 0;               // U1RTS pin is in Flow Control mode
     
    U1MODEbits.UEN = 0b00;              // U1TX, U1RX are enabled
     
    U1MODEbits.WAKE = 1;                // Wake-up enabled
     
    U1MODEbits.LPBACK = 0;              // Loopback mode is disabled
     
    U1MODEbits.RXINV = 0;               // U1RX IDLE state is '1'
     
    U1MODEbits.PDSEL = 0b00;            // 8-bit data, no parity
     
    U1MODEbits.STSEL = 0;               // 1 stop bit
     
    U1STAbits.UTXINV = 0;               // U1TX IDLE state is '1'
     
    U1MODEbits.ON = 1;                  // UART1 is enabled
     
    U1STAbits.URXEN = 1;                // UART1 receiver is enabled
     
    U1STAbits.UTXEN = 1;                // UART1 transmitter is enabled
}

接下来的两个函数将分别通过UART行发送一个字符和一个字符串。函数的输入是你想通过UART发送的任何字符或字符串。

SendChar

void SendChar(char c)
{
    U1STAbits.UTXEN = 1;                // Make sure transmitter is enabled
    // while(CTS)                       // Optional CTS use
    while(U1STAbits.UTXBF);             // Wait while buffer is full
    U1TXREG = c;                        // Transmit character
}

SendString

void SendString(char *string)
{
     
   int i = 0;
     
   U1STAbits.UTXEN = 1;                // Make sure transmitter is enabled
     
   while(*string)
    {
        while(U1STAbits.UTXBF);         // Wait while buffer is full
        U1TXREG = *string;              // Transmit one character
        string++;                       // Go to next character in string
    }
}

在我们得到完整示例之前,最后两个函数是用于通过UART接收信息的。第一个函数将读取单个字符,第二个函数将读取指定长度的字符串。

ReadChar

char ReadChar(void)
{
    //RTS = 0                           // Optional RTS use
    while(!U1STAbits.URXDA)             // Wait for information to be received
    //RTS = 1
    return U1RXREG;                     // Return received character
}

ReadString

void ReadString(char *string, int length)
{  
    int count = length;
     
    do
    {
        *string = ReadChar();               // Read in character
        SendChar(*string);                  // Echo character
         
        if(*string == 0x7F && count>length) // Backspace conditional
        {
            length++;
            string--;
            continue;
        }
         
        if(*string == '\r')                 // End reading if enter is pressed
            break;
         
        string++;
        length--;
         
    }while(length>1);
     
    *string = '\0';                         // Add null terminator
}

在本例中,UART转USB接口板将同时连接PIC32 Starter Kit和运行PuTTY的台式计算机。每当在终端中输入字符串并按enter键时,PIC就会将字符串回显到终端程序中。RTS和CTS引脚不会被用来控制数据流,所以我们就把它们设为0。

UART 的例子

// PIC32MX360F512L Configuration Bit Settings
 
// 'C' source line config statements
 
// DEVCFG3
// USERID = No Setting
 
// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20         // PLL Multiplier (20x Multiplier)
#pragma config FPLLODIV = DIV_4         // System PLL Output Clock Divider (PLL Divide by 4)
 
// DEVCFG1
#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = ON            // CLKO Output Signal Active on the OSCO Pin (Enabled)
#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
 
// DEVCFG0
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
#pragma config ICESEL = ICS_PGx2        // ICE/ICD Comm Channel Select (ICE EMUC2/EMUD2 pins shared with PGC2/PGD2)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)
 
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
 
#include <xc.h>
  
 
void initUART(void);
void SendChar(char c);
void SendString(char *string);
char ReadChar(void);
void ReadString(char *string, int length);
 
main()
{
    initUART();
    PORTDbits.RD14 = 0;                 // Set RTS and CTS pins to 0
    PORTDbits.RD15 = 0;
    char string[90];
    while(1)
    {
        ReadString(string,90);          // Read in a string
        SendString(string);             // Echo that string
    }
}
  
 
void initUART(void)
{
    U1MODEbits.BRGH = 0;                // Baud Rate = 9600
    U1BRG = 129;
     
    U1MODEbits.SIDL = 0;                // Continue operation in SLEEP mode
     
    U1MODEbits.IREN = 0;                // IrDA is disabled
     
    U1MODEbits.RTSMD = 0;               // U1RTS pin is in Flow Control mode
     
    U1MODEbits.UEN = 0b00;              // U1TX, U1RX are enabled
     
    U1MODEbits.WAKE = 1;                // Wake-up enabled
     
    U1MODEbits.LPBACK = 0;              // Loopback mode is disabled
     
    U1MODEbits.RXINV = 0;               // U1RX IDLE state is '1'
     
    U1MODEbits.PDSEL = 0b00;            // 8-bit data, no parity
     
    U1MODEbits.STSEL = 0;               // 1 stop bit
     
    U1STAbits.UTXINV = 0;               // U1TX IDLE state is '1'
     
    U1MODEbits.ON = 1;                  // UART1 is enabled
     
    U1STAbits.URXEN = 1;                // UART1 receiver is enabled
     
    U1STAbits.UTXEN = 1;                // UART1 transmitter is enabled
}
  
 
void SendChar(char c)
{
    U1STAbits.UTXEN = 1;                // Make sure transmitter is enabled
    // while(CTS)                       // Optional CTS use
    while(U1STAbits.UTXBF);             // Wait while buffer is full
    U1TXREG = c;                        // Transmit character
}
  
 
void SendString(char *string)
{
     
   int i = 0;
     
    U1STAbits.UTXEN = 1;                // Make sure transmitter is enabled
     
    while(*string)
    {
        while(U1STAbits.UTXBF);         // Wait while buffer is full
        U1TXREG = *string;              // Transmit one character
        string++;                       // Go to next character in string
    }
}
  
 
char ReadChar(void)
{
    //PORTDbits.RD15 = 0;                // Optional RTS use
    while(!U1STAbits.URXDA);             // Wait for information to be received
    //PORTDbits.RD15 = 1;
    return U1RXREG;                      // Return received character
}
  
 
void ReadString(char *string, int length)
{  
    int count = length;
     
    do
    {
        *string = ReadChar();               // Read in character
        //SendChar(*string);                  // Echo character
         
        if(*string == 0x7F && count>length) // Backspace conditional
        {
            length++;
            string--;
            continue;
        }
         
        if(*string == '\r')                 // End reading if enter is pressed
            break;
         
        string++;
        length--;
         
    }while(length>1);
     
    *string = '\0';                         // Add null terminator
}

SPI 配置

串行外设接口(SPI)是用于设备间通信的接口。与UART不同,SPI接口是同步的,这意味着两个设备之间有一个时钟连接,以保持它们的同步。异步通信不能保证两个设备都以相同的速率运行,因此同步通信通常是传输和接收数据的更好方式。使用SPI总线的一大优点是可以与多个设备进行通信。进行SPI通信需要连接4个引脚;Master Out Slave In (MOSI), Master In Slave Out (MISO),时钟,还有一个芯片选择。MOSI线和MISO线是承载信息的线路。芯片选择引脚选择与哪个设备通信。这里有一个很好的资源来刷新SPI信息: 串行外设接口(SPI) - SparkFun Learn

PIC32有两个独立的SPI总线。使用SPI总线几乎与使用UART通信完全相同。PIC可以配置为主总线或从总线。从家庭参考手册第23节的第21页开始,有关于PIC32 SPI总线以及如何设置它们的信息。稍后在本指南中,我们还将做设置SPI总线的示例。在示例中,将使用加速度计进行通信。

重要的寄存器

配置和从SPI总线读写最重要的寄存器是SPIxCON、SPIxSTAT、SPIxBUF和SPIxBRG寄存器。

在使用SPI总线进行通信之前,我们必须初始化模块并将其配置为我们的规范。这包括选择一次将通信多少信息,时钟频率和时钟的空闲状态等选项。大多数选项都是使用SPIxCON寄存器配置的。

首先,必须设置时钟频率。这是通过向SPIxBRG寄存器写入一个值来完成的。与UART的配置非常相似,有一个简单的公式来确定时钟速率,如下所示。

FPB是外围总线时钟,我们将其设置为20MHz。在示例中,我们将使用2.5MHz的SPI时钟频率,因此我们将SPIxBRG设置为3

在配置SPI总线时,我们现在将回顾SPIxCON寄存器中最重要的选项。在后面的例子中,SPI总线将是这样配置的:

FRMEN = 0,禁用帧SPI支持

SIDL = 0,在空闲模式下继续操作

DISSDO = 0, SDO (data out)引脚由模块控制

MODE16 = 1, MODE32 = 0, 16位数据宽度

CKP = 1,时钟空闲状态高

CKE = 0,从空闲时钟状态过渡到活动时钟状态时数据发生变化

SSEN = 0, SSx引脚未用于从模式

MSTEN = 1, PIC32为master

SMP = 1,输入数据在数据输出时间结束时采样

ON = 1, SPI外设使能

配置SPI外设时,只需要SPIxSTAT寄存器中的1位。SPIROV = 0将清除任何已经发生的溢出。SPIxSTAT寄存器中的其他位将在稍后读写SPI总线时使用。

SPIxBUF寄存器是要发送的信息被写入的地方,也是接收到的信息被读取的地方。这个缓冲区在发送信息的同时读取信息,所以它是非常高效的。与UART不同的是,UART有一个缓冲器用于传输数据,另一个缓冲器用于接收信息,SPI外设只使用一个缓冲器用于两者。

例子

这些示例将密切遵循UART示例的格式。将有一个配置SPI外设的示例,一个从缓冲区写入和读取的函数,以及它们的实现示例。

第一个例子展示了如何设置SPI外设。

initSPI

void initSPI(void)
{
    CS = 1;                     // Set CS high (idle state)
     
    IEC0bits.SPI1EIE = 0;       // SPI interrupts disabled
    IEC0bits.SPI1RXIE = 0;
    IEC0bits.SPI1TXIE = 0;
     
    SPI1CONbits.ON = 0;         // Turn off SPI module
     
    SPI1BUF = 0;                // Clear the receive buffer
     
    SPI1BRG = 3;                // FSCK = 2.5MHz
     
    SPI1STATbits.SPIROV = 0;    // Clear overflow flag
     
     
    /* SPI1CON settings */
    SPI1CONbits.FRMEN = 0;      // Framed SPI support is disabled
    SPI1CONbits.SIDL = 0;       // Continue operation in IDLE mode
    SPI1CONbits.DISSDO = 0;     // SDO1 pin is controlled by the module
    SPI1CONbits.MODE16 = 1;     // 16 bit mode
    SPI1CONbits.MODE32 = 0;
    SPI1CONbits.CKP = 1;        // Idle state for clock is high, active state is low
    SPI1CONbits.CKE = 0;        // Output data changes on transition from idle to active
    SPI1CONbits.SSEN = 0;       // Not in slave mode
    SPI1CONbits.MSTEN = 1;      // Master mode
    SPI1CONbits.SMP = 1;        // Input data sampled at the end of data output time
     
    SPI1CONbits.ON = 1;         // Turn module on
}

下一个例子是一个函数,用于向SPI缓冲区写入,同时也读取接收到的信息。这个函数的输入就是需要传输的数据,返回的值就是接收到的信息。

WriteReadSPI

short WriteReadSPI(unsigned short i)
{
    CS = 0;                         // Set the chip select low
    SPI1BUF = i;                    // Write to buffer for transmission
    while (!SPI1STATbits.SPIRBF);   // Wait for transfer to be completed
    CS = 1;                         // Set the chip select back high
    return SPI1BUF;                 // Return the received value
}

最后一个例子是配置和写入函数的实现。在这个例子中,PIC32初始化加速度计,并不断地从加速度计中读取数据。然后,这些数据就可以显示出来,或者用来做任何你想做的事情。

SPI 的例子


// PIC32MX360F512L Configuration Bit Settings
 
// 'C' source line config statements
 
// DEVCFG3
// USERID = No Setting
 
// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20         // PLL Multiplier (20x Multiplier)
#pragma config FPLLODIV = DIV_4         // System PLL Output Clock Divider (PLL Divide by 4)
 
// DEVCFG1
#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = ON            // CLKO Output Signal Active on the OSCO Pin (Enabled)
#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
 
// DEVCFG0
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
#pragma config ICESEL = ICS_PGx2        // ICE/ICD Comm Channel Select (ICE EMUC2/EMUD2 pins shared with PGC2/PGD2)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)
 
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
 
#include <xc.h>
  
void initSPI(void);
short WriteReadSPI(unsigned short i);
void initAccel(void);
float ReadAccelX(void);
 
#define CS LATDbits.LATD1
  
 
main()
{
    TRISDbits.TRISD1 = 0;       // make CS an output
    initSPI();                  // initialize SPI module
    initAccel();                // initialize the accelerometer
    float X;
     
    while(1)
    {
        X = ReadAccelX();       // get an accelerometer value
         
        /*Use the value to do something*/
    }
}
 
 
void initSPI(void)
{
    CS = 1;              // Set chip select high (idle state is high)
     
    IEC0bits.SPI1EIE = 0;       // SPI interrupts disabled
    IEC0bits.SPI1RXIE = 0;
    IEC0bits.SPI1TXIE = 0;
     
    SPI1CONbits.ON = 0;         // Turn off SPI module
     
    SPI1BUF = 0;                // Clear the receive buffer
     
    SPI1BRG = 3;                // FSCK = 2.5MHz
     
    SPI1STATbits.SPIROV = 0;    // Clear overflow flag
     
     
    /* SPI1CON settings */
    SPI1CONbits.FRMEN = 0;      // Framed SPI support is disabled
    SPI1CONbits.SIDL = 0;       // Continue operation in IDLE mode
    SPI1CONbits.DISSDO = 0;     // SDO1 pin is controlled by the module
    SPI1CONbits.MODE16 = 1;     // 16 bit mode
    SPI1CONbits.MODE32 = 0;
    SPI1CONbits.CKP = 1;        // Idle state for clock is high, active state is low
    SPI1CONbits.CKE = 0;        // Output data changes on transition from idle to active
    SPI1CONbits.SSEN = 0;       // Not in slave mode
    SPI1CONbits.MSTEN = 1;      // Master mode
    SPI1CONbits.SMP = 1;        // Input data sampled at the end of data output time
     
    SPI1CONbits.ON = 1;         // Turn module on
}
  
short WriteReadSPI(short i)
{
    CS = 0;                         // Set the chip select low
    SPI1BUF = i;                    // Write to buffer for transmission
    while (!SPI1STATbits.SPIRBF);   // Wait for transfer to be completed
    CS = 1;                         // Set the chip select back high
    return SPI1BUF;                 // Return the received value
}
 
void initAccel(void)
{
    WriteReadSPI(0b0010000001100111); // CTRL_REG1_XM
    WriteReadSPI(0b0010010010010100); // CTRL_REG5_XM
    WriteReadSPI(0b0010010100000000); // CTRL_REG6_XM
    WriteReadSPI(0b0010011000000000); // CTRL_REG7_XM
}
 
float ReadAccelX(void)
{
   short X_H = WriteReadSPI(0b1010100100000000);    // Read first data register
   short X_L = WriteReadSPI(0b1010100000000000);    // Read second data register
   X_L = X_L & 0b0000000011111111;                  // Combine the data from both registers
   X_H = X_H << 8;
   X_H = X_H & 0b1111111100000000;
   signed short X = X_H | X_L;
   float value = X * 0.000061;                      // Convert to units of g
   return value;
}

在这个例子中,你可能会对发送到加速度计的看似随机的数字感到困惑,但这些并不重要。SPI模块和函数的实现是这个例子中最重要的部分。

ADC 配置

模数转换器(adc)是为您的项目添加与物理世界的兼容性的好方法。adc将从电路或传感器(如电位器电路或温度传感器)获取模拟电压信号,并将该信号转换为PIC32可以理解和使用的数字信号。PIC32允许多达16个模拟输入引脚用于其10位ADC。这是很多输入!有关PIC32 ADC的信息可以在家族参考手册的第17节中找到。

在本教程中,视差操纵杆将与PIC的ADC一起使用。操纵杆简单来说就是两个独立的电位器电路,每个方向一个。示例中的操纵杆将需要2个不同的输入引脚。操纵杆是一种廉价、简单的ADC玩法。

重要寄存器和示例

由于PIC32上只有一个ADC模块,因此没有一个寄存器名会像前面那样包含“x”。与UART和SPI一样,ADC配置有很多选项,但本教程将帮助您从简单的配置开始。与ADC模块相关的寄存器相当多,所以这里不会全部列出。配置模块最重要的是AD1CON1、AD1CON2和AD1CON3寄存器。有关配置ADC的更多帮助,可以在第17节第26页的系列参考手册中找到详细的指南。

与本指南之前的配置示例不同,我们将从如何简单配置ADC模块的示例函数开始,然后解释设置。该函数可以在下面找到。

initADC

void initADC(void)
{
    AD1PCFGbits.PCFG0 = 0;          // Analog input in Analog mode
    AD1PCFGbits.PCFG1 = 0;
    TRISBbits.TRISB0 = 1;           // Pin set as input
    TRISBbits.TRISB1 = 1;
     
    AD1CHSbits.CH0NA = 0;           // Channel 0 negative input is VR-
    AD1CHSbits.CH0SA = 0;           // Channel 0 positive input is AN0
     
    AD1CON1bits.FORM = 0;           // Integer 16-bit output
     
    AD1CON1bits.SSRC = 0b111;       // Internal counter ends sampling and starts conversion
     
    AD1CSSL = 0;                    // No scanning required
     
    AD1CON2bits.VCFG = 0;           // Internal voltage references
     
    AD1CON2bits.CSCNA = 0;          // Do not scan inputs
     
    AD1CON2bits.BUFM = 0;           // Buffer configured as one 16-word buffer
     
    AD1CON2bits.ALTS = 0;           // Always use MUX A input multiplexer settings
     
    AD1CON3bits.ADRC = 0;           // Clock derived from PBclock
    AD1CON3bits.ADCS = 0b00111111;  // TAD = 2*TPB
     
    AD1CON3bits.SAMC = 0b11111;     // 31 TAD auto-sample time
     
    AD1CON1bits.ON = 1;             // A/D converter module is operating
}

以下是对每个设置的解释:
• AD1PCFGbits。PCFG0和AD1PCFGbits。PCFG1 = 0,将引脚AN0和AN1置于模拟模式
• TRISBbits。TRISB0和TRISBbits。TRISB1 = 1,设置引脚AN0和AN1作为输入
• AD1CHSbits。CH0NA = 0,使负参考等于VR-
• AD1CHSbits。CH0SA = 0,使AN0成为ADC的输入(稍后可以更改)
• AD1CON1bits。FORM = 0,将输出设置为16位整数
• AD1CON1bits。SSRC = 0b111,内部计数器结束采样并开始转换(自动转换)
• AD1CSSL = 0,无输入扫描
• AD1CON2bits。VCFG = 0,使用内部参考电压(VDD和VSS)
• AD1CON2bits。CSCNA = 0,模块不扫描输入
• AD1CON2bits。BUFM = 0,缓冲区配置为一个16字缓冲区
• AD1CON2bits。ALTS = 0,始终使用MUX A输入多路器设置
• AD1CON3bits。ADRC = 0,时钟来源于外设总线时钟
• AD1CON3bits。ADCS = 0b00111111,设置转换时钟
• AD1CON3bits。SAMC = 0b11111,设置自动采样时间
• AD1CON1bits。ON = 1,开启ADC模块
为了使用ADC,您需要启动一个采样过程,然后从缓冲区中读取一个值。本例中使用的输出格式是一个无符号16位整数,其范围为0-1023。当操纵杆向一个方向推时,ADC将输出一个接近(但不等于)0的值,当它向另一个方向推时,它将接近1023。当操纵杆静止时,该值将接近511,但要找到确切的数字,需要做一些测试。从ADC读取数值相当简单,可以用一个小函数完成,可以在下面找到。输入就是你想要读取的通道。对于操纵杆,选项是0或1。
ReadADC

int ReadADC(int ch)
{
    AD1CHSbits.CH0SA = ch;          // Select input channel
    AD1CON1bits.SAMP = 1;           // Start sampling
    while(!AD1CON1bits.DONE);       // Wait for conversion to complete
    return ADC1BUF0;                // Read conversion result
}

下一个例子是让ADC快速工作的模板。要将其实现到您自己的项目中,只需将操纵杆的输出连接到PIC32的AN0和AN1引脚,并填写条件语句中的空白即可。在这个例子中,你会注意到,当操纵杆没有被推动时,有一个“缓冲区”。这是确保在释放操纵杆时不会发生任何事情的最安全的方法。

ADC 的例子

// PIC32MX360F512L Configuration Bit Settings
 
// 'C' source line config statements
 
// DEVCFG3
// USERID = No Setting
 
// DEVCFG2
#pragma config FPLLIDIV = DIV_2         // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20         // PLL Multiplier (20x Multiplier)
#pragma config FPLLODIV = DIV_4         // System PLL Output Clock Divider (PLL Divide by 4)
 
// DEVCFG1
#pragma config FNOSC = FRCPLL           // Oscillator Selection Bits (Fast RC Osc with PLL)
#pragma config FSOSCEN = OFF            // Secondary Oscillator Enable (Disabled)
#pragma config IESO = OFF               // Internal/External Switch Over (Disabled)
#pragma config POSCMOD = OFF            // Primary Oscillator Configuration (Primary osc disabled)
#pragma config OSCIOFNC = ON            // CLKO Output Signal Active on the OSCO Pin (Enabled)
#pragma config FPBDIV = DIV_1           // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/1)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576        // Watchdog Timer Postscaler (1:1048576)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
 
// DEVCFG0
#pragma config DEBUG = OFF              // Background Debugger Enable (Debugger is disabled)
#pragma config ICESEL = ICS_PGx2        // ICE/ICD Comm Channel Select (ICE EMUC2/EMUD2 pins shared with PGC2/PGD2)
#pragma config PWP = OFF                // Program Flash Write Protect (Disable)
#pragma config BWP = OFF                // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF                 // Code Protect (Protection Disabled)
 
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
 
#include <xc.h>
 
void initADC(void);
int ReadADC(int ch);
 
main()
{
    initADC();              // Initialize ADC module
     
    int x,y;
     
    while(1)
    {
        x = ReadADC(0);     // Set x equal to channel 0 value
        if(x>521)           // When joystick is pushed one way
        {                   // (could be horizontal or vertical direction)
 
        }
        else if(x<501)      // Joystick pushed the other way
        {
 
        }
        else                // Joystick is at rest or being pushed in
        {                   // perpendicular direction only
 
        }
         
         
        y = ReadADC(1);     // Set y equal to channel 1 value
         
        if(y>521)           // When joystick is pushed one way
        {                   // (could be horizontal or vertical direction)
                            
        }
        else if(y<501)      // Joystick pushed the other way
        {
             
        }
        else                // Joystick is at rest or being pushed in
        {                   // perpendicular direction only
             
        }
    }
}
 
void initADC(void)
{
    AD1PCFGbits.PCFG0 = 0;          // Analog input in Analog mode
    AD1PCFGbits.PCFG1 = 0;
    TRISBbits.TRISB0 = 1;           // Pin set as input
    TRISBbits.TRISB1 = 1;
     
    AD1CHSbits.CH0NA = 0;           // Channel 0 negative input is VR-
    AD1CHSbits.CH0SA = 0;           // Channel 0 positive input is AN0
     
    AD1CON1bits.FORM = 0;           // Integer 16-bit output
     
    AD1CON1bits.SSRC = 0b111;       // Internal counter ends sampling and starts conversion
     
    AD1CSSL = 0;                    // No scanning required
     
    AD1CON2bits.VCFG = 0;           // Internal voltage references
     
    AD1CON2bits.CSCNA = 0;          // Do not scan inputs
     
    AD1CON2bits.BUFM = 0;           // Buffer configured as one 16-word buffer
     
    AD1CON2bits.ALTS = 0;           // Always use MUX A input multiplexer settings
     
    AD1CON3bits.ADRC = 0;           // Clock derived from PBclock
    AD1CON3bits.ADCS = 0b00111111;  // TAD = 2*TPB
     
    AD1CON3bits.SAMC = 0b11111;     // 31 TAD auto-sample time
     
    AD1CON1bits.ON = 1;             // A/D converter module is operating
}
 
int ReadADC(int ch)
{
    AD1CHSbits.CH0SA = ch;          // Select input channel
    AD1CON1bits.SAMP = 1;           // Start sampling
    while(!AD1CON1bits.DONE);       // Wait for conversion to complete
    return ADC1BUF0;                // Read conversion result
}

问题/意见

任何问题或评论请访问 Digi-Key 的技术论坛