实现一个简单的msp430软件
以做一个简单的采集设备为例。假定已经准备好硬件设备,现在我们开始在MCU上搭建运行的软件。
使用的软件为iar ew430 5.10b,仿真器是杭州利尔达的USB型MSP430仿真器LSD-FET430 UIF,晶振源为8Mhz。
开发环境第一步 新建一个项目,projiect->new project
new project确定后会提示输入项目名称保存位置等,按照提示填写完毕后,一个空的项目就建立成功了。
新的空白项目设置MCU芯片 设置堆栈 设置连接文件的位置对项目的属性进行设置,以便可以进行正常的调试
图中的连接文件直接放在了项目文件夹下所以这样设置,也可以使用默认设置。
如果需要改变段的大小的话,就需要对xcl文件进行修改。
因此最好将xcl文件放在项目文件夹下这样有助于定制化
xcl文件在IAR的安装地址的config文件夹下 xcl文件放置的位置 设置hex文件格式 后期远程升级会用到 输出文件 设置Debug方式
Debug有两种方式,一种是软件模拟调试,还有一种是JTAG调试 设置烧写方式
第二步开始code吧
任何软件都是从main函数开始,那我们就建一个main.c文件。
项目目的主要是完成数据采集,需要使用AD采集和UART。
首先为项目添加头文件,新建一个include.h文件,填入需要加入的头文件
#include "msp430x54xa.h"
#include "string.h"
使用类似方法新建其他的头文件如datatypedef.h --数据定义&globalvar.h--全局变量
在include.h中添加新的头文件
#include "msp430x54xa.h"
#include "datatypedef.h"
#include "globalvar.h"
#include "string.h"
新建main.c文件,加入如下内容
#include "include.h"
void main()
{
//硬件初始化
InitSys( );
//FlashWrite_seg((UCHAR *)0xAc00 , UartBuffer0 , 490 );
WDT_CLR;
//配置io口,内部模块
ConfigGpio();
uart_a0setup(3);//和sim1模块通讯,控制sim1
uart_a1setup(3);//和sim1模块通讯,控制sim1
uart_a2setup(3);//和PC通讯,用于调试
uart_a3setup(3);//用于和程控放大电路通讯
Timera0Config();//定时器,10ms周期
_EINT();
while(1);
}
这里面一些硬件初始化的函数还未定义,因此我们新建一个专门定义硬件初始化的文件,命名为device.c,填入如下内容
#include "include.h"
#include "string.h"
//==============================================================================
/*系统时钟配置,SMCLK = MCLK = 8M,ACLK = REFCLOCK = 32768*/
//==============================================================================
void InitSys( )
{
U_INT num = 0 ;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///设置基本时钟/////////////////////////////////////////////////////////////////////////////////////////////////
WDTCTL = WDTPW + WDTHOLD; // 关闭看门狗
P11DIR = BIT1 + BIT2 ; // P11.1-2 to output direction
//P11SEL = BIT1 + BIT2 ; // P11.1-2 to output SMCLK,MCLK
P5SEL = BIT2 + BIT3 ; // Port5.2 5.3 select XT2 ,5.0 5.1 selectBIT0 ++ BIT1 + BIT3
// for ref Vcc
UCSCTL6 &= ~XT2OFF; // 使能XT2【高频晶振】
// UCSCTL6 |= XT2BYPASS ; //
UCSCTL3 |= SELREF_2; // FLLref = REFO
// Since LFXT1 is not used,
// sourcing FLL with LFXT1 can cause
// XT1OFFG flag to set
UCSCTL4 |= SELA_2; // ACLK=REFO,SMCLK=DCO,MCLK=DCO
// Loop until XT1,XT2 & DCO stabilizes
do
{
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
// Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
for( num = 0xFF ; num > 0 ; num -- ) ;
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
UCSCTL6 &= ~( XT2DRIVE0 + XT1DRIVE0 ) ; // Decrease XT2 Drive according to
//UCSCTL5 |= DIVM__0 + DIVS__0; // expected frequency
//重新配置MCLK和SMCLK为高频时钟,8MHz
UCSCTL4 |= SELS_5 + SELM_5; // SMCLK=MCLK=XT2,ACLK=REF
}
void InitGpio()
{
PASEL = 0;
PBSEL = 0;
PCSEL = BIT2+BIT3+BIT0;
PDSEL = 0;
PESEL = 0;
PFSEL = 0;
// PGSEL = 0;
PAOUT = 0;
PBOUT = 0;
P4OUT |= BIT0;
PCOUT = 0;
PDOUT = 0;
P8OUT |= BIT3;
PEOUT = 0;
PFOUT = 0;
PADIR = 0xFFFF;
PBDIR = 0xFFFF;
PCDIR = 0xFFFF;
PDDIR = 0xFFFF;
P9DIR = 0xFF;
PFDIR = 0xFFFF;
PAIE = 0;
}
//初始化系统IO口控制,
void ConfigGpio()
{
P1SEL |= BIT1;
InitGpio();
SIM_BATOFF_1;
SIM_BATOFF_2;
CHARGE_OFF;
P2DIR &= ~( BIT5 );// BIT5,BIT6 (SIM_0 SIM_1)设置为输入状态
P3DIR = BIT4+BIT5 ;//0:sd en and 6:CAMERA POW EN 7:ALTRASONIC POW EN
P5DIR = BIT6+BIT7;
P8DIR &= ~BIT2 ;//
//P3DIR = BIT0 ; //FM CS CONTROL
P4DIR &= ~BIT3 ;//comm (2010-10-13) power en
P4DIR |=BIT6;//控制红外灯;
P4OUT |=BIT6;//初始化关闭红外灯;
P1DIR = BIT3 + BIT4 + BIT5 + BIT6+ BIT7; //3: mcu3 to mcu1 require 4:初始状态0,1兼可 mcu3 to mcu1 status
P7DIR = BIT2 ; //com1 power en
// P1IES |= 0x01 ; //mcu1 to mcu2 pps input,下降沿触发中断
P10DIR |= BIT6;
P10OUT |= BIT6;
P10DIR &= ~BIT4;
P9DIR &= ~BIT3;
P6SEL |= 0XF;
P2DIR |= BIT1+BIT2+BIT3;
P2OUT &=0xF1;
//
}
///////////////////////////////////////////
/////异步串口0设置:br=1(4800) //////
/////br=2(9600);br=3(115200) //////
///////////////////////////////////////////
void uart_a0setup( UCHAR br )
{
P3SEL |= BIT5 +BIT4 ;
UCA0CTL1 |= UCSWRST ; // **Put state machine in reset**
UCA0CTL1 |= UCSSEL1 ; // SMCLK
switch( br )
{
case 1:
UCA0BRW = 104; // 8MHz 4800 (see User's Guide)
UCA0MCTL = UCBRS_0 + UCBRF_3 + UCOS16; // Modln UCBRSx=0, UCBRFx=3,
break;
case 2:
UCA0BRW = 52; // 8MHz 9600 (see User's Guide)
UCA0MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
case 3:
UCA0BRW = 69; // 8MHz 115200 (see User's Guide)
UCA0MCTL = UCBRS_4 + UCBRF_0 ; // Modln UCBRSx=4, UCBRFx=0,
break;
default:
UCA0BRW = 52; // 8MHz 9600 (see User's Guide)
UCA0MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
}
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA0IE |= UCRXIE ;
}
///////////////////////////////////////////
/////异步串口1设置:br=1(4800) //////
/////br=2(9600);br=3(115200) //////
///////////////////////////////////////////
void uart_a1setup( UCHAR br )
{
P5SEL |= BIT6 +BIT7 ;
//UCA1CTL0 |= UCMSB;
UCA1CTL1 |= UCSWRST ; // **Put state machine in reset**
UCA1CTL1 |= UCSSEL1 ; // SMCLK
switch( br )
{
case 1:
UCA1BRW = 104; // 8MHz 4800 (see User's Guide)
UCA1MCTL = UCBRS_0 + UCBRF_3 + UCOS16; // Modln UCBRSx=0, UCBRFx=3,
break;
case 2:
UCA1BRW = 52; // 8MHz 9600 (see User's Guide)
UCA1MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
case 3:
UCA1BRW = 69; // 8MHz 115200 (see User's Guide)
UCA1MCTL = UCBRS_4 + UCBRF_0 ; // Modln UCBRSx=4, UCBRFx=0,
break;
default:
UCA1BRW = 52; // 8MHz 9600 (see User's Guide)
UCA1MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
}
UCA1CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA1IE |= UCRXIE ;
}
///////////////////////////////////////////
/////异步串口2设置:br=1(4800) //////
/////br=2(9600);br=3(115200) //////
///////////////////////////////////////////
void uart_a2setup( UCHAR br )
{
P9SEL |= BIT5 +BIT4 ;
// UCA2CTL0 |= UCMSB;
UCA2CTL1 |= UCSWRST ; // **Put state machine in reset**
UCA2CTL1 |= UCSSEL1 ; // SMCLK
switch( br )
{
case 1:
UCA2BRW = 104; // 8MHz 4800 (see User's Guide)
UCA2MCTL = UCBRS_0 + UCBRF_3 + UCOS16; // Modln UCBRSx=0, UCBRFx=3,
break;
case 2:
UCA2BRW = 52; // 8MHz 9600 (see User's Guide)
UCA2MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
case 3:
UCA2BRW = 69; // 8MHz 115200 (see User's Guide)
UCA2MCTL = UCBRS_4 + UCBRF_0 ; // Modln UCBRSx=4, UCBRFx=0,
break;
case 4:
UCA2BRW = 138;
UCA2MCTL = UCBRS_7 + UCBRF_0;
break;
default:
UCA2BRW = 52; // 8MHz 9600 (see User's Guide)
UCA2MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
}
UCA2CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA2IE |= UCRXIE ; ;
}
///////////////////////////////////////////
/////异步串口3设置:br=1(4800) //////
/////br=2(9600);br=3(115200) //////
///////////////////////////////////////////
void uart_a3setup( UCHAR br )
{
P10SEL |= BIT5 +BIT4 ;
// UCA3CTL0 |= UCMSB;
UCA3CTL1 |= UCSWRST ; // **Put state machine in reset**
UCA3CTL1 |= UCSSEL1 ; // SMCLK
switch( br )
{
case 1:
UCA3BRW = 104; // 8MHz 4800 (see User's Guide)
UCA3MCTL = UCBRS_0 + UCBRF_3 + UCOS16; // Modln UCBRSx=0, UCBRFx=3,
break;
case 2:
UCA3BRW = 52; // 8MHz 9600 (see User's Guide)
UCA3MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
case 3:
UCA3BRW = 69; // 8MHz 115200 (see User's Guide)
UCA3MCTL = UCBRS_4 + UCBRF_0 ; // Modln UCBRSx=4, UCBRFx=0,
break;
case 4:
UCA3BRW = 32; // 8MHz 115200 (see User's Guide)
UCA3MCTL = UCBRS_4 + UCBRF_0 ; // Modln UCBRSx=4, UCBRFx=0,
break;
default:
UCA3BRW = 52; // 8MHz 9600 (see User's Guide)
UCA3MCTL = UCBRS_0 + UCBRF_1 + UCOS16; // Modln UCBRSx=0, UCBRFx=1,
break;
}
UCA3CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA3IE |= UCRXIE ; ;
// COM3IEON ;
}
//==============================================================================
//===========================TimerA0作为延时计数时钟设置 ===========================
//===========================中断周期10ms========================================
//===========================增模式,采用SMCLK作为输入时钟源====================
//==============================================================================
void Timera0Config()
{
TA0CTL = TASSEL1 + TACLR + TAIE + ID1 + ID0 ; //8M频率,8分频,输入时钟为1Mhz
TA0CCR0 = 10000-1 ; //定义定时器A存储器的值,使输出频率f=f_ACLK/(N*TACCR0),N为分频数=1; ////////////////
TA0CTL |= MC__UP ; //定时器开关 */ //////////////////////////////////////////////////
/////////////////////////////////////////////////////// ////////////////////////////////////////////
}
接着我们需要将这两个文件加到项目中去。
添加文件 添加完之后接着我们编译一下看看能不能成功?
捕获_副本.png 编译信息哈哈,成功了。少量的warning可以忽略,不影响软件的运行。
但这仅仅是可以下载到MCU中去,硬件配置基本完成,但是还没有实际的功能。那现在就给他加点功能吧。
由于是采集设备,那么我们先实现基本的AD采集吧。
在device.c中加入对AD寄存器的配置函数void ConfigAdc(),以及它的采样时钟源的配置void TimerbConfig()。
ad采集开始和结束的函数void AdcStart()&void AdcStop(),
串口发送数据的函数void Com2TxChar( UC a )&void txtoPC(UC* P1,UI P2)
软件使用内部看门狗复位的函数void WdtReset()
//配置AD采集模块
void ConfigAdc()
{
P6SEL = 0xff ; //设置AD采集输入管脚A0~A7
// P6SEL &= ~BIT4;
// P7SEL |= 0XF0 ; //A12~A15
P5SEL |= BIT0 + BIT1; //设置采集参考电压输入管脚
ADC12CTL0 &= ~ADC12ENC ; //关闭AD使能
ADC12CTL0 = ADC12ON + ADC12SHT0_8 +ADC12MSC ;//打开AD采集模块内核 //去掉“+ ADC12REFON ” jmj 2011.03.12
ADC12CTL1 = ADC12SSEL1 + ADC12CONSEQ_3 + ADC12CSTARTADD_0 + ADC12SHS_3 ;
//时钟MCLK,多通道连续采集,开始地址A0,TimerB触发
ADC12CTL2 = ADC12TCOFF + ADC12RES1 + ADC12SR ;//温度传感器关闭,12bit AD 低采样率<50k
ADC12MCTL0 = ADC12SREF_0 + ADC12INCH_0 ; //外部电压参考,A0通道
ADC12MCTL1 = ADC12SREF_0 + ADC12INCH_1 ; //外部电压参考,A1通道
ADC12MCTL2 = ADC12SREF_0 + ADC12INCH_2 ; //外部电压参考,A2通道
ADC12MCTL3 = ADC12SREF_0 + ADC12INCH_3 ; //外部电压参考,A3通道
ADC12MCTL4 = ADC12SREF_0 + ADC12INCH_4; //外部电压参考,A4通道[采集温度传感器的值]
ADC12MCTL5 = ADC12SREF_0 + ADC12INCH_5 ; //外部电压参考,A5通道
ADC12MCTL6 = ADC12SREF_0 + ADC12INCH_6 ; //外部电压参考,A6通道
ADC12MCTL7 = ADC12SREF_0 + ADC12INCH_7 + ADC12EOS ; //外部电压参考,A7通道,时序采样结束
ADC12IE = 0X0080 ; //打开通道7的中断
ADC12CTL0 |= ADC12ENC ; //打开AD使能
// ADC12CTL0 |= ADC12SC ; //SHP = 0,ADC12SC保持高电平
}
//==========timerb被设置为AD采集采样率==========================================
//==========freq,设定的采样率的值。============================================
//==========增模式,不使能中断,采用SMCLK作为时钟源=============================
//==============================================================================
void TimerbConfig()
{
TBCCTL0 = 0;
TBCCTL1 = 0;
TBCTL = TBSSEL1 + TBCLR ;//8MHz jmj 2011.03.30 //+ ID0 + ID1;//8Mhz,8分频 1MHz;2010-10-14
// +ID0 + TBIE //////////////////// 1分频,4M
/////定时器TB1为比较器为采集触发频率//////////////
TBCCTL1 |= OUTMOD_3 ;// ; +CCIE/* //TimerB1 比较器设置///////////////////*/
TBCCR1 = 555;
TBCCR0 = 999 ;
TBCTL |= MC__UP ; //启动TimerB,增计数模式,由于提供采样率,
//不使能中断
//////////////////////////////////////////////////////////////////
}
//
void AdcStart()
{
UI i;
for(i=0;i<SendPeriod;i++)
{
Data[i]=0L;
}
//清空ADC的计数器
TimerbConfig();
ConfigAdc() ; //SHP = 0,ADC12SC保持高电平
ADC12CTL0 |= ADC12SC ; //SHP = 0,ADC12SC保持高电平
AdcWorking = 1;
}
void AdcStop()
{
//打开通道7的中断
int temp;
TBCTL = 0 ;
TBCCTL0 = 0;
TBCCTL1 = 0;
TBCCTL2 = 0;
TBCCTL3 = 0;
TBCCTL4 = 0;
TBCCTL5 = 0;
TBCCTL6 = 0;
TB0R = 0;
TBCCR0 = 0;
TBCCR1 = 0;
TBCCR2 = 0;
TBCCR3 = 0;
TBCCR4 = 0;
TBCCR5 = 0;
TBCCR6 = 0;
//开启TimerB定时器
ADC12CTL0 = 0 ; //打开AD使能
ADC12CTL1 = 0 ;
ADC12CTL2 = 0;
ADC12IV = 0;
ADC12MCTL0 = 0;
ADC12MCTL1 = 0;
ADC12MCTL2 = 0;
ADC12MCTL3 = 0;
ADC12MCTL4 = 0;
ADC12MCTL5 = 0;
ADC12MCTL6 = 0;
ADC12MCTL7 = 0;
//SHP = 0,ADC12SC保持高电平
// ADC12CTL0 |= ADC12ENC ; //打开AD使能
// ADC12CTL0 |= ADC12SC ;
ADC12IFG = 0;
ADC12IE = 0;
temp = ADC12MEM0;
temp = ADC12MEM1;
temp = ADC12MEM2;
temp = ADC12MEM3;
temp = ADC12MEM4;
temp = ADC12MEM5;
temp = ADC12MEM6;
temp = ADC12MEM7;
AdcWorking = 0;
txtoPC("ADC is Stop Work!\r\n",
SIZEOF_STRING("ADC is Stop Work!\r\n"));
}
void memcpy1(UC* P1 ,UC* P2,UI n)
{
UI i=0;
for(i=0;i<n;i++)
{
P1[i]=P2[i];
WDT_CLR;
WDT1_CLR;
}
}
///////////////////////////////////////////////////
///////串口2发送程序///////////////////////////////
///////////////////////////////////////////////////
void Com2TxChar( UC a )
{
while (!(UCA2IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
UCA2TXBUF = a;
//UCA3TXBUF = a;
while (!(UCA2IFG&UCTXIFG));
}
void txtoPC(UC* P1,UI P2)
{
UI i;
for(i=0;i<P2;i++)
Com2TxChar(P1[i]);
}
void WdtReset()
{
//使能看门狗
// WDTCTL = WDTPW+WDTSSEL_3;
//禁止中断
WDTCTL = 0;
_DINT();
//循环等待
while(1);
}
增加中断函数文件 isr.c,内容如下,主要实现了ADC中断 AdcIsr(void)和定时器中断ten_ms_TimerIsr(void)。
#include "include.h"
#include "string.h"
#pragma vector = ADC12_VECTOR
__interrupt void AdcIsr(void)
{
int temp, i;
ADC12IFG = 0;
static UI temp_volt0=0;
static UI temp_volt1= 0;
UL temp_df=0L;
if(SendDataCnt < SendPeriod)
{
if((AdcCnt++) < (1000*60))
{
Data[SendDataCnt]+= abs(ADC12MEM0-2048);
}
else
{
AdcCnt = 0;
temp_volt0 += ADC12MEM1;
temp_volt1 += ADC12MEM2;
DJ_state=1;
SendDataCnt++;
}
}
else
{
BatVolt = temp_volt0/SendPeriod;
ChargeVolt = temp_volt1/SendPeriod;
temp_volt0=0;
temp_volt1=0;
SendDataCnt = 0;
NeedSendData = 1;
//GprsLinkDelay = MAX_LINK_DELAY;
// AdcStop();
LPM0_EXIT;
}
}
#pragma vector = TIMER0_A1_VECTOR
__interrupt void ten_ms_TimerIsr(void)
{
static UC msCounter=0;
static UC ledcounter = 5;
UC temp4[4];
TA0CTL &= ~TAIFG;
WDT_CLR;
WDT1_CLR;
//----------------------------
if(msCounter<100)
{
msCounter++;
}
else
{
msCounter=0;
if(ledcounter){
ledcounter--;
}
secondCounter++;
if(secondCounter == 0x3840)
{
secondCounter = 0;
WdtReset();//循环,等待看门狗复位
while(1);
}
}
}
主函数main.c中增加了开始采集,以及采集两分钟后,通过串口通知上位机已经采集完一个周期的数。
SendPeriod = 1;
memset((UC*)Data,0,480);//初始化数据
AdcStart();
while(1)
{
_BIS_SR(LPM0_bits + GIE); //进入低功耗模式
if(NeedSendData)
{
NeedSendData = 0;
memcpy1((UC*)((UI)Data+12*SendPeriod),(UC*)((UI)Data+8*SendPeriod),4*SendPeriod);
memcpy1((UC*)((UI)Data+8*SendPeriod),(UC*)((UI)Data+4*SendPeriod),4*SendPeriod);
memcpy1((UC*)((UI)Data+4*SendPeriod),(UC*)Data,4*SendPeriod);
txtoPC("Get one period data!\r\n",
SIZEOF_STRING("Get one period data!\r\n"));
}
}
保存后编译一下,没有错误的话直接下载到MCU中去。实际运行一下,看看能不能正常工作。
第三步下载运行
下载之前需要注意一下仿真器是否连接正常,正常后再下载
判断仿真器是否连接正常 下载 下载完成后如果程序功能正常的话,按下运行键MCU会采数并且一分钟后在上位机上显示一段采数完毕的提示。
如果出现bug就要根据出错信息进行排查。
总结
当然,这只是抛砖引玉,想让大家了解一个430项目是如何完成。
重要的是要明确项目成功的定义,设立短期目标,然后在预期时间的2/3时间内完成,剩下的时间可以进行测试定稿。
单片机程序的实现方式基本相同,可能使用的寄存器定义不同,只需要看懂datasheet的相关部分,再将软件架构和逻辑想明白,基本部分就可以完成了,后期再通过测试和完善功能,接近完美的产品就产生了,当然这也是需要时间检验的。