emmm怎么说呢?这个代码可以说不是很好,在网上拼凑出来的代码。 我先把这个设计的要求说一下:设置一个按键,要求当按键按下时,单片机能够对按下时间进行计时,同时将该计时时间发送到1602液晶上同时通过RS232发送到上位机显示。 我们完成后得到的结果是这样的:首先,有两个按钮来控制单片机,第一个是按下开始和暂停,第二个是长按下零。然后它就可以通过了RS232上传到上位机,当然,你应该选择一个接收工具来接收这个网络,或者学校的机房。 然而,我们有一个缺陷,那就是当我们接受数据时,我们从头开始。哈,你按下数字,现在数字已经开始离开,然后一直上传到上位机数据。然后你按下它,暂停它。上位机没有传输数据,然后按下它,开始在上位机中有数据。(非常着迷)。 首先,我先把代码放在上面
#include <msp430x14x.h> #include "Config.h" int second = 0, minute = 0, count = 0, flag = 0, a = 0, b = 0, c = 0; unsigned char FlagLcd; void Init(void);//声明初始化函数 char Data[20]; //发送的字符串 void Init(void) { UCTL0 &= ~SWRST; //USART控制寄存器UCTL0,SWRST=0x01,~SWRST=0xFE,将 //UCTL0寄存器第0位复位后,USART才能再次被允许 UCTL0 =0X10; //UCTL设置数据长度为8位,第5位为0,设置一个停止位 UBR00 = 0x03; //使用32768Hz波特率为96000 UBR10 = 0x00; UMCTL0 = 0x4A; UTCTL0 = 0X10; 发送控制寄存器,第四位1,选择辅助时钟ACLK1 ME1 |= UTXE0 ; ///块允许寄存器ME1,UTXE0=0x80,设置ME1的第7位为1, //使USART允许模式发送 P3SEL|=BIT4; //P三口选择寄存器的第四位1,选择外围模块 P3DIR|=BIT4; //P三口寄存器的第四位1,选择输出 } //************************************************************************* // 初始化IO口子程序 //************************************************************************* void Port_init() { P4SEL = 0x00; P4DIR = 0xFF; ///数据口输出模式 P5SEL = 0x00; P5DIR|= BIT5 BIT6 BIT7; ///控制口设置为输出模式 P1SEL = 0x00; //P1普通IO功能 P1DIR = 0xF0; //P1.0~P1.3输入模式,外部电路已接上拉电阻 P1IE = 0x0f; //开启 位中断 P1IES = 0x00; //上升沿触发中断 P1IFG = 0x00; ///软件清零中断标志寄存器 } //*********************************************************************** // 显示命令写入函数 //*********************************************************************** void LCD_write_com(unsigned char com) { RS_CLR; RW_CLR; EN_SET; DataPort = com; ///命令写入端口 delay_ms(5); EN_CLR; } //*********************************************************************** // 函数中写入显示数据 //*********************************************************************** void LCD_write_data(unsigned char data) { RS_SET; RW_CLR; EN_SET; DataPort = data; ///数据写入端口 delay_ms(5); EN_CLR; } //*********************************************************************** // 清空显示屏 //*********************************************************************** void LCD_clear(void) { LCD_write_com(0x01); //清屏显示 delay_ms(5); } //*********************************************************************** // 显示屏字符串写入函数 //*********************************************************************** void LCD_write_str(unsigned char x,unsigned char y,int w) { if (y == 0) { LCD_write_com(0x80 x); ///显示第一行 } else { LCD_write_com(0xC0 x); ///显示第二行 } LCD_write_data(48 w); } //*********************************************************************** // 显示屏单字符写入函数 //*********************************************************************** void LCD_write_char(unsigned char x,unsigned char y,unsigned char data) { if (y == 0) { LCD_write_com(0x80 x); ///显示第一行 } else { LCD_write_com(0xC0 x); ///显示第二行 } LCD_write_data( data); } //*********************************************************************** // 显示初始化函数 //*********************************************************************** void LCD_init(void) { LCD_write_com(0x38); ///显示模式设置 delay_ms(5); LCD_write_com(0x08); //显示关闭 delay_ms(5); LCD_write_com(0x01); //显示清屏 delay_ms(5); LCD_write_com(0x06); ///显示光标移动设置 delay_ms(5); LCD_write_com(0x0C); ///显示开和光标设置 delay_ms(5); } //*********************************************************************** // TIMERA初始化,设置为UP模式计数 //*********************************************************************** void TIMERA_Init(void) //UP模式计数,计数周期为CCR0 1 { TACTL |= TASSEL1 TACLR ID0 ID1 MC0 TAIE; //SMCLK制作时钟源,8分频,增加计数模式,开中断 TACCR0 = 9999; //CCR0=9999,10ms中断一次 } //*********************************************************************** // 关闭计时,暂停计数 //*********************************************************************** void TimerA_end(void) { TACTL &= 0xfffd; } //********************************************************************** // 扫描按键P1^2是否长按 //********************************************************************** void GetKey()//长按,返回2;短按,返回1。 { unsigned char keyRetu=0; //返回按键值 staticunsigned char s_keyState=0,keyTime=0; //按键状态,按键按下的时间
switch (s_keyState)
{
case 0:
{
if((P1IN&0x02)==0x00) //检测到有按键,转到状态1,相当于是消抖过程。
{
s_keyState=1;
}
}
break;
case 1:
{
if((P1IN&0x02)==0x00) //再次检测到有按键,转到状态2
{
s_keyState=2;
keyTime=0; //清零按键时间计数器
}
else
{
s_keyState=0; //没有检测到按键,说明状态0检测到是一个抖动,重新转到状态0
}
}
break;
case 2:
{
if((P1IN&0x02)==0x02) //检测到按键松开
{
s_keyState=0; //状态转到状态0
keyRetu=1; //输出1
}
else
{
if(++keyTime>=150) //按下时间>1s
{
s_keyState=3; //转到状态3
keyTime=0; //清零按键时间计数器
keyRetu=2; // 输出2
}
}
}
break;
case 3:
{
if((P1IN&0x02)==0x02) //检测到按键松开
{
s_keyState=0; //状态转到状态0
}
else
{
s_keyState=3; //转到状态3
}
}
break;
}
if(keyRetu==2)
{
a = 0;
b = 0;
c = 0;
count = 0;
second = 0;
minute = 0;
}
}
//***********************************************************************
// TIMERA中断服务程序,需要判断中断类型
//***********************************************************************
#pragma vector = TIMERA1_VECTOR
__interrupt void Timer_A(void)
{
switch(TAIV) //需要判断中断的类型
{
case 2:break;
case 4:break;
case 10:count++;break; //设置标志位Flag
}
if(count==100) //100次为1秒
{
second++;
count=0;
}
if(second == 60)
{
minute++;
second = 0;
}
GetKey();
}
//**********************************************************************
// P1口中断服务程序,需要判断
//**********************************************************************
#pragma vector = PORT1_VECTOR
__interrupt void P1_IRQ(void)
{
switch(P1IFG&0x0F)
{
case 0x01: {
unsigned int i;
Data[0]=c/10+48;
Data[1]=c%10+48;
Data[2]=0x3a;
Data[3]=b/10+48;
Data[4]=b%10+48;
Data[5]=0x3a;
Data[6]=a/10+48;
Data[7]=a%10+48;
Data[8]=10;
for(i=0;i<=8;i++)
{
TXBUF0=Data[i]; //向缓冲器送入待发送数据
while((UTCTL0&0x01)==0); //发送缓冲器有待发数据时,UTCTL0的第0位复位,进入等待
}
flag++;
P1IFG=0x00;
break;
}
default:
P1IFG = 0x00;
break;
}
}
//***********************************************************************
// 主程序
//***********************************************************************
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
Init(); //调用初始化函数
WDT_Init(); //看门狗设置
Clock_Init(); //系统时钟设置
Port_init(); //系统初始化,设置IO口属性
delay_ms(100); //延时100ms
LCD_init(); //液晶参数初始化设置
LCD_clear(); //清屏
TIMERA_Init();
_EINT();
while (1)
{
//暂停或重置时进入此分支
if(flag%2==0)
{
LCD_write_str(0,1,c/10);
LCD_write_str(1,1,c%10);
LCD_write_char(2,1,0x3a);
LCD_write_str(3,1,b/10);
LCD_write_str(4,1,b%10);
LCD_write_char(5,1,0x3a);
LCD_write_str(6,1,a/10);
LCD_write_str(7,1,a%10);
LCD_write_str(0,0,c/10);
LCD_write_str(1,0,c%10);
LCD_write_char(2,0,0x3a);
LCD_write_str(3,0,b/10);
LCD_write_str(4,0,b%10);
LCD_write_char(5,0,0x3a);
LCD_write_str(6,0,a/10);
LCD_write_str(7,0,a%10);
}
//计时过程中进入此分支
else
{
count = a;
second = b;
minute = c;
LCD_write_str(0,1,minute/10);
LCD_write_str(1,1,minute%10);
LCD_write_char(2,1,0x3a);
LCD_write_str(3,1,second/10);
LCD_write_str(4,1,second%10);
LCD_write_char(5,1,0x3a);
LCD_write_str(6,1,count/10);
LCD_write_str(7,1,count%10);
a = count;
b = second;
c = minute;
}
}
}
说一下在这里面引入了两个头文件首先是第一个#include <msp430x14x.h>这个文件是msp430系列的单片机自带的头文件,学校里系统教过单片机的如果是用IAR的话应该是知道这个头文件的,这里面的内容有点多就不一一叙述了。 其次就是#include "Config.h"这个头文件了下面我把这个头文件放在下面
//延时函数,IAR自带,经常使用到
#define CPU_F ((double)8000000) //外部高频晶振8MHZ
//#define CPU_F ((double)32768) //外部低频晶振32.768KHZ
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
//自定义数据结构,方便使用
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
//8个LED灯,连接在P6口,可通过断开电源停止使用,ADC使用时断开电源
#define LED8DIR P6DIR
#define LED8 P6OUT //P6口接LED灯,8个
//4个独立按键连接在P10~P13
#define KeyPort P1IN //独立键盘接在P10~P13
//串口波特率计算,当BRCLK=CPU_F时用下面的公式可以计算,否则要根据设置加入分频系数
#define baud 9600 //设置波特率的大小
#define baud_setting (uint)((ulong)CPU_F/((ulong)baud)) //波特率计算公式
#define baud_h (uchar)(baud_setting>>8) //提取高位
#define baud_l (uchar)(baud_setting) //低位
//RS485控制管脚,CTR用于控制RS485处于收或者发状态
#define RS485_CTR1 P5OUT |= BIT2; //控制线置高,RS485发送状态
#define RS485_CTR0 P5OUT &= ~BIT2; //控制线置低,RS485接收状态
//2.8寸TFT彩屏显示控制相关硬件配置
#define RS_CLR P5OUT &= ~BIT5 //RS置低
#define RS_SET P5OUT |= BIT5 //RS置高
#define RW_CLR P5OUT &= ~BIT6 //RW置低
#define RW_SET P5OUT |= BIT6 //RW置高
#define RD_CLR P5OUT &= ~BIT7 //E置低
#define RD_SET P5OUT |= BIT7 //E置高
#define CS_CLR P5OUT &= ~BIT0 //CS置低
#define CS_SET P5OUT |= BIT0 //CS置高
#define RST_CLR P5OUT &= ~BIT3 //RST置低
#define RST_SET P5OUT |= BIT3 //RST置高
#define LE_CLR P5OUT &= ~BIT1 //LE置低
#define LE_SET P5OUT |= BIT1 //LE置高
//2.8寸TFT彩屏触摸屏控制相关硬件配置
#define PEN_CLR P2OUT &= ~BIT0 //PEN置低,触碰触摸屏时,Penirq引脚由未触摸时的高电平变为低电平
#define PEN_SET P2OUT |= BIT0 //PEN置高
#define PEN (P2IN & 0x01) //P2.0输入的值
#define TPDO_CLR P2OUT &= ~BIT1 //TPDO置低
#define TPDO_SET P2OUT |= BIT1 //TPDO置高
#define TPDOUT ((P2IN>>1)&0x01) //P2.1输入的值
#define BUSY_CLR P2OUT &= ~BIT3 //BUSY置低
#define BUSY_SET P2OUT |= BIT3 //BUSY置高
#define TPDI_CLR P2OUT &= ~BIT4 //TPDI置低
#define TPDI_SET P2OUT |= BIT4 //TPDI置高
#define TPCS_CLR P2OUT &= ~BIT5 //TPCS置低
#define TPCS_SET P2OUT |= BIT5 //TPCS置高
#define TPCLK_CLR P2OUT &= ~BIT6 //TPCLK置低
#define TPCLK_SET P2OUT |= BIT6 //TPCLK置高
//彩屏/12864液晶/1602液晶的数据口,三液晶共用
#define DataDIR P4DIR //数据口方向
#define DataPort P4OUT //P4口为数据口
//12864/1602液晶控制管脚
#define RS_CLR P5OUT &= ~BIT5 //RS置低
#define RS_SET P5OUT |= BIT5 //RS置高
#define RW_CLR P5OUT &= ~BIT6 //RW置低
#define RW_SET P5OUT |= BIT6 //RW置高
#define EN_CLR P5OUT &= ~BIT7 //E置低
#define EN_SET P5OUT |= BIT7 //E置高
#define PSB_CLR P5OUT &= ~BIT0 //PSB置低,串口方式
#define PSB_SET P5OUT |= BIT0 //PSB置高,并口方式
#define RESET_CLR P5OUT &= ~BIT1 //RST置低
#define RESET_SET P5OUT |= BIT1 //RST置高
//12864应用指令集
#define CLEAR_SCREEN 0x01 //清屏指令:清屏且AC值为00H
#define AC_INIT 0x02 //将AC设置为00H。且游标移到原点位置
#define CURSE_ADD 0x06 //设定游标移到方向及图像整体移动方向(默认游标右移,图像整体不动)
#define FUN_MODE 0x30 //工作模式:8位基本指令集
#define DISPLAY_ON 0x0c //显示开,显示游标,且游标位置反白
#define DISPLAY_OFF 0x08 //显示关
#define CURSE_DIR 0x14 //游标向右移动:AC=AC+1
#define SET_CG_AC 0x40 //设置AC,范围为:00H~3FH
#define SET_DD_AC 0x80 //设置DDRAM AC
#define FUN_MODEK 0x36 //工作模式:8位扩展指令集
//颜色代码,TFT显示用
#define White 0xFFFF //显示颜色代码
#define Black 0x0000
#define Blue 0x001F
#define Blue2 0x051F
#define Red 0xF800
#define Magenta 0xF81F
#define Green 0x07E0
#define Cyan 0x7FFF
#define Yellow 0xFFE0
//NRF2401模块控制线
#define RF24L01_CE_0 P1OUT &=~BIT5 //CE在P15
#define RF24L01_CE_1 P1OUT |= BIT5
#define RF24L01_CSN_0 P2OUT &=~BIT7 //CS在P27
#define RF24L01_CSN_1 P2OUT |= BIT7
#define RF24L01_SCK_0 P3OUT &=~BIT3 //SCK在P33
#define RF24L01_SCK_1 P3OUT |= BIT3
#define RF24L01_MISO_0 P3OUT &=~BIT2 //MISO在P32
#define RF24L01_MISO_1 P3OUT |= BIT2
#define RF24L01_MOSI_0 P3OUT &=~BIT1 //MOSI在P31
#define RF24L01_MOSI_1 P3OUT |= BIT1
#define RF24L01_IRQ_0 P1OUT &=~BIT4 //IRQ在P14
#define RF24L01_IRQ_1 P1OUT |= BIT4
//DS18B20控制脚,单脚控制
#define DQ_IN P1DIR &= ~BIT7 //设置输入,DS18B20接单片机P53口
#define DQ_OUT P1DIR |= BIT7 //设置输出
#define DQ_CLR P1OUT &= ~BIT7 //置低电平
#define DQ_SET P1OUT |= BIT7 //置高电平
#define DQ_R P1IN & BIT7 //读电平
//红外接收头H1838控制脚,单脚控制
#define RED_IN P1DIR &= ~BIT6 //设置输入,红外接收头接单片机PE3口
#define RED_OUT P1DIR |= BIT6 //设置输出
#define RED_L P1OUT &= ~BIT6 //置低电平
#define RED_H P1OUT |= BIT6 //置高电平
#define RED_R (P1IN & BIT6) //读电平
//***********************************************************************
// 系统时钟初始化,外部8M晶振
//***********************************************************************
void Clock_Init()
{
uchar i;
BCSCTL1&=~XT2OFF; //打开XT2振荡器
BCSCTL2|=SELM1+SELS; //MCLK为8MHZ,SMCLK为8MHZ
do{
IFG1&=~OFIFG; //清楚振荡器错误标志
for(i=0;i<100;i++)
_NOP();
}
while((IFG1&OFIFG)!=0); //如果标志位1,则继续循环等待
IFG1&=~OFIFG;
}
//***********************************************************************
// 系统时钟初始化,内部RC晶振
//***********************************************************************
void Clock_Init_Inc()
{
// DCOCTL = DCO0 + DCO1 + DCO2; // Max DCO
// BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // XT2on, max RSEL
DCOCTL = 0x60 + 0x00; //DCO约3MHZ,3030KHZ
BCSCTL1 = DIVA_0 + 0x07;
BCSCTL2 = SELM_2 + DIVM_0 + SELS + DIVS_0;
}
//***********************************************************************
// 系统时钟初始化,外部32.768K晶振
//***********************************************************************
void Clock_Init_Ex32768()
{
uchar i;
BCSCTL2|=SELM1 + SELM0 + SELS; //MCLK为32.768KHZ,SMCLK为8MHZ
do{
IFG1&=~OFIFG; //清楚振荡器错误标志
for(i=0;i<100;i++)
_NOP();
}
while((IFG1&OFIFG)!=0); //如果标志位1,则继续循环等待
IFG1&=~OFIFG;
}
//***********************************************************************
// MSP430内部看门狗初始化
//***********************************************************************
void WDT_Init()
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗
}
就这样单片的实训的代码就全都放在下面了但是这个代码是有问题的,也是我们没有解决的问题,其中有一个非常大的问题,就是上面说的上传到上位机的问题。