入門
如今,無需進行一些編程就很難創(chuàng)建任何電子電路。不幸的是,您學(xué)習(xí)為一個制造商創(chuàng)建固件的知識不一定適用于另一個制造商。
出于這個原因,購買評估板通常是一個好主意,這樣您就可以學(xué)習(xí)在一個肯定可以正常工作的板上對您的微控制器進行編程,然后再用自己的設(shè)計來解決問題。熟悉制造商為您提供的資源(論壇、數(shù)據(jù)表、用戶指南、視頻等)也是一個好主意。
在這種情況下,我使用了MSP430FR2633,因此我在MSP CapTIvate MCU 開發(fā)套件上進行了練習(xí),并向TI 的 E2E 社區(qū)尋求建議。
本文并未涵蓋源代碼的每一行;相反,它以源代碼為例提供了有關(guān)固件編程的一般信息。
添加 ASCII 引腳圖
最后,我想為自己添加一些額外的參考。由于這是一個自定義板,我想在一個位置向自己提供盡可能多的信息。很有可能一個小時后我不會記得我的引腳連接是什么,而且我肯定不會記得一個星期后。如果我進行任何編程更改,不必挖掘原理圖會很好。
出于這個原因,我在源代碼中包含了一個連接圖。網(wǎng)絡(luò)上有各種ASCII 圖表生成器,這使得操作相對快速。
// CP2102N ┌──────┬──────┐ ┌────────────┐ // ┌────────────┐ │ │ P1.0 │→ UCB0STE →│ EN (NC) │ // │ USB │→ RX →│ P2.5 │ P1.1 │→ UCB0CLK →│ CLK │ // │ TO │→ TX ←│ P2.6 │ P1.2 │→ UCBSIMO →│ SIMO (NC) │ // │ UART │ │ │ P1.3 │← UCBSOMI ←│ SOMI │ // └────────────┘ ├──────┼──────┤ │ │ // ST_IN1 ←│ P2.0 │ P2.4 │← BUSY ←│ BUSY │ // ST_IN2 →│ P2.1 │ P3.0 │→ RDL →│ RDL │ // │ │ P3.1 │→ CNV →│ CNV │ // └──────┴──────┘ └────────────┘ // MSP430FR2633 ADC
我們需要做的第一件事是處理我們的引腳,以便編譯器知道哪些設(shè)備連接到什么。為此,您需要了解寄存器的概念。如果您已經(jīng)熟悉這些概念,請隨意跳到下面的“定義寄存器描述快捷方式”。
什么是寄存器?
寄存器是內(nèi)存中由十六進制數(shù)字標(biāo)識的位置,位屬于寄存器。每個寄存器中的位控制微控制器功能的特定方面。
每個寄存器控制一個字節(jié)(8 位)或一個字(16 位)。然而,為了使討論簡單,下圖說明了控制單個字節(jié)的寄存器。
定義寄存器描述快捷方式
現(xiàn)在我們對寄存器有了非常簡短的背景,讓我們在上下文中使用它。
MSP430FR2xx 系列用戶指南中的寄存器描述指示哪些位控制哪些功能。當(dāng)寄存器不允許直接訪問單個位時,您可以將字節(jié)操作與適當(dāng)?shù)奈谎诖a結(jié)合使用;單個位可以使用“|=”運算符設(shè)置,使用“&=”運算符清除,或使用“^=”運算符切換。
上圖顯示了四種字節(jié)操作前后虛寄存器 W、X、Y 和 Z 中的數(shù)據(jù)。
配置位在數(shù)據(jù)表中以十六進制或二進制表示法提供。但是,鍵入諸如 0x3F2A &= 0xC9 之類的內(nèi)容并跟蹤程序中發(fā)生的事情是相當(dāng)不方便的。因此寄存器和引腳名稱在引用的頭文件中或在程序的開頭定義。并且使用寄存器的名稱和與該寄存器關(guān)聯(lián)的引腳而不是原始的十六進制和二進制數(shù)。例如,“WDTCTL = WDTHOLD”(WatchDog Timer ConTroL 寄存器 = WatchDog Timer HOLD 值)—換句話說,停止看門狗定時器。
因此,編程不再使用十六進制地址,而是使用這些命名的快捷方式指定寄存器和位,并直接或使用字節(jié)操作(例如上面提到的那些)修改位。
void main(void) { WDTCTL = (WDTPW | WDTHOLD); // Stop watchdog timer … CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source CSCTL0 = 0; // clear DCO and MOD registers CSCTL1 &= ~(DCORSEL_7); // Clear DCO frequency select bits first CSCTL1 |= DCORSEL_5; // Set DCO = 16MHz …
命名變量——引腳的位位置
對于 MSP430,每個端口控制一組引腳 — GPIO 通常為 8 位,因此一個端口對應(yīng)一個字節(jié)。雖然您不能立即定義端口,但控制端口引腳的寄存器不可位尋址,因此無法創(chuàng)建與單個引腳對應(yīng)的名稱(例如,“#define somePinVar 1.5”)。
相反,我們將名稱附加到引腳的位位置,并使用該名稱與端口寄存器一起控制它。
首先命名引腳(它們所連接的端口將在程序后面說明)。這可以在主程序的開頭或在單獨的頭文件中完成。選擇變量和常量的名稱是為了便于以后理解,并基于 PCB 上相應(yīng)的網(wǎng)絡(luò)名稱。
#define UART_RX_PIN BIT5 // P2.5 eUSCI_A1 #define UART_TX_PIN BIT6 // P2.6 eUSCI_A1 #define SPI_EN_PIN BIT0 // P1.0 eUSCI_B0 -- not used #define SPI_CLK_PIN BIT1 // P1.1 eUSCI_B0 #define SPI_MOSI_PIN BIT2 // P1.2 eUSCI_B0 -- not used yet #define SPI_MISO_PIN BIT3 // P1.3 eUSCI_B0 #define ADC24_RDL BIT0 // P3.0 set low always #define ADC24_CNV BIT1 // P3.1 L->H->L (20 ns) to convert #define ADC24_BUSY BIT4 // P2.4 goes low after conversion #define ST_IN1 BIT0 // 3PST switch input 0 #define ST_IN2 BIT1 // 3PST switch input 1
程序的下一部分定義了使用的變量以及函數(shù)原型。我使用全局變量并將數(shù)組保留為易失性,以避免 SPI 和 UART 中斷服務(wù)例程的任何潛在問題。本節(jié)中定義的所有變量都可用于所有函數(shù)。
// Variable Declarations … uint16_t numTrials; // number of times to repeat reads uint16_t numReads; // number of conversions per read … uint8_t byteCounter; // need 24 bits + 16 bits (5 bytes) volatile uint8_t dataBuffer[5]; // Holder for all SPI data // Function Prototypes void readADC(uint16_t); // Runs conversions uint8_t spix(uint8_t); // SPI interface communication void uartx(uint8_t); // UART interface communication one-way
分配引腳功能
MSP430FR2633 具有可支持多種功能的引腳。因此,必須告知 MCU 引腳是用于通用輸入/輸出還是用于UART或I 2 C等集成外設(shè)。
以下幾行代碼配置端口 2 使用,首先為 UART Tx/Rx 連接對應(yīng)的端口 2 引腳選擇備用功能(即集成外設(shè)),然后配置 UART_TX 引腳和兩個自檢開關(guān)引腳作為輸出。
// Select module function for UART (page 63) P2SEL0 |= (UART_TX_PIN | UART_RX_PIN); // Set port pins as output for UART and SP3T Switch P2DIR |= (UART_TX_PIN | ST_IN1 | ST_IN2);
自定義函數(shù)
自定義函數(shù)用于讀取 ADC — 基本上只是切換啟動轉(zhuǎn)換標(biāo)志,等待繁忙指示器關(guān)閉,然后再次切換啟動轉(zhuǎn)換標(biāo)志 — 由在開始時設(shè)置的常數(shù)所指示的次數(shù)該程序。
void readADC(uint16_t numReads) { // Start conversion, wait for busy flag to turn off, // and start the next conversion. for (counter = 1; counter <= numReads; counter++) { P3OUT ^= ADC24_CNV; // Toggle convert flag high > 20 ns to start __delay_cycles(1); // measurement. 1/16MHz * 1 cycles = 62.5 ns P3OUT &= ~ADC24_CNV; // Set convert flag back low // Wait to let conversion finish - might never enter function while ((P2IN & ADC24_BUSY)) { __delay_cycles(1); // 1/16Mhz = 62.5 ns } }
收集數(shù)據(jù)
數(shù)據(jù)將從我的微控制器中出來,并通過 USB 快速傳輸?shù)?PC。如果我在通過 UART 到 USB 轉(zhuǎn)換器發(fā)送數(shù)據(jù)之前將此數(shù)據(jù)轉(zhuǎn)換為十進制等效值或度/分/秒,則很難看到數(shù)據(jù)以每秒約 100 行的速度滾動時有多穩(wěn)定。相反,我們將數(shù)據(jù)轉(zhuǎn)換為二進制。
通過將數(shù)據(jù)轉(zhuǎn)換為二進制,我可以在串行監(jiān)視器中快速查看有多少未更改的位,只需從左側(cè)開始計數(shù)。一旦位開始變化,我就會遇到噪音(當(dāng)然,假設(shè)傳感器完全靜止)。數(shù)據(jù)以我在完整程序中用制表符分隔的塊的形式出現(xiàn):三個字節(jié)用于角度數(shù)據(jù),兩個字節(jié)用于每次讀取的樣本數(shù)(我后來又添加了兩個字節(jié)來創(chuàng)建一個計數(shù)器)。
for (byteCounter = 0; byteCounter < 5; byteCounter++) //0,1,2,3,4 { for (bitCounter = 0; bitCounter < 8; bitCounter++) { if (((dataBuffer[byteCounter] >> (7 - bitCounter)) & 0b1) == 0) { uartx('0'); // ASCII 0 } Else { uartx('1'); // Ascii 1 } }
您會注意到我必須更改每個字節(jié)的“字節(jié)順序”,以便它首先到達 UART MSB。
樣本數(shù)據(jù)顯示了 ADC 讀數(shù)的一列、自上次數(shù)據(jù)傳輸以來讀取的讀數(shù)數(shù)量以及一個計數(shù)器。
使用 Mathematica 處理數(shù)據(jù)
在最終程序中,我最終添加了一個試用計數(shù)器并使用Mathematica(在Rasbian OS上免費)處理數(shù)據(jù)。未來的文章將更詳細地解釋數(shù)據(jù)和數(shù)據(jù)處理。
初步試驗的數(shù)據(jù)如上所示。帶有彩色三角形的垂直刻度顯示三個縮放級別的最大值、+1σ、平均值、-1σ 和最小值。還包括散點圖、直方圖及其伴隨的理想化正態(tài)分布。
本文解釋了該項目的一小部分固件編程。下一篇文章將描述設(shè)備中的噪聲,未來的文章將分析數(shù)據(jù)。
您將在下面找到 MCU 上程序的完整源代碼的可下載文件。如果您也有興趣訪問 Mathematica 源代碼文件,請在下面的評論中告訴我!
Precision_Inclinometer_Firmware.zip
// _ _ _ _ _____ _ _ _ // /\ | | | /\ | | | | / ____(_) (_) | // / \ | | | / \ | |__ ___ _ _| |_| | _ _ __ ___ _ _ _| |_ ___ // / /\ \ | | | / /\ \ | '_ \ / _ \| | | | __| | | | '__/ __| | | | | __/ __| /// ____ \| | |/ ____ \| |_) | (_) | |_| | |_| |____| | | | (__| |_| | | |_\__ \ //_/ \_\_|_/_/ \_\_.__/ \___/ \__,_|\__|\_____|_|_| \___|\__,_|_|\__|___/ // _____ _ __ _____ _ // | |___ ___| |_ __| | | | |_ _ ___| |_ ___ ___ // | | | | .'| _| '_| | | | | | | | . | | -_|_ -| // |_|_|_|__,|_| |_,_| |_____| |__|__|___|_ |_|_|___|___| // w/ support by Bruce McKenney |___| 2018/11/09 // _ __ // ' ) ) _/_ // ______ . ./--'__. / __. // / / / <(_// \(_/|<_(_/|_ // __ __ _ ___ __ __ __ _ __ _ _ ____ ____ ____ ____ // ( | ( \/ __| ) ( | ( \/ \( \/ | __|_ _| __| _ \ // )(/ ( (__/ (_/\)(/ ( O ) \/ \) _) )( ) _) ) / // (__)_)__)\___)____(__)_)__)\__/\_)(_(____)(__)(____|__\_) // /****************************** Connection Diagram ****************************/ // CP2102N ┌──────┬──────┐ ┌────────────┐ // ┌────────────┐ │ │ P1.0 │→ UCB0STE →│ EN (NC) │ // │ USB │→ RX →│ P2.5 │ P1.1 │→ UCB0CLK →│ CLK │ // │ TO │→ TX ←│ P2.6 │ P1.2 │→ UCBSIMO →│ SIMO (NC) │ // │ UART │ │ │ P1.3 │← UCBSOMI ←│ SOMI │ // └────────────┘ ├──────┼──────┤ │ │ // ST_IN1 ←│ P2.0 │ P2.4 │← BUSY ←│ BUSY │ // ST_IN2 →│ P2.1 │ P3.0 │→ RDL →│ RDL │ // │ │ P3.1 │→ CNV →│ CNV │ // └──────┴──────┘ └────────────┘ // MSP430FR2633 ADC /********************************* Description ********************************/ // by Mark Hughes for AllAboutCircuits.com. Find complete information for this // project at http://allaboutcircuits.com/author/mark-hughes // This is a one-directional SPI to UART to USB bridge interface // CNV is toggled one/multiple times (up to 65535) and data is averaged inside // the ADC until SPI_CLK toggles data out and into MSP430. Data is then sent // via UART to CP2102N at 115200, and then from the CP2102N to the computer. // The data is sent over UART in Binary format MSB first using ASCII characters. // self-test and auto-calibration not yet implemented. // When ST_IN1 != ST_IN2, enters self-test mode for sensor /*********************************** Headers **********************************/ #include #include #include /******************************* Pin Definitions ******************************/ #define UART_RX_PIN BIT5 // P2.5 eUSCI_A1 #define UART_TX_PIN BIT6 // P2.6 eUSCI_A1 #define SPI_EN_PIN BIT0 // P1.0 eUSCI_B0 -- not used #define SPI_CLK_PIN BIT1 // P1.1 eUSCI_B0 #define SPI_MOSI_PIN BIT2 // P1.2 eUSCI_B0 -- not used yet #define SPI_MISO_PIN BIT3 // P1.3 eUSCI_B0 #define ADC24_RDL BIT0 // P3.0 set low always #define ADC24_CNV BIT1 // P3.1 L->H->L (20 ns) to convert #define ADC24_BUSY BIT4 // P2.4 goes low after conversion #define ST_IN1 BIT0 // 3PST switch input 0 #define ST_IN2 BIT1 // 3PST switch input 1 /**************************** Variable Declarations ***************************/ // Can be consolidated and revised later with proper function calls and // data returns later on. uint8_t bitCounter; // 0-7 counter for bits in a byte. uint8_t byteCounterSPI; // 0-4 counter for SPI data bytes. uint8_t byteCounter; // need 24 bits + 16 bits (5 bytes) uint16_t counter; // Temporary counter uint16_t numTrials; // Temporary counter uint16_t numTrialsMax = 1024; // Number of times to repeat measurement uint16_t numReads = 4; // number of conversions per read. // Can replace volatile buffer with pointer later on. volatile uint8_t dataBuffer[5]; // Holder for all SPI data /****************************** Function Prototypes ***************************/ // Function Prototypes void readADC(uint16_t); // Decides number of conversions uint8_t spix(uint8_t); // SPI interface void uartx(uint8_t); // UART interface /******************************** Main Program ********************************/ void main(void) { //********************* Begin Configuration ******************************** WDTCTL = (WDTPW | WDTHOLD); // Stop watchdog timer FRCTL0 = FRCTLPW | NWAITS_1; // FRAM configuration for > 8 MHz __bis_SR_register(SCG0); // Disable Frequency Locked Loop (FLL) CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source CSCTL0 = 0; // Clear DCO and MOD registers CSCTL1 &= ~(DCORSEL_7); // Clear DCO frequency select bits first CSCTL1 |= DCORSEL_5; // Set DCO = 16MHz CSCTL2 = FLLD_0 + 487; // DCOCLKDIV = 16MHz __delay_cycles(3); // Wait to allow stabilization of clock __bic_SR_register(SCG0); // Reenable FLL while (CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)) /*EMPTY*/; // FLL locked // default DCOCLKDIV as MCLK and SMCLK source CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // Disable GPIO power-on default high-impedance mode PM5CTL0 &= ~LOCKLPM5; // Disable GPIO power-on default high-impedance mode // PxDIR: 0(In) 1(Out) // PxSEL: Function Select Register (see datasheet) // PxOUT: 0(L) 1(H): Output Register // PxREN: 0(L) 1(En): Resistor Enable (on input only) // Select SPI module function for SPI (page 60) P1SEL0 |= (SPI_MISO_PIN | SPI_MOSI_PIN | SPI_CLK_PIN); // Set MOSI and CLK as outputs. P1DIR |= (SPI_MOSI_PIN | SPI_CLK_PIN); // Select module function for UART (page 63) P2SEL0 |= (UART_TX_PIN | UART_RX_PIN); // Set port pins for UART and SP3T Switch P2DIR |= (UART_TX_PIN | ST_IN1 | ST_IN2); // IN1/IN2 initially low to open SPST switch. P2OUT &= ~(ST_IN1 | ST_IN2); // Set port pins for ADC P3SEL0 &= ~(ADC24_RDL | ADC24_CNV); // Set direction for RDL and CNV P3DIR |= (ADC24_RDL | ADC24_CNV); // Set port output low for RDL and CNV P3OUT &= ~(ADC24_RDL | ADC24_CNV); // Setup SPI in UCB0 Control Word 0 // Place UCB in reset state before modifying settings UCB0CTLW0 |= UCSWRST; // Master-mode, synchronous clock, inactive state high, MSB first. UCB0CTLW0 |= (UCMST | UCMSB | UCCKPH | UCSYNC | UCMSB | UCSSEL__SMCLK); // Bit clock prescaler UCB0BRW = 0x0002; // Bit rate clock = SMCLK/2 = 8 MHz // Release reset and initialize state machine UCB0CTLW0 &= ~UCSWRST; // Put UCA state machine in reset, select mode and clock. UCA1CTLW0 = UCMODE_0 | UCSSEL__SMCLK | UCSWRST; // UART, SMCLK, Reset // 16M/(UCOS)16/9600 UCA1BRW = 104 // UCA1BRW = 104; // UCBRS=0xD6, 16x oversample, UCBRF=2 // UCA1MCTLW = (0xD6 << 8) | UCOS16 | UCBRF_2; // 16M/(UCOS)16/115200 UCA1BRW = 8; UCA1BRW = 8; // 115200 Baud. UCA1MCTLW = (0xD6 << 8) | UCOS16 | UCBRF_11; // 115200 Baud. // Release reset and initialize state machine UCA1CTLW0 &= ~UCSWRST; /**************************** End Configuration ***************************/ /****************************** Main Program ******************************/ // Read the ADC a certain number of times. Toggle ST_IN1 and ST_IN2 to // flash LED after certain number of reads. for (numTrials = 1; numTrials <= numTrialsMax; numTrials++) { // Turn LEDS on/off every 100 trials if (numTrials % 100 == 1) { P2OUT ^= (ST_IN1 | ST_IN2); } __delay_cycles(1); readADC(numReads); // Perform n conversions } // Turn LEDS off because numTrials likely didn't. P2OUT &= ~(ST_IN1 | ST_IN2); // Add an additional carriage return and newline. uartx('\r'); // Carriage return uartx('\n'); // Newline // End main. } /**************************** Function to read ADC ****************************/ void readADC(uint16_t numReads) { // Start conversion, wait for busy to turn off, and start the next conversion. for (counter = 1; counter <= numReads; counter++) { P3OUT ^= ADC24_CNV; // Toggle convert flag high > 20 ns to start __delay_cycles(1); // measurement. 1/16MHz * 1 cycles = 62.5 ns P3OUT &= ~ADC24_CNV; // Set convert flag back low // Wait to let conversion finish - might never enter function while ((P2IN & ADC24_BUSY)) { __delay_cycles(1); // 1/16Mhz = 62.5 ns } } // Shift out dummy bytes to allow ADC data to shift into dataBuffer[] for (byteCounterSPI = 0; byteCounterSPI < 5; byteCounterSPI++) { // Shift out useless data to allow shift in of ADC data. dataBuffer[byteCounterSPI] = spix(0xFF); } // Find binary equivalent of each dataBuffer byte. Send it out over UART. // Each byte is shifted to the right n-bits and the LSB is read as 1 or 0 // (7 - bitCounter) is used to change Endianess. for (byteCounter = 0; byteCounter < 5; byteCounter++) //0,1,2,3,4 { for (bitCounter = 0; bitCounter < 8; bitCounter++) //0,1,2,3,4,5,6,7 { // Need to change endianness of data before sending out UART if (((dataBuffer[byteCounter] >> (7 - bitCounter)) & 0b1) == 0) { uartx('0'); // ASCII 0 } else { uartx('1'); // Ascii 1 } } // Data Formatting if (byteCounter == 2) { // After the three data-bytes, add a tab to separate number of reads uartx('\t'); // Ascii tab } // After the last byte is out, send a carriage return and line feed. if (byteCounter == 4) { uartx('\t'); for (bitCounter = 0; bitCounter < 16; bitCounter++) { if (((numTrials >> (15 - bitCounter)) & 0b1) == 0) { uartx('0'); // ASCII 0 } else { uartx('1'); // Ascii 1 } } uartx('\r'); // Return uartx('\n'); // Line Feed } } return; } uint8_t spix(uint8_t c) { while (!(UCB0IFG & UCTXIFG)) /*EMPTY*/; UCB0TXBUF = c; while (!(UCB0IFG & UCRXIFG)) /*EMPTY*/; c = UCB0RXBUF; return (c); } void uartx(uint8_t c) { while (!(UCA1IFG & UCTXIFG)) /*EMPTY*/; UCA1TXBUF = c; return; }
-
微控制器
+關(guān)注
關(guān)注
48文章
7542瀏覽量
151311 -
msp430
+關(guān)注
關(guān)注
180文章
2403瀏覽量
229298 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
5032瀏覽量
97371
發(fā)布評論請先 登錄
相關(guān)推薦
評論