PIC32 和 MPLABX 的使用入門

介紹

Microchip 提供廣泛的 32 位元微控制器產品組合。本指南是為了使用 PIC32MX360F512L 微控制器建立的。雖然有很多種類,但 Microchip 已經方便地為每個f32 位元微控制器家族建立了文件。這些文件將在本指南後面進行解釋。本指南的目的是幫助剛開始接觸 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

本教學不需要加速計板,操縱桿和 US B到 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使用者指南 如果你正在使用它,入門套件的使用者指南中有很多關於電路板的有用資訊。如果你決定用 PIC32 來建造自己的電路板,本指南有一些很棒的原理圖可供參考。
I/O擴充板資訊表 如果您正在使用擴充板的入門套件,您將需要這個原型。它有有用的原理圖,告訴你板上的每個引腳是什麼。
Microchip 開發者幫助 Microchip 建立這個教學是為了訓練剛接觸他們產品的開發人員。這個網站上有很多有用資料,關於與 PIC 微控制器有關的一切。 這裡也有自定節奏和即時線上培訓。
用 C 編程 32 位元微控制器,Lucio Di Jasio 著 這本書產生了很多關於這個入門指南的資料。 Lucio 從最基礎的開始,透過實踐訓練教你 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分頻器」。
  • 選擇「20X 倍增器」為鎖相環倍增器選項。
  • 輸出分頻器選項選擇「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 轉 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 Example

// 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 Example

// 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 Example

// 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
}

問題/意見

任何問題或評論請訪問 DigiKey 的技術論壇