MSP430
該MCU是由德州儀器TI生產的16位低功耗單片機
主要分以下型號:
- 專注低功耗的1xx通用型,配備1KB-60KB FLASH、512B-10KB RAM,工作時耗電僅達200uA/MIPS,RAM保持模式耗電0.1uA,RTC模式耗電0.7uA;可在6us之內快速喚醒。搭載10/12位斜率SAR ADC,集成模擬比較器、DMA、硬件乘法器、BOR、SVS、12位DAC
- 能耗比高的F2xx通用型,性能16MIPS@3.3V,配備1-120KB FLASH,8-128KB RAM,工作耗電220uA,配備10/12位斜率SAR ADC,集成16位Σ-ΔADC,基本上等於1xx的升級版
- 性價比高的G2xx經濟型,性能16MIPS@3.3V,對標友商stm32l,主打模擬外設和低功耗
- 面向計量和智能電網的AFE2xx專用型:性能略低於以上兩個2xx系列,但是集成了1-3個獨立的24位Σ-ΔADC,一個16位定時器、一個16位硬件乘法器、USART控制器、看門狗和GPIO
- 停產的老型號3xx
- 面向低功耗多媒體的4xx控制型,8-16MIPS處理性能,配備LCD控制器、FLL、SVS,針對低功耗測量和醫療應用,功耗和1xx相近,4-120KB FLASH、8-256KB RAM,引腳豐富最多可達80Pin,配備10/12位斜率SAR、16位Σ-ΔADC,同樣集成了12位ADC、DMA、硬件乘法器、運放、USCI模塊等
- 超高能耗比的5xx超低功耗型,能達到25MIPS@3.3V,工作模式功耗165uA/MIPS,RTC模式2.5uA,RAM保持模式可達1uA,待機喚醒時間極短,小於5ms,配備256KB FLASH、18KB RAM,額外集成了USB、模擬比較器
- 高性能、低功耗的6xx系列旗艦型,達到25MIPS@3.3V,配備功耗優化的創新電源管理模塊和USB控制器,配備LCD控制器,有256KB FLASH、18KB RAM,74Pin引腳,功耗與5xx系列相同,還額外集成了電壓管理模塊
- 基於FRAM技術的FRxx系列,和主要的F系列差別在於使用了FRAM存儲技術,能夠達到更快的FLASH訪問速度並在所有功率模式下實現零功率狀態保持,即使發生功率損耗的情況也可以保證寫入操作,寫入壽命能達到100M個周期,不再需要EEPROM
- 低電壓C、L系列,兩個謝列都可以在0.9-1.65V電壓范圍內工作並提供4MIPS的性能
- 集成射頻基帶的CC無線系列,具有低於1GHz的片上射頻收發器,工作電壓為1.8-3.3V,處理性能20MIPS
- 特殊系列:面對車規應用、電容觸摸、超聲波測量、DSP等等特殊用途的系列設備
綜合來看MSP430具有以下特點:
-
超低功耗
使用1.8-3.6V低電壓供電,RAM數據保持方式下耗電僅為0.1μA/MIPS,活動模式下耗電也僅僅為250μA/MIPS,IO輸入端口漏電流僅為50mA,相比之下只有stm8和stm32l0系列能夠達到同等級的低功耗水准。普通的8051則遠遠落后
-
能效比高,針對算法加速
MSP430基於RISC架構,采用了一般DSP才有的16位多功能硬件乘法器、硬件乘-加功能、DMA等架構,可以高效實現如FFT、DFT、FIR濾波等數字信號處理算法
-
模擬外設技術高
MSP430片內集成多種模擬外設,包括液晶驅動器和ADC、DAC等,具體外設由型號決定
-
外設寄存器直接按位尋址
外設寄存器可以直接進行賦值、按位操作
msp430系列使用了馮諾依曼架構,並構建了MAB(存儲器地址總線)、MDB(存儲器數據總線)兩個總線協議,其中RAM、FLASH共用同一個地址空間,程序被下載到FLASH,設備復位后自動讀取並執行程序指令,局部變量存儲在RAM,BSS段變量存儲在FLASH,FLASH掉電不丟失
設備內部時鍾至少具有3套時鍾源:
- LFXT1CLK:低頻時鍾,32.768kHz
- XT2CLK:高頻時鍾,8MHz
- DCOCLK:片內數字控制RC振盪器,經常用作系統和外設時鍾信號,其穩定性由FLL與硬件控制
三套時鍾源可以被設備單獨選用,時鍾通過片內總線提供給設備;有些型號還具有更多類型的時鍾源
DMA可以直接接管總線以提高傳輸效率(不同於AMBA總線的仲裁,MAB、MDB總線只采用主控設備-從設備的方式)
CPU則采用了雙總線位寬的靈活處理方式,分為16位尋址的CPU和20位尋址的CPUX。CPU采用RISC架構,配備27條指令和7種統一的尋址模式,尋址空間64KB;CPUX尋址空間為1MB,采用面向控制的結構和指令系統,集成了計算分支、表處理等特性,可以在不分頁的情況下處理1MB的地址范圍,屬於RISC正交指令集(正交:指令集的絕大多數指令格式相同、長度相同,所有寄存器的尋址可以替換;而指令的操作碼、尋址方式、操作數寄存器字段的取值相互獨立),可以實現MTM(內存到內存)傳輸,不需要經過中間寄存器,一並對16位CPU實現了兼容
在電賽中最常用的就是MSP430F5529,下面均以F5529為例說明
外圍電路設計
供電
MCU部分模擬外設和FLASH、RAM對於電源要求較高,但是MCU數字部分對於電源要求較低,因此采用雙電源——模擬/數字的方式為MCU供電。模擬-數字電源之間采用磁珠跨接3.3V和地除雜波,同時需要使用10uF、100nF電容並聯進行濾波,10uF用於濾除低頻雜波,100nF則用於旁路
復位
RST引腳低電平有效,因此和一般單片機的復位電路一樣即可
晶振
需要使用兩個晶振接入來保證電源穩定,XT1接低頻32.768kHz,XT2接高頻晶振,一般為4MHz,因為內部電容不足以起振,所以同時需要單獨配備20-30pF的匹配電容,一般使用22pF電容
USB
f5529具有USB控制器,能夠使用4芯電纜:5V、D+、D-、GND,並可以兼容USB OTG的ID線。
編譯燒錄
MSP430支持JTAG和SBW(Spy-Bi-Wire,TI指定的兩線調試接口,信號叫為SBWTCK和SBWTDIO)
同時也支持BSL(BootStrap Loader),或者說BootLoader加載程序燒錄可以通過USB、UART等對單片機進行ISP燒錄,在PUR引腳和USB D+之間跨接1.4k電阻,下連1M電阻到地,並通過一個加了限流電阻(一般為100Ω)的微動開關連接到VCC即可實現USB的BSL燒錄
MSP430的開發環境是TI基於eclipse開發的Code Composer Studio,簡稱CCS,在其中使用專用的MSP430 Compile與Linker即可實現C程序編譯鏈接
關鍵字和內聯函數
同時CCS支持了一些擴展關鍵字,列舉如下:
-
__asm
用於C語言內嵌匯編,這個和keil一樣 -
__interrupt
放在函數前指示中斷函數,一般和#pragma指令共用#pragma vector=UART0RX_VECTOR __interrupt void UART_ISR(void) {}
上例指示了一個串口0接收中斷,#pragma指令讓中斷向量表中的地址位重定向了
-
__monitor
放在函數前,在執行到函數時自動關閉全局中斷,類似__atom指令 -
__no_init
放在全局變量錢讓程序啟動時不被變量賦初值 -
__raw
關閉中斷服務函數的恢復現場能力,這會導致中斷服務函數無法返回 -
__regvar
聲明變量為寄存器變量,注意不能使用指針指向寄存器變量,並且必須搭配使用__no_init禁止初始化 -
sfrb
用於聲明單字節IO數據類型對象,和51一樣用於定義寄存器地址
除了關鍵字外,CCS還包含了許多內聯函數,常見的幾個摘錄如下
__no_operation();//空指令,相當於NOP
__enable_interrupt();//打開全局中斷
__disable_interrupt();//關閉全局中斷
__delay_cycles(unsigned long __cycles);//延時__cycles個主時鍾(MCLK)周期
__set_SP_register(unsigned short);//為堆棧指針寄存器SP賦值
預定義寄存器
CCS中還預置了一些單片機常用的寄存器和配置,如下所示
-
端口定義,其中x表示端口號
PxIN:端口輸入寄存器
PxOUT:端口輸出寄存器
PxDIR:端口方向控制寄存器
PxSEL:端口復用寄存器
注意:MSP430不支持位操作,一般通過屏蔽位的方法實現位操作,這是它和51開發方面最大的不同,如下所示
#define BIT0 00000001b #define BIT1 00000010b #define BIT2 00000100b #define BIT3 00001000b #define BIT4 00010000b #define BIT5 00100000b #define BIT6 01000000b #define BIT7 10000000b P1OUT |= BIT0; //這樣可以實現P1.0輸出 P1OUT |= BIT4; //這樣可以實現P1.0和P1.3同時輸出 P1OUT &= ~BIT1; //這樣可以取消P1.0的輸出
-
低功耗模式的進入和退出
CCS預定義了一些宏指令用來實現低功耗模式
LPM3;//進入低功耗模式 LPM3_EXIT;//退出低功耗模式
其中數字可以寫0-4,分別對應四種低功耗模式
-
外設寄存器
各種片上外設的寄存器都被定義為了宏,可以通過
|=
與&=~
的方式進行按位控制 -
部分常用代碼
_dint(); //等效於__disable_interrupt _EINT(); //等效於__enable_interrupt _NOP(); //空指令 _OPC(x); //在指令流中插入一個常熟,對與參數對應的任何指令進行編碼 _SWAP_BYTES(x); //將無符號16位整數的高8位和低8位交換 monitor //關鍵字__monitor的宏定義 no_init //關鍵字__no_init的宏定義
綜上所述,MSP430的開發和8051的開發非常類似,並沒有stm32的庫封裝,而是直接操作寄存器。這不僅僅是由於MSP430的性能較低,也是由於這樣的編程方式寫出的代碼更加簡潔、指令量更少,能突出體現MSP430低功耗的優勢
片上外設開發
MSP430的片上外設寄存器具體配置和51單片機的很像,但是有一些功能更加復雜,和stm32的寄存器接近,同時也提供了簡化操作的庫函數
GPIO
f5529一共有80個引腳,和stm32的gpio結構類似,並且也具有復用功能。除此之外某些引腳具備基本的電源功能,分別能夠為MCU的片上數字電路和模擬電路供電,一般情況下可以共用一個電源,但在某些高精度測量場合需要雙電源隔離供電。msp430還具有一個USB電源,可以直接輸出5V供電,經過片上LDO后能在端口VBUS處輸出穩定的3.3V電壓供單片機和外設使用,最大驅動電流60mA
其中P1、P2端口IO都具有外部中斷能力,分別對應P1IV中斷向量和P2IV中斷向量。端口可單獨配置強驅動和弱驅動模式,強驅動模式下全片最大輸出電流100mA,單端口最大電流15mA;弱驅動模式下全片最大輸出電流48mA,單端口最大電流6mA
寄存器操作可以通過上面介紹過的屏蔽位法,也可以通過分別操作寄存器高8位和低8位的方法實現
//使用三種方法將P1.1和P2.2配置為輸出功能
P1DIR |= 0x02; //0x02 == 00000010b
P2DIR |= 0x04; //0x04 == 00000100b
PADIR_L |= 0x02; //DIR寄存器低8位,代表P1
PADIR_H |= 0x04; //DIR寄存器高8位,代表P2
PADIR |= 0x0402; //直接操作DIR寄存器,將其視作uint16_t
-
端口配置
如下配置端口為輸入狀態並配置內部上拉
P1DIR &= ~BIT1; //BIT1 == 0x00000010b,設置P1.1為輸入模式 P1REN |= BIT1; //使能上下拉電阻 P1OUT |= BIT1; //P1.1配置上拉電阻
通過配置PxDIR.n |= 1可以將相應的IO口配置為輸出狀態,在輸出狀態下,PxREN、PxIN無效。
P1OUT |= BIT1; //P1.1輸出高電平 P1OUT &= ~BIT1; //P1.1輸出低電平 P1DS.1 &= ~BIT1; //配置P1.1為弱驅動輸出 P1DS.1 |= BIT1; //配置P1.1為強驅動輸出
-
端口復用配置
基本每個IO都有端口復用功能,通過配置PxSEL.n把對應的IO口配置為復用功能
使用以下代碼配置P1.0為定時器A0時鍾輸入
P1DIR &= ~BIT0; //設置P1.0為輸入狀態 P1SEL |= BIT0; //將P1.0復用為定時器A0時鍾輸入
相關配置需要按照datasheet中的端口復用表選擇
時鍾系統與低功耗
msp430f5529具有5種時鍾源(XT1CLK、XT2CLK、VLOCLK、REFOCLK、DCOCLK)和3種時鍾信號(MCLK、SMCLK、ACLK)
時鍾系統可以軟件配置成不需要外部晶振、需要一個外部晶振、需要兩個外部晶振、外部時鍾輸入等方式,最極端的情況下單片機內部具有自身振盪器可以為CPU及片上外設提供系統時鍾
時鍾系統的安全性比較重要,msp430配備了緊急保護系統,在外部時鍾故障時會自動選擇內部時鍾源REFOCLK或VLOCLK作為時鍾信號,並產生響應故障信號(可選中斷)
系統時鍾大致分為兩級,信號生成級和信號分配級,中間通過MUX連接。信號生成級別分為三個模塊基本的OSC模塊可以通過晶振旁路、內部REFO或VLO直接輸出XT1CLK、VLOCLK、REFOCLK三種信號;可選的XT2模塊直接輸出XT2晶振的4MHz時鍾作為XT2CLK;可以通過晶振旁路和FLL(Frequency Locked Loop鎖頻環)進行晶振時鍾倍頻和分頻,信號源(即FLLREFCLK反饋時鍾)通過MUX直接引用XT1CLK、REFOCLK、XT2CLK之一,經過多個倍頻分頻器后輸出為DCOCLK和DCOCLKDIV。所有信號分別輸出到信號分配級,通過MUX分配給ACLK、MCLK、SMCLK
XT1CLK:外部低頻或高頻時鍾源,默認關閉,需要接入外部晶振並通過軟件使晶振起振后再使用,一般使用32.768kHz的低頻晶振,但是也可以使用4-32MHz的外部高頻時鍾源,端口P5.4、P5.5
使用下面的代碼對時鍾源進行配置
P5SEL |= BIT4 | BIT5; //配置P5.4、P5.5為XT1復用功能
UCSCTL6 |= XCAP_3; //配置匹配電容為12pF
UCSCTL6 &= ~XT1OFF; //使能XT1,使外部晶振起振
while(SFRIFG1 & OFIFG)
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); //清除三類時鍾故障標志位,等待起振
SFRIFG1 &= ~OFIFG; //清除震盪器故障標志位
}
XT2CLK:和XT1CLK類似,但只能接4-32MHz的高頻晶振,一般接入4MHz晶振,需要額外加匹配電容方便起振。端口P5.2、P5.3
需要注意的是在配置SMCLK和MCLK為XT2CLK時鍾源之前需要先修改ACLK和REFCLK的時鍾源,因為它們的時鍾源默認為XT1CLK,但這里並沒有啟動,所以會導致沒有必要的XT1CLK始終故障,會影響判斷XT2是否起振,實現代碼如下
P5SEL |= BIT2 | BIT3; //配置P5.2、P5.3為XT2復用功能
UCSCTL6 &= ~XT2OFF; //使能XT2
UCSCTL4 = UCSCTL4 & (~(SELA_7)) | SELA_1; //將ACLK配置為VLOCLK
UCSCTL3 |= SELREF_2; //將REFCLK配置為REFOCLK
while(SFRIFG1 & OFIFG)
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); //清除三類時鍾故障標志位,等待起振
SFRIFG1 &= ~OFIFG; //清除震盪器故障標志位
}
VLOCLK:內部低功耗、低頻時鍾源,頻率10kHz,精度較低,會隨電源電壓和溫度產生較大漂移,用於不需要精准時鍾基准的系統控制,被使用時自動開啟、不使用時自動關閉,低功耗喚醒模式下回優先使用該時鍾源作為系統和看門狗時鍾
配置UCSCTL4選擇
REFOCLK:內部修整低頻參考時鍾源,精度較高,32.768kHz,和VLOCLK一樣不需要配置寄存器進行起振,若未使用外部晶振,系統會自動選擇該時鍾源作為ACLK和DCOCLK鎖頻環參考時鍾源
通過UCSCTL4選擇
DCOCLK:內部數字控制時鍾源,具有寬工作頻率,最高可產生25MHz時鍾頻率,可以和FLL配合控制參考時鍾,也可以引入其他時鍾源反饋進行時鍾分頻/倍頻,但是需要額外配置
這是f5xx中最常用的時鍾源,類似於stm32的PLL時鍾(它的內部也是類似的PLL)
其頻率計算公式如下
REFCLK來源見上文
n為輸入時鍾分頻,通過UCSCTL3中的FLLCLKDIV設定,查找該寄存器介紹可知其取值0-7,對應n取值2p,默認為0,不分頻
D通過UCSCTL2中的FLLD設對,可取值0-7,對應D取值2p,默認為1,即D=2,二分頻
N可以通過UCSCTL2中的FLLN設定,取值0-1023,當FLLN=0時,N=1,除此之外N=FLLN,默認為31,即N=31
如果系統復位后不進行任何設置,DCOCLK=2097152Hz,DCOCLKDIV=1048576Hz
MCLK和SMCLK都默認選擇DCOCLKDIV作為時鍾源。
通過配置DCORSEL、DCOx、MOD來選擇DCO的頻率設置范圍(最小值和最大值)
詳細內容參考datasheet
MODOSC:內部模塊振盪器,是UCS時鍾模塊下屬的振盪器,能產生4.8MHz的MODCLK時鍾,用於FLASH、ADC等片上外設
MCLK:為CPU和片上外設提供主時鍾,默認使用DCOCLKDIV
通過配置DIVM選擇MCLK分頻系數為1、2、4、8、16、32
UCSCTL4 = UCSCTL4 & (~SELM_7) | SELM_0; //XT1CLK時鍾源
UCSCTL4 = UCSCTL4 & (~SELM_7) | SELM_1; //VLOCLK時鍾源
UCSCTL4 = UCSCTL4 & (~SELM_7) | SELM_2; //REFOCLK時鍾源
UCSCTL4 = UCSCTL4 & (~SELM_7) | SELM_3; //DCOCLK時鍾源
UCSCTL4 = UCSCTL4 & (~SELM_7) | SELM_4; //DCOCLKDIV時鍾源
UCSCTL4 = UCSCTL4 & (~SELM_7) | SELM_5; //XT2CLK時鍾源
UCSCTL5 = UCSCTL5 & (~DIVM_7) | DIVM_0; //MCLK不分頻
UCSCTL5 = UCSCTL5 & (~DIVM_7) | DIVM_1; //MCLK 2分頻
UCSCTL5 = UCSCTL5 & (~DIVM_7) | DIVM_2; //MCLK 4分頻
UCSCTL5 = UCSCTL5 & (~DIVM_7) | DIVM_3; //MCLK 8分頻
UCSCTL5 = UCSCTL5 & (~DIVM_7) | DIVM_4; //MCLK 16分頻
UCSCTL5 = UCSCTL5 & (~DIVM_7) | DIVM_5; //MCLK 32分頻
ACLK:輔助時鍾,專用來為外圍模塊提供信號。默認使用XT1CLK時鍾源,如果未起振,則使用REFOCLK。配置方法和MCLK完全一致
SMCLK:子系統主時鍾,和MCLK基本一致,只是不為CPU提供時鍾
上電復位后,UCS默認配置如下:
- ACLK選擇XT1為時鍾源,如果未起振則使用REFOCLK並生成時鍾故障標志
- MCLK選擇DCOCLKDIV
- SMCLK選擇DCOCLKDIV
需要注意:msp430f5529的XTIN和XTOUT引腳默認為GPIO功能,並在上電情況下不會啟動,需要額外進行軟件設置
同時P7.7、P2.2、P1.0分別能夠對外輸出MCLK、SMCLK、ACLK時鍾
這里用最為復雜的DCO配置說明整個時鍾系統的配置流程
#include <msp430f5529.h>
#include <stdint.h>
void SetVcoreUp(uint_32_t level); //提升核心電壓以提升工作頻率
void XT1_ON(void); //啟動XT1
void DCO__16MHz(void); //使用DCO將XT1倍頻到16MHz
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
P1SEL |= BIT0; //P1.0 ACLK輸出
P1DIR |= BIT0;
P2SEL |= BIT2; //SMCLK輸出
P2DIR |= BIT2;
P7SEL |= BIT7; //MCLK輸出
P7DIR |= BIT7;
P7DIR |= BIT0; //P7.0 LED驅動輸出
P1OUT |= BIT0;
XT1_ON();
DCO__16MHz();
while(1)
{
__delay_cycles(8000000); //每0.5s
P7OUT ^= BIT0 ;//LED狀態翻轉一次
}
}
void SetVcoreUp(uint_32_t level)
{
PMMCTL0_H = PMMPW_H; //解鎖PMM寄存器,允許寫入
SVSMHCTL = SVSHE + SVSHRVL0 * level + SVMHE + SVSMHRRL0 * level; //設置SVS/SVM高側到新的等級
SVSMLCTL = SVSLE + SVMLE + SVSMLRRL0 * level; //設置SVS低側到新的等級
while((PMMIFG & SVSMLDLYIFG) == 0); //等待SVM穩定
PMMIFG &= ~(SVMLVLRIFG + SVMLIFG); //清除已經置位的標志
PMMCTL0_L = PMMCOREV0 * level; //設置VCORE到新的等級
if((PMMIFG & SVMLIFG)) //等待達到新的電壓等級
{
while((PMMIFG & SVMLVLRIFG) == 0);
}
//設置SVS/SVM低側到新的水平
SVSMLCTL = SVSLE + SVSLRVL0 * level + SVMLE + SVSMLRRL0 * level;
PMMCTL0_H = 0x00; //鎖住PMM的寫入路徑
}
void XT1_ON(void)
{
P5SEL |= BIT4 |BIT5; //配置XT1引腳
UCSCTL6 |= XCAP_3; //配置電容為12pF
UCSCTL6 &= ~XT1OFF; //使能XT1
while(SFRIFG1 & OFIFG)
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG); //清除三類時鍾錯誤標志位
SFRIFG1 &= ~OFIFG; //清除時鍾錯誤標志位
}
}
void DCO__16MHz(void)
{
SetVcoreUp(1); //一級一級提升核心電壓,不能跨級
SetVcoreUp(2);
/* 配置寄存器使DCOCLK=4.9MHz,DCOCLKDIV=2.45MHz */
__bis_SR_register(SCG0); //關閉FLL庫函數
UCSCTL0 = 0x0000; //清零寄存器值,FLL運行時系統會自動配置該寄存器
UCSCTL1 = DCORSEL_5; //選擇DCOCLK頻率范圍 6-23.7MHz
//FLLD=0,則D=1;FLLN=487,則N=487;N在UCSCTL3寄存器,默認值為1,則DCOCLK=1*(487+1)*32768=15.990784MHz
//DCODIVCLK=(487+1)*32768=15.990784MHz
UCSCTL2 = FLLD_0 + 487;
__bic_SR_register(SCG0); //開啟FLL控制回路
__delay_cycles(76563); //延時等待時鍾穩定
while(SFRIFG1 & OFIFG) //檢測時鍾錯誤並等待時鍾穩定
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);
SFRIFG1 &= ~OFIFG;
}
}
低功耗配置
msp430一共有8種工作模式
- 活躍模式(AM)
- 低功耗模式(LPM)0
- 低功耗模式1
- 低功耗模式2
- 低功耗模式3
- 低功耗模式3.5
- 低功耗模式4
- 低功耗模式4.5
但是並不是所有系列都支持這些工作模式,對於f5529來說,不支持LPM3.5
使用以下指令來進入和退出低功耗模式0-4
/* 開總中斷並進入低功耗模式 */
__bis_SR_register(LPMn_bits + GIE);
/* 退出低功耗模式 */
LPMn_EXIT; //其中n可以換成數字0-4
需要注意:低功耗模式喚醒都需要使用外部中斷,所以需要在進入低功耗模式同時開啟總中斷
在最高級別LPM4.5低功耗模式下,RAM中內容會直接丟失,所以在從LPM4.5喚醒后需要重新配置寄存器和相關設置
中斷
低功耗模式喚醒使用的指令實際上是通過直接修改SR寄存器的值,清除休眠標志,它內聯到以下函數
_bic_SR_register_on_exit(LPM3_bits);//退出LPM3
MSP430和arm一樣都具有系統中斷、不可屏蔽中斷和可屏蔽中斷三種,其中系統中斷和不可屏蔽中斷優先級最高;可屏蔽中斷可以通過狀態寄存器SR中的GIE位來屏蔽和開啟
大致的中斷作用與arm類似,而使用方式與51類似
下面主要介紹外部中斷
-
初始化端口時要清空中斷標志位
P1IFG &= ~(BIT0); //清空中斷標志位
-
使用
PIES
寄存器選擇觸發邊沿(0為上升沿,1為下降沿)並使用PxIE
寄存器使能中斷P1IES &= ~BIT0; //P1.0上升沿觸發 P1IES |= BIT1; //P1.1下降沿觸發 P1IES |= BIT2; //P1.2下降沿觸發 P1IE |= BIT0; //使能P1.0中斷 P1IE |= BIT1; //使能P1.1中斷 P1IE |= BIT2; //使能P1.2中斷
-
開總中斷
__bis_SR_register(GIE);
-
編寫中斷服務函數
#pragma vector = 中斷向量地址 __interrupt void ISR_function(void) { //中斷服務函數 }
-
在中斷函數內部清零中斷標志位
__even_in_range(P1IV, 16); //用於查詢P1的所有中斷標志位並自動清零,使用該函數可以實現將所有P1的外部中斷放在同一個函數內解決的功能,如下所示 switch(__even_in_range(P1IV, 16)) { case 0: //無中斷 break; case 2: //P1IFG.0 /* 中斷處理函數 */ break; case 4: //P1IFG.1 /* 中斷處理函數 */ break; case 6: //P1IFG.2 /* 中斷處理函數 */ break; case 8: //P1IFG.3 break; case 10: //P1IFG.4 break; case 12: //P1IFG.5 break; case 14: //P1IFG.6 break; case 16: //P1IFG.7 break; default: //出錯情況 break; }
此外,msp430還支持手動的嵌套中斷
示例程序如下所示(TI的七段數碼管驅動示例程序)
#include <msp430.h>
const unsigned char SEVENSEG_OUTPUT[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
unsigned char loopCounter;
unsigned char timeCounter1;
unsigned char timeCounter2;
unsigned char timeCounter3;
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; //關閉看門狗
PADIR = 0x03ff; // P1, P2.0 and P2.1 output, P2.6 and P2.7 input
PAOUT = 0xc03f;
P2REN = 0xc0; // P2.6 P2.7 上拉電阻使能
P2IES = 0x3f; // P2.6 P2.7 配置中斷為上升沿
P2IE = 0xc0; // P2.6 P2.7 interrupt enabled
PM5CTL0 &= ~LOCKLPM5; // 關閉GPIO高阻抗模式
RTCMOD = 50; // 設置RTC重裝計數值為50
// 64/32768 * 51 = ~0.1 sec.
SYSCFG2 |= RTCCKSEL; // Source = ACLK = REFO,64分頻,選擇ACLK作為RTC時鍾
RTCCTL = RTCSS_1 | RTCSR | RTCPS__64;
P2IFG = 0; // 清除P1.3中斷標志位
__bis_SR_register(GIE); //開全局中斷
while(1)
{
PAOUT |= (BIT9 | SEVENSEG_OUTPUT[timeCounter3]); // 按順序顯示七段數碼管數字
__delay_cycles(100);
PAOUT = 0xc000; // 清除引腳
PAOUT |= (BIT8 | SEVENSEG_OUTPUT[timeCounter2]);
__delay_cycles(100);
PAOUT = 0xc000;
P1OUT |= (BIT7 | SEVENSEG_OUTPUT[timeCounter1]);
__delay_cycles(100);
P1OUT = 0;
}
}
//RTC中斷服務函數
#pragma vector=RTC_VECTOR
__interrupt void RTC_ISR(void)
{
__bis_SR_register(GIE); // 允許嵌套中斷
RTCIV = 0;
timeCounter1++; // timeCounter1代表0.1s, timeCounter2代表1s,timeCounter3代表10s,經典延時操作
if(timeCounter1 > 9)
{
timeCounter1 = 0;
timeCounter2++;
if(timeCounter2 > 9)
{
timeCounter3++;
timeCounter2 = 0;
}
if(timeCounter3 > 9)
{
timeCounter3 = 0;
}
}
}
//P2中斷服務函數
#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)
{
__bis_SR_register(GIE); // 允許嵌套中斷
if(P2IFG & BIT6)
{
P2IFG &= ~BIT6; // 清除P2.6中斷標志位
// 第一次按下按鈕時開啟定時器;第二次按下時停止定時器
if(loopCounter == 0)
{
loopCounter++; //開啟一輪循環
RTCCTL |= RTCIE; //開啟定時器
}
else
{
RTCCTL &= ~RTCIE; //關閉定時器
loopCounter = 0; //循環清零
}
}
if(P2IFG & BIT7)
{
P2IFG &= ~BIT7; // 清除P2.7中斷標志位並復位秒表
RTCCTL &= ~RTCIE; //關閉定時器
loopCounter = 0; //復位所有變量
timeCounter3 = 0;
timeCounter2 = 0;
timeCounter1 = 0;
}
}
實現中斷嵌套需要注意以下幾點:
msp430默認關閉中斷嵌套,一定要中斷嵌套的話,就必須在中斷服務程序中打開總中斷
msp430的指令中,_DINT()和_EINT()分別指關和開總中斷
當進入中斷服務程序時,只要不在中斷服務程序中再次開中斷,則總中斷是關閉的,此時來中斷不管是比當前中斷的優先級高還是低都不執行
若在中斷服務程序A中開了總中斷,則可以響應后來的中斷B(不管B的優先級比A高還是低),B執行完再繼續執行A
注意:進入中斷服務程序B后總中斷同樣也會關閉,如果B中斷程序執行時需響應中斷C,則此時也要開總中斷,若不需響應中斷,則不用開中斷,B執行完后跳出中斷程序進入A程序時,總中斷會自動打開
若在中斷服務程序中開了總中斷,后來的中斷同時有多個,則會按優先級來執行,即中斷優先級只有在多個中斷同時到來時才起作用,中斷服務不執行搶先原則
對於單源中斷,只要響應中斷,系統硬件會自動清除中斷標志位。對於TA/TB定時器的比較/捕獲中斷,只要訪問TAIV/TBIV,標志位就會被自動清除;對於多源中斷要手動清標志位,比如P1/P2口中斷,要手工清除相應的標志。如果在這種中斷里用_EINT();
開中斷,而在打開中斷前沒有清標志,就會有相同的中斷不斷嵌入,導致堆棧溢出引起復位,所以在這類中斷中必須先清標志再打開中斷開關
常用中斷向量表
#define BASICTIMER_VECTOR (0 * 2u) /* 0xFFE0 Basic Timer */
#define PORT2_VECTOR (1 * 2u) /* 0xFFE2 Port 2 */
#define USART1TX_VECTOR (2 * 2u) /* 0xFFE4 USART 1 Transmit */
#define USART1RX_VECTOR (3 * 2u) /* 0xFFE6 USART 1 Receive */
#define PORT1_VECTOR (4 * 2u) /* 0xFFE8 Port 1 */
#define TIMERA1_VECTOR (5 * 2u) /* 0xFFEA Timer A CC1-2, TA */
#define TIMERA0_VECTOR (6 * 2u) /* 0xFFEC Timer A CC0 */
#define ADC12_VECTOR (7 * 2u) /* 0xFFEE ADC */
#define USART0TX_VECTOR (8 * 2u) /* 0xFFF0 USART 0 Transmit */
#define USART0RX_VECTOR (9 * 2u) /* 0xFFF2 USART 0 Receive */
#define WDT_VECTOR (10 * 2u) /* 0xFFF4 Watchdog Timer */
#defineCOMPARATORA_VECTOR (11 * 2u) /* 0xFFF6Comparator A */
#define TIMERB1_VECTOR (12 * 2u) /* 0xFFF8 Timer B CC1-6, TB */
#define TIMERB0_VECTOR (13 * 2u) /* 0xFFFA Timer B CC0 */
#define NMI_VECTOR (14 * 2u) /* 0xFFFC Non-maskable */
#define RESET_VECTOR (15 * 2u) /* 0xFFFE Reset [HighestPriority] */
定時器
msp430有四個基本定時器,外加一個RTC定時器,部分定時器具有PWM輸出功能
計數器核心是一個計數寄存器,對輸入的時鍾信號進行計數,可以配置其捕獲跳變沿種類和分頻系數,使用寄存器TAxR獲取當前計數值。定時器主要分成兩部分:主計數器和捕獲比較器模塊。捕獲比較器模塊與主計數器模塊通過TAxR連通,主計數器會根據輸入的信號跳變沿遞增/遞減寄存器TAxR/TBxR的值,捕獲比較器會根據收到的比較值對寄存器的值進行檢查,根據當前模式不同做出不同反應,因為計數寄存器被共用,所以可以將其分成多個通道。一般當捕獲比較器滿足設置的條件時就會產生中斷,存儲計數值或輸出相應的信號。只需要主計數器即可完成定時工作,捕獲比較器的作用則在於配合主計數器完成更多擴展功能
-
Timer_A:16位定時器(最大值65535),具有7個捕獲比較器,支持多路捕獲比較、PWM輸出、間隔定時功能
F5529中包含兩個Timer_A模塊,記作Timer_A0、Timer_A1、Timer_A2,三個模塊的主計數器在結構上完全相同,單捕獲比較器的數量不相同:Timer_A0有7個,Timer_A1和Timer_A2各有3個。
-
Timer_B:16位定時器(最大值65535),具備Timer_A的所有功能,但它還具備雙緩沖比較鎖存與同步加載功能
定時器的基本模式如下:
-
捕獲器模式
觸發信號到來時捕獲器將計數寄存器的值復制到捕獲比較器的計數值寄存器TAxCCRn/TBxCCRn,並產生中斷請求
-
比較器模式
需要程序向計數值寄存器TAxCCRn/TBxCCRn中寫入初值,當主計數器的計數寄存器TAxR/TBxR計數值達到寄存器中存儲的初值后定時器模塊就會向CPU請求中斷
通過配置TASSEL可以選擇時鍾來自ACLK、SMCLK、TAxCLK(外部輸入)或INCLK(定時器級聯)
定時器A
msp430的定時器A的主計數器具有以下幾個工作模式
-
增模式
設備會重復從0自增到TAxCCRn的值,溢出時觸發中斷
最基礎的功能
-
連續模式
設備會重復從0自增到0FFFFh,然后從0重新開始計數
一般用於生成獨立的時間間隔和輸出頻率,時間間隔完成時會生成中斷
起始設置TAxCCRn的初值,並在中斷服務函數中重新設置TAxCCRn的值,使其與初值的計數個數相同即可產生固定的時間間隔,這種操作還可以應用多個通道,因為多通道之間相互獨立
最基礎的功能
-
增減模式
定時器從0自增到TAxCCR0,再自減到0,也就是說其定時周期為兩倍的TAxCCR0
捕獲比較器工作模式如下:
通過設置捕獲比較器中的CAP位可以選擇捕獲比較器的工作模式為比較模式(0)或捕獲模式(1),
-
捕獲模式
當CAP=1時選擇捕獲模式,用於記錄時間時間。
觸發信號輸入CCIxA/CCIxB連接外部的引腳或內部的信號,通過CCIS位來選擇;通過CM位選擇觸發捕獲事件的輸入信號觸發沿
每當觸發信號到來時,捕獲比較器會1. 將TAxR的值復制到TAxCCRn寄存器中;2. 將捕獲器中斷標志CCIFG置位觸發中斷
-
比較模式
如果計數器TAxR的值和某個TAxCCRn的值相等時,相應的中斷標志位會被置位,產生一個比較中斷。一般該模式用於產生PWM信號
-
輸出模式
傳統的定時器通過標志位判斷來觸發事件,但msp430配備了專用的輸出模塊,使用輸出模塊寄存器OUTMODEx可以快速輸出PWM信號或其他控制信號
定時器中斷
定時器A具有兩個中斷源,捕獲比較器0中斷獨立,其他所有中斷(定時器溢出中斷、捕獲比較器1中斷、捕獲比較器2中斷等等)共用中斷源,通過TAxIV來確定具體觸發中斷的中斷源
使用步驟如下:
-
設置主計數器時鍾
TA0CTL = TASSEL_1;
-
設置分頻
TA0EX0 = TAIDEX_7 //8分頻
-
初始化CCR寄存器(設置初值)並使能中斷
TA0CCR0 = 9000; //設置初值 TA0CCTL0 = CCIE; //使能TA0CCR0中斷
-
設置捕獲比較器模式
TA0CTL |= MC_2 + TACLR; //清除TA0R,啟動定時器,選擇連續計數模式
也可以使用下面的代碼啟用其他模式
MC_1 增模式 MC_3 減增模式
-
開總中斷
__bis_SR_register(GIE);
-
配置中斷服務函數
#pragma vector = TIMER0_A0_VECTOR //TA0CCR0中斷 __interrupt void TA0CCR0_ISR(void) { TA0CCR0 += 16384; //添加偏置 /* 中斷處理函數 */ }
定時器B
在捕獲比較器和比較器之間加入的比較鎖存器可以分組控制比較值載入的時刻,實現同步更新數據
在Timer_B中可以通過配置寄存器TBxCCTLn來選擇TBxCCRn載入TBxCLn的時刻,在Timer_B中起到比較作用的是比較鎖存器TBxCLn而不是CCR寄存器,當TBxR的值達到TBxCLn時,相應的中斷標志位置位,產生比較器中斷請求,TBxCCRn的值會在寄存器設置的時間點載入TBxCLn,從而實現比較延時更新
輸入捕獲
使用定時器的外部計數功能來測量脈沖個數,可用於測速、編碼器驅動等場合
基本使用方式和上面的定時器中斷類似,但是需要以下額外語句
-
配置外部時鍾源,使能溢出中斷
TA0CTL = TASSEL_0 + TAIE;
-
清除TAxR、啟動定時器並工作於連續模式
TA0CTL |= TACLR + MC_2;
-
在定時器中斷服務函數內遞增全局計數變量用於獲取輸入捕獲的脈沖數
uint8_t counter; uint8_t loop; #pragma vector = TIMER0_A1_VECTOR __interrupt void TA0_ISR(void) { counter++; if(counter>10) { loop++; counter=0; } }
PWM輸出
msp430可以實現在不占用CPU資源的情況下輸出PWM信號,程序如下所示
#include <msp430f5529.h>
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; //關閉看門狗
P1DIR |= BIT2 | BIT3;
P1SEL |= BIT2 | BIT3; //設置為定時器復用
TA0CCR0 = 512-1; //PWM周期,頻率=32768/512=64
TA0CCTL1 = OUTMODE_7; //CCR1輸出模式7
TA0CCR1 = 384; //CCR1 PWM占空比設置為384/512=75%
TA0CCTL2 = OUTMODE_7; //CCR2輸出模式7
TA0CCR2 = 128; //CCR2 PWM占空比設置為128/512=25%
//開啟定時器
TA0CTL = TASSEL_1 + MC_1 + TACLR; //定時器時鍾設置為32768Hz的ACLK,配置為增模式,清空TA0R
while(1)
{
/* 可實現在P1.2上輸出75%占空比,在P1.3上輸出25%占空比,頻率都是64Hz的PWM信號 */
}
}
總線協議
msp430f5529中配備了通用串行通信接口模塊USCI,它支持了多種通信模式,UART、SPI、IIC都可以使用該外設進行處理
使用方法很類似,僅給出示例代碼
UART
串口接收並復讀數據
#include <msp430f5529.h>
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
/* 初始化UART */
P4SEL |= BIT4 + BIT5; //配置P4.4、P4.5為USCI_A1 Tx、Rx
UCA1CTL1 |= UCSWRST; //復位USCI_A1
UCA1CTL1 |= UCSSEL_2; //SMCLK 無校驗位 8字符長度 1個停止位
UCA1BR0 = 9; //低8位=9
UCA1BR1 = 0; //高8位=0,調制后波特率約為115200bps
UCA1MCTL |= UCBRS_1 + UCBRF_0; //調制器UCBRSx=1,UCBRFx=0
UCA1CTL1 &= ~UCSWRST; //啟動USCI_A1
UCA1IE |= UCRXIE; //使能USCI_A1接收中斷
__bis_SR_register(LPM0_bits + GIE); //使能全局中斷
while(1);
}
#pragma vector=USCI_A1_VECTOR
__interrupt void USCI_A1_ISR(void)
{
switch(__even_in_range(UCA1IV, 4))
{
case 0: //無中斷
break;
case 2: //接收中斷RXIFG
UCA1TXBUF = UCA1RXBUF; //復讀
break;
case 4: //發送中斷TXIFG
break;
default:
break;
}
}
UCA1TXBUF:串口發送寄存器
UCA1RXBUF:串口接收寄存器
UCA1IE:串口中斷控制寄存器,可以選擇接收中斷、發送中斷
IIC
需要注意,msp430的IIC無法內部上拉,所以必須在外部接入4.7k的上拉電阻到VCC(3.3V)
下面的代碼僅說明如何將USCI配置成IIC驅動模式
-
包含頭文件
#include <msp430.h>
-
初始化引腳復用功能
P4SEL |= BIT1 + BIT2;
-
初始化USCI,並配置傳輸速率
UCB1CTL1 |= UCSWRST; //復位USCI_B1 UCB1CTL0 |= UCMST + UCMODE_3 + UCSYNC; //配置為IIC主機,同步模式 UCB1CTL1 = UCSSEL_2 + UCSWRST; //SMCLK,保持UCSWRST置位 UCB1BR0 = 12; //fscl=SMCLK/12=100kHz UCB1BR1 = 0;
-
設置從機地址
這里假設從機地址是0x48
UCB1I2CSA = 0x48; //從機地址0x48
-
啟動外設
UCB1CTL1 &= ~UCSWRST; //清除復位標志,外設開始運行
-
編寫中斷控制函數
uint8_t IIC_RXByte; uint8_t IIC_TXByte; uint8_t *PRxData; //接收緩存 uint8_t *PTxData; //發送緩存 #pragma vector=USCI_B1_VECTOR __interrupt void USCI_B1_ISR(void) { switch(__even_in_range(UCB1IV, 12)) { case 0: //無中斷 break; case 2: //ALIFG break; case 4: //無響應中斷NACKIFG break; case 6: //STTIFG break; case 8: //停止位中斷STPIFG break; case 10: //接收中斷RXIFG IIC_RXByte--; //遞減字節計數變量 if(IIC_RXByte) //如果沒有接收完畢 { *PRxData++ = UCB1RXBUF; //接收剩下的數據到緩存區 if(IIC_RXByte == 1) //檢查是否只剩一個字節未接收 { UCB1CTL1 |= UCTXSTP; //發送停止條件 } } else //已經接收完畢 { *PRxData = UCB1RXBUF; //將最后一字節數據存儲到緩存區 UCB1IE &= ~UCRXIE; //禁用接收中斷 __bic_SR_registe_on_exit(LPM0_bits); //退出LPM0,進入活躍模式 } break; case 12: //發送中斷TXIFG if(IIC_TXByte) //如果沒有發送完畢 { UCB1TXBUF = *PTxData; //IIC發送數據 IIC_TXByte--; //遞減字節計數變量 } else //已經接收完畢 { UCB1CTL1 |= UCTXSTP; //置位發送停止條件位 UCB1IFG &= ~UCTTXIFG; //清除發送中斷標志位TXIFG UCB1IE &= ~UCTXIE; //禁用發送中斷 __bic_SR_registe_on_exit(LPM0_bits); //退出LPM0,進入活躍模式 } break; default: break; } }
-
編寫相關驅動
TxData[8] = {0}; //發送緩存區 RxData[8] = {0}; //接收緩存區 uint8_t IIC_RXByte; uint8_t IIC_TXByte; uint8_t *PRxData; //接收緩存 uint8_t *PTxData; //發送緩存 void main(void) { /* 這里省略初始化部分 */ disable_WDG(); init_iic(); while(1) { /* 發送設置 */ PTxData = TxData; IIC_TXByte = 8; while(UCB1CTL1 & UCTXSTP); //確保停止條件已發送,總線空閑 UCBCTL1 |= UCTR; //設置主機工作在發送機模式 UCBCTL1 |= UCTXSTT; //發送開始條件並發送有“寫標志位”的地址 UCB1IE |= UCTXIE; //使能發送中斷 __bis_SR_register(LPM0_bits + GIE); //進入LPM0,使能全局中斷 /* 接收設置 */ PRxData = RxData; IIC_RXByte = 8; while(UCB1CTL1 & UCTXSTP); //確保停止條件已發送,總線空閑 UCBCTL1 &= ~UCTR;//設置主機工作在接收機模式 UCBCTL1 |= UCTXSTT; //發送開始條件並發送有“讀標志位”的地址 UCB1IE |= UCRXIE; //使能接收中斷 __bis_SR_register(LPM0_bits + GIE); //進入LPM0,使能全局中斷 } }
SPI
使用以下代碼設置SPI
/* 這里省略端口復用設置 */
UCB0CTL1 |= UCSWrST;
UCB0CTL0 |= UCMST + UCSYNC; //設置為三線SPI主機模式,8位數據位
UCB1CTL1 = UCSSEL_2; //時鍾設置為SMCLK
UCB1BR0 = 0xFF; //UCB0CLK = SMCLK / 0xFFF
UCB1BR1 = 0x0F; //一般來說可以選擇100kHz以上的頻率,通常使用4MHz頻率
UCB1CTL1 &= ~UCSWRST; //清除復位標志,外設開始運行
中斷和驅動編寫部分和IIC基本一致,不再贅述
片上模擬外設
msp430集成了12位ADC/DAC和模擬比較器外設
ADC
f5529配備了SAR架構的ADC12_A模塊,支持12位ADC,具有16個模擬輸入通道、16個獨立的轉換和存儲單元,可在脫離CPU情況下完成ADC轉換,最高200ksp(千次采樣/每秒)
基本配置流程如下:
-
配置核心控制寄存器,選擇時鍾、轉換模式、啟動參考電壓生成器
ADC12_A內部具有獨立的REF模塊,可以提供1.5V、2V、2.5V參考電壓。通過REF模塊的REFMSTR位選擇參考電壓:置1時(默認狀態)使用REF模塊控制參考電壓;置0時使用ADC12_A的參考電壓模塊控制參考電壓
使用ADC12REF2_5V控制參考電壓大小,ADC12REFON控制是否開啟電壓生成器,ADC12REFOUT控制是否輸出參考電壓
ADC使用ADC12CLK時鍾用來控制采樣和轉換的時間和周期,時鍾源可選擇SMCLK、MCLK、ACLK和ADC12OSC(UCS模塊的MODCLK的5MHz內部振盪器),時鍾源使用ADC12DIV控制的預分頻器和ADC12SSELx控制的分頻器進行分頻,可選擇1-32分頻
基本配置程序如下
ADC12CTL0 = ADC12ON+ADC12SHT0_8+ADC12MSC; //開啟ADC12,設置采樣時間,設置采樣模式(這里使用了多采樣轉換)
ADC具有4種模式,通過CONSEQx位選擇
- 單通道模式:單通道只采樣和轉換一次,當ADC12SC置位時觸發一次采樣轉換操作,持續一段時間后自動復位
- 序列通道(自動掃描)模式:使用CSTARTADDx位選擇開始轉換的第一個ADC1MCTLx,指定后序列啟動指針會自動遞增,被它指向的通道會自動開始轉換,轉換完成后自動復位,操作一直繼續直到處理到ADC12EOS=1的ADC12MCTLx才會停止,ADC12EOS作為序列結束的標志,只在序列轉換模式下使用
- 重復單通道模式:一個單獨的通道會被不斷采樣轉換,可以設置完成中斷來讀取轉換結果
- 重復序列通道(自動重復)模式:一序列通道會被重復采樣和轉換,使用CSTARTADDx定義第一個ADC12McTLx,序列再檢測到ADC12EOS(序列結束標志)置位后會自動結束,下一個觸發信號將重新開始序列
-
保持其處於禁用狀態,ADC12ENC=0
-
配置引腳復用
將GPIO復用為ADC輸入引腳
PxSEL |= 0x0n; //選擇Px.n引腳復用為ADC輸入
注意:只有能接入ADC的對應IO口才能復用
-
配置采樣定時器
一次轉換由一個采樣信號SHI的上升沿引起,可通過SHSx位來選擇,可以選為直接由ADC12SC位控制或使用定時器來控制
- ADC12SHP=0,使用擴展采樣模式,SHI信號直接控制SAMPCON並定義采樣周期長度;SAMPCON=1時采樣活躍,SAMPCON的下降沿會在同步ADC12CLK信號后啟動轉換
- ADC12SHP=1,使用脈沖采樣模式,SHI信號用於觸發采樣定時器,采樣定時器在同步AD12CLK后將SAMPCON保持在高電平並持續一個可編程的間隔\(t_{sample}\),整個采樣時間就是\(t_{sample}+t_{sync}\)
-
可單獨配置每個通道的參考電壓和輸入源
使用ADC12MCTLx(x為0-15)控制轉換存儲單元,通過ADC12SREF和ADC12INCH分別選擇參考電壓和模擬信號的輸入通道
在單通道單轉換模式中,復位ADC12ENC立刻停止一個轉換且會導致轉換結果不可預知,一般來說需要使用以下語句停止單通道轉換
while(ADC12BUSY != 0) { delay(); } ADC12ENC = 0; //等待ADC12BUSY = 0后才能停止單通道轉換
重復單通道模式下,復位ADC12ENC會在當前轉換結束時停止轉換器
序列通道或重復序列通道中,復位ADC12ENC會在序列結束時停止轉換器
任何模式中都可以通過清零ADC12CONSEQ並復位ADC12ENC位來立刻停止,但這樣會導致轉換結果不可預知
-
可以使能集成溫度傳感器或配置轉換完成中斷
ADC可以直接連接內部的溫度傳感器(這是參考電壓生成器的一部分)來獲取內部溫度,計算公式如下
\[T=(ADC_{raw} - CAL\_ADC\_T30) \times (\frac{85-30}{CAL\_ADC\_T85 - CAL\_ADC\_T30}) + 30 \]其中T是精確溫度值,\(ADC_{raw}\)是數模轉換結果,兩個\(CAL\_ADC\_T\)為溫度矯正參數,需要通過地址進行訪問調用,每個設備的矯正參數都不同,該參數會被使用TLV(Tag-Length-Value)的方式寫入單片機,地址調用形式如下
*((unsigned int*)0x1A1A) //具體地址需要根據電壓和設備的不同來確定,詳細內容參考datasheet即可
ADC12_A具有18個中斷,共用一個中斷源
可單獨配置某通道完成中斷、ADC12MEMx溢出中斷、ADC12_A計時溢出中斷,所有中斷通過唯一的中斷向量寄存器來配置,也就是說只使用一個中斷服務函數
注意:任何對於ADC12IV的讀寫操作都會自動復位ADC12OV或ADC12TOV,如果中斷服務函數在訪問ADC12IV寄存器時有ADC12OV和ADC12IFGx中斷生成,那么ADC12OV中斷條件會自動復位,在中斷服務函數返回后緊接着處理其他中斷,因此應避免中斷執行時間過長導致ADC中斷占用前台應用
使用下面的程序來開啟ADC中斷
ADC12IE = 0x01;
-
將ADC12ENC=1來使能設備
ADC12CTL0 |= ADC12ENC; ADC12CTL0 |= ADC12SC;
-
通過讀取ADC12MCTLx對應的ADC12MEMx來獲取轉換結果
單通道單次轉換示例如下(測量引腳電壓高於參考電壓則指示燈亮)
#include <msp430.h>
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; //停止WDG
ADC12CTL0 = ADC12SHT02 + ADC12ON; //設置采樣時間並開啟ADC12_A
ADC12CTL1 = ADC12SHP; //使用采樣定時器
ADC12IE = 0x01; //使能中斷
ADC12CTL0 |= ADC12ENC; //使能轉換通道
P6SEL |= 0x01; //配置P6.1復用為ADC輸入
P1DIR |= 0x01; //配置P1.0信號指示輸出
while (1)
{
ADC12CTL0 |= ADC12SC; //開始依次采樣
__bis_SR_register(LPM0_bits + GIE); //進入LPM0,開啟全局中斷
__no_operation(); //用於調試器打斷點的空指令
}
}
/* ADC中斷服務函數 */
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
{
switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC溢出中斷
case 4: break; // Vector 4: ADC超時中斷
case 6: // Vector 6: ADC12IFG0
/* 測量P1引腳 */
if (ADC12MEM0 >= 0x7ff) // ADC12MEM = A0 > 0.5AVcc?
P1OUT |= BIT0; // P1.0 = 1
else
P1OUT &= ~BIT0; // P1.0 = 0
__bic_SR_register_on_exit(LPM0_bits); //退出LPM0模式
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: break; // Vector 12: ADC12IFG3
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}
多通道重復轉換示例如下(連續讀取P6.0、P6.1、P6.2、P6.3的ADC值)
#include <msp430.h>
#define Num_of_Results 8
volatile unsigned int A0results[Num_of_Results];
volatile unsigned int A1results[Num_of_Results];
volatile unsigned int A2results[Num_of_Results];
volatile unsigned int A3results[Num_of_Results];
int main(void)
{
WDTCTL = WDTPW+WDTHOLD; //關閉WDG
P6SEL = 0x0F; //使能ADC復用引腳(P6的低四位0、1、2、3進行復用)
ADC12CTL0 = ADC12ON+ADC12MSC+ADC12SHT0_8; //開啟ADC、擴展采樣時間避免結果溢出
ADC12CTL1 = ADC12SHP+ADC12CONSEQ_3; //使用采樣定時器,設置為多通道重復采樣模式
/* 設置0、1、2、3通道的參數 */
ADC12MCTL0 = ADC12INCH_0; // ref+=AVcc, channel = A0
ADC12MCTL1 = ADC12INCH_1; // ref+=AVcc, channel = A1
ADC12MCTL2 = ADC12INCH_2; // ref+=AVcc, channel = A2
ADC12MCTL3 = ADC12INCH_3+ADC12EOS; // ref+=AVcc, channel = A3 ,設置ADC結束標志
ADC12IE = 0x08; //使能ADC12IFG.3中斷
ADC12CTL0 |= ADC12ENC; //使能轉換通道
ADC12CTL0 |= ADC12SC; //開始轉換-軟件觸發
__bis_SR_register(LPM0_bits + GIE); //進入LPM0並開啟全局中斷
__no_operation(); //用於調試器打斷點
}
#pragma vector=ADC12_VECTOR
__interrupt void ADC12ISR (void)
{
static unsigned int index = 0;
switch(__even_in_range(ADC12IV,34))
{
case 0: break; // Vector 0: No interrupt
case 2: break; // Vector 2: ADC overflow
case 4: break; // Vector 4: ADC timing overflow
case 6: break; // Vector 6: ADC12IFG0
case 8: break; // Vector 8: ADC12IFG1
case 10: break; // Vector 10: ADC12IFG2
case 12: // Vector 12: ADC12IFG3
A0results[index] = ADC12MEM0; // Move A0 results, IFG is cleared
A1results[index] = ADC12MEM1; // Move A1 results, IFG is cleared
A2results[index] = ADC12MEM2; // Move A2 results, IFG is cleared
A3results[index] = ADC12MEM3; // Move A3 results, IFG is cleared
index++; // 保存到結果緩存區
if (index == 8)
{
(index = 0);
}
case 14: break; // Vector 14: ADC12IFG4
case 16: break; // Vector 16: ADC12IFG5
case 18: break; // Vector 18: ADC12IFG6
case 20: break; // Vector 20: ADC12IFG7
case 22: break; // Vector 22: ADC12IFG8
case 24: break; // Vector 24: ADC12IFG9
case 26: break; // Vector 26: ADC12IFG10
case 28: break; // Vector 28: ADC12IFG11
case 30: break; // Vector 30: ADC12IFG12
case 32: break; // Vector 32: ADC12IFG13
case 34: break; // Vector 34: ADC12IFG14
default: break;
}
}
DAC
msp430f5529配備了DAC12_A模塊,模塊組成結構如下:
-
核心
可以通過修改DAC12RES的值將DAC12_A配置為8位或12位模式;配置DAC12IR和DAC12OG位可以將滿標度輸出配置為所選參考電壓的1倍、2倍或3倍;配置DAC12DF位選擇輸入的數據格式是原碼還是補碼。
-
端口
大多數DAC復用的端口都有其他復用功能,但是當DAC12AMPx>0時,DAC12_A會忽略PxSEL.y和PxSEL.x的值,自動配置端口為DAC12_A輸出復用功能
每個DAC通道都能輸出到兩個不同的端口,通過DAC122OPS選擇,詳細參數需要查閱datasheet
-
參考電壓
使用DAC12SREFx選擇DAC12_A參考電壓,該值從AVCC、外部電壓輸入、內部1.16V參考電壓、內部REF模塊提供1.5V、2V、2.5V參考電壓之一選擇
-
參考輸入和電壓輸出緩沖區
參考輸入和電壓輸出緩沖區通過寄存器配置來平衡建立時間和功耗,通過配置DAC12AMPx來選擇組合,其值越小、建立時間越長、緩沖區上的電流消耗越小
-
數據格式
使用原碼或補碼形式都可以設置DAC,使用DAC12_xDAT(取值范圍0800h(輸出0V)-07ffh(輸出標度),另外取值0000h時輸出標度的一半)控制
相關示例程序如下
/* 輸出固定電壓 */
void main(void)
{
WDTCTL = WDTPW+WDTHOLD; //關閉WDG
//設置無增益,參考電壓AVCC,啟動DAC校准
DAC12_0CTL0 = DAC12IR + DAC12SREF_1 + DAC12AMP_5 + DAC12CALON;
DAC12_0CTL0 |= DAC12ENC; //使能DAC12_A
//需要注意這里的輸出值是12位
DAC12_0DAT = 0x000; //DAC輸出0V
//和上面一樣的流程
DAC12_1CTL0 = DAC12IR + DAC12SREF_1 + DAC12AMP_5 + DAC12CALON;
DAC12_0CTL0 |= DAC12ENC; //使能DAC12_A
DAC12_0DAT = 0x700; //輸出1.4V
__bis_SR_register(LPM4_bits); //進入LPM4,DAC會自動輸出
}
/* 輸出波形 */
//以FLASH換內存
static int Sin_tab[100] = {
1638,1740,1843,1944,2045,2143,2240,2335,2426,2515,
2600,2681,2758,2831, 2899,2962,3020,3072,3119,3160,
3195,3224,3246,3262,3272,3272,3263,3247,3224,3196,
3161,3120,3074,3021,2964,2901,2833,2760,2683,2602,
2517,2429,2337,2243,2146,2047,1947,1845,1743,1640,
1537,1435,1333,1233,1134,1037,943,851,762,677,596,
519,446,378,314,256,204,157,116,81,52,29,13,3,0,2,
12,28,50,78,113,154,200,252,310,373,440,513,590,
671,756,756,844,936,1030,1127,1225,1326,1427,1529};
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //停看門狗
INIT_XT2(); //開啟時鍾
P5SEL = 0XFF;
ADC12CTL0 = REFON; //參考電壓為內部2.5v
/* 配置DMA,直接將Sin_tab數據傳輸到DAC */
DMA0SA = (int) Sin_tab; //源地址寄存器
DMA0DA = DAC12_0DAT_; //目的地址寄存器
DMA0SZ = 100; //傳輸基本單元的個數
DMACTL0 = DMA0TSEL_5; // DAC12_0CTL的DAC12IFG標志
DMA0CTL = DMADT_4 + DMASRCINCR_3 + DMAEN; //DMADT_4:重復的塊傳輸方式
/* 使用內部1.5V參考電壓,無增益,使能DAC12_A校准並使能DAC12_A */
DAC12_0CTL = DAC12LSEL_2 + DAC12IR + DAC12AMP_5 + DAC12IFG + DAC12ENC; //配置DAC
/* 強制輸出第一個中斷 */
CCTL1 = OUTMOD_3; //設置並復位
CCR1 = 1; // PWM Duty Cycle
CCR0 = 8-1; //1kHz頻率
TACTL = TASSEL_2 + MC_1; //使用SMCLK時鍾源,向上計數模式
__bis_SR_register(LPM0_bits); //進入LPM0,DMA和DAC都在工作,會自動輸出
}
可編程比較器
f5529配備了Comp_B模塊,支持精密線性數模轉換、電源電壓監控、外部模擬信號電壓監測功能
其中核心是一個精密電壓比較器,同相端比反相端電壓高,則輸出高電平,否則輸出低電平,使用CBON位打開/關閉比較器
使用CBCTL0寄存器控制外部輸入端口,CBIPEN和CBIMEN分別控制同相端和反相端;使用CBIMSEL和CBIPSEL控制端子連接的GPIO,應選擇P6的端口,以這兩個寄存器控制端口號
可以使用CBSHORT短路正反相輸入,可用來建立簡單的采樣-保持機制,一般來說設置采樣時間為3-10τ,3τ可以將采樣電容充電到95%的輸入信號電壓值,5τ可以將采樣電容充電到99%,10τ可以滿足12位的精度
使用CBF控制位控制輸出信號的輸出濾波器
使用參考電壓生成器來生成VREF,可以應用於比較器輸入端,使用CBREF0x和CBREF1xl來控制
使用CBPWRMD來選擇比較器功耗模式,默認為00——最大功耗、最快速度;可以調節到11來使用最低功耗、最低速度
使用CBCTL3來控制比較器的端口是否禁用;使用CBIPSEL或CBIMSEL來控制對應端口的輸入緩沖區
比較器也可以開啟中斷
對於可編程電壓比較器而言,一般使用滯后比較來讓參考電壓根據輸出值變化,可以讓比較器輸出更加穩定,降低噪聲
代碼如下
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //停看門狗
P1DIR |= BIT6; //P1.6作輸出
P1SEL |= BIT6; //復用為比較器輸出CBOUT
P7DIR |= 0xFF; //P7設置為輸出,用來驅動LED顯示當前比較器結果
CBCTL0 |= CBIPEN + CBIPSEL_0; //比較器施恩那個,設置輸入通道CB0為P6.0
CBCTL1 |= CBPWRMD_0; //設置為高速模數
CBCTL2 |= CBRSEL; //使用VREF作為反相端的參考電壓
/* 設置高低兩個閾值電壓 */
CBCTL2 |= CBRS_1 + CBREF13; //以VCC為參考電壓,CBREF1=8,VREF=VCC/4
CBCTL2 |= CBREF04 + CBREF03; //VREF0=VCC * 3/4
CBCTL3 |= BIT0; //打開Comp_B
CBCTL1 |= CBON; //使能比較器
__delay_cycles(75); //等待比較器內部參考電壓達到穩定
while(1)
{
if(CBCTL1 & CBOUT) //如果CBOUT為高電平
{
P7OUT = 0x00; //LED點亮
}
else
{
P7OUT = 0xFF; //LED熄滅
}
}
}
以上代碼在輸入電壓大於VREF0=3/4 VCC時CBOUT輸出高電平,LED點亮;輸入電壓小於VREF0=1/4 VCC時CBOUT輸出低電平,LED熄滅;當電壓在二者之間時,CBOUT狀態不變,起到了穩定輸出的作用
同樣的比較器也可以產生中斷信號,在檢測到設定的跳變沿(上升沿或下降沿)后觸發中斷,進入對應的中斷服務函數,可以在其中判斷比較器輸出的電平,避免了輪詢
CBCTL1 |= CBPWRMD_1 + CBF + CBFDLY_3; //普通模式,選擇中斷邊壓,使用輸出濾波
/* 在中間設置其他比較器參數 */
/* 等待一定時間來讓參考電壓穩定 */
CBINT &= ~(CBIFG + CCBIIFG); //清除錯誤中斷標志
CBINT |= CBIE + CBIIE; //使能比較器輸出中斷和輸出反相中斷
__bis_SR_register(LPM0_bits + GIE); //開啟全局中斷
#pragma vector=COMP_B_VECTOR
__interrupt void Comp_B_ISR(void)
{
switch(__even_in_range(CBIV, 4))
{
case 0: //無中斷
break;
case 2: //中斷CBIFG
if(CBCTL1 & CBOUT) //判斷是否輸出高電平
{
}
break;
case 4: //反相中斷CBIIFG
if(!(CBCTL1 & CBOUT)) //反相端需要相反的判斷
{
}
break;
default:
break;
}
}
這里僅列出中斷服務函數和其開啟方式
DMA
msp430的DMA最多有8個通道,但是msp430f5529只有3個通道
基本使用方法和stm32的DMA完全一樣,四種模式、單傳輸、塊傳輸、連續傳輸都和stm32的傳輸方法類似,但設置更為簡單
一個使用DMA進行UART傳輸的例子如下所示:
#include <msp430.h>
#include <stdint.h>
static uint8_t String1[] = {"Hello World\r\n"};
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //停看門狗
/* 端口配置 */
P4SEL = BIT4 + BIT5; //配置P4.4、P4.5為Tx、Rx端口
/* 配置USCI_A1為UART模式 */
UCA1CTL1 = UCSSEL_1; //使用ACLK為時鍾源
//控制分頻器設置波特率為9600
UCA1BR0 = 0x03; //分頻器高八位
UCA1BR1 = 0x0; //分頻器低八位
UCA1MCTL = UCBRS_3 + UCBRF_0; //調制器UCBRSx = 3
UCA1CTL1 &= ~UCSWRST; //啟動設備
/* 配置DMA */
DMACTL0 = DMA0TSEL_1; //以定時器TA0CCR0 CCIFG為觸發源
__data16_write_addr((uint16_t)&DMA0SA, (uint32_t)String1); //源地址:String1字符串
__data16_write_addr((uint16_t)&DMA0DA), (uint32_t)&UCA1TXBUF); //目標地址:UART發送緩存區域
DMA0CTL = DMADT_4 + DMASRCINCR_3 + DMASBDB +DMAEN; //重復單傳輸,遞增模式,字節到字節,並使能DMA
/* 配置定時器 */
TA0CCR0 = 8192; //字符傳輸頻率=32768/8192=4 字節/s
TA0CTL = TASSEL_1 + MC_1; //使用ACLK作為時鍾源,采用增模式
__bis_SR_register(LPM0_bits); //進入LPM3
}
這個程序會連續向外以9600波特率發送串口數據
電源管理
msp430f5529的電源管理模塊PMM由監督器SVS和監視器SVM組成
SVS是強制要求的,用於保障設備穩定運行
SVM是寬松可編程的,用於進行一些低功耗控制和安全保障
可以通過對SVM編程提高VCORE來支持更高的MCLK,也就是所謂的超頻