1、第一章 MSP430单片机入门基础,前沿,1、MSP430系列单片机系统工程设计与实践这本书是我见过的最好的关于单片机的书,非常值得逐字逐句的彻底学习一遍。 2、与51不同,MSP430单片机是RISC处理器,通过对比两者的区别,可以建立起代码移植的思想。 3、MSP430单片机属于混合信号处理器,资源极其丰富,有利于全面学习硬件知识。,青岛大学-TI 大学生创新中心 傅强,2,1.1 初识MSP430单片机,MSP430:低功耗之王,水果电池驱动 1、为什么我们在乎功耗? 凡是以电池为电源的电子设备都看重低功耗。 几乎所有的单片机会标榜自己有低功耗方面的设计。 2、MSP430为什么可以成为
2、低功耗之王? 在硬件上,cpu和不用的模块可以休眠 在软件上,必须设计恰当的休眠和唤醒 430是实现低功耗的必要条件,非充分条件。,青岛大学-TI 大学生创新中心 傅强,3,1.1.1 MSP430单片机的应用前景,便携设备,高集成度、小型化。 野外安置的终身电池设备。 无需额外供电的自供电设备。 高精度测量、控制领域。 当普通单片机用。,青岛大学-TI 大学生创新中心 傅强,4,1.1.2 MSP430单片机的特点,多时钟系统 CPU时钟、功能模块时钟、休眠唤醒时钟 软件设置时钟 16位RISC处理器,单指令周期 模块化结构 各模块完全独立,不需要cpu干涉 避免了cpu与外部模块复杂的数据
3、通信 学会最贵的430,等于会用了全系列430,青岛大学-TI 大学生创新中心 傅强,5,采用冯诺依曼结构(普林斯顿结构),程序指令存储器和数据存储器统一编址。 举例:实现存储器中两数据相乘,要经过3个步骤,通过总线取两个数据和取出指令(干什么)到CPU。 如果是哈佛结构,数据总线和指令总线分开。 430可以在ram里跑程序,加上具有flash控制器,可实现固件更新。 rom中的升级代码-复制到ram运行-擦除rom-升级rom,1.1.2 MSP430单片机的特点,青岛大学-TI 大学生创新中心 傅强,6,科普:ROM、RAM、DRAM、SRAM、EEPROM、FLASH,我们现在用的51,
4、都是flash rom的,但是内部没有flash控制器,所以需要EEPROM存实时数据。 Msp430带flash控制器,单片机自己就能擦写flash,所以不用EEPROM。,青岛大学-TI 大学生创新中心 傅强,7,MSP430FE425A资源 8M/s处理速度 512RAM(数据)+16KB Flash(程序代码) 内置Flash控制器,剩余Flash可存数据。 内置时钟管理单元,可内部倍频 3路同步采样、差分输入、32倍程控增益放大器的16位ADC 温度传感器 1.2V基准源和输出缓冲器 128段LCD驱动器 增强UART串口 看门狗 BasicTimer定时器 16位TimeA定时器,
5、3路捕获和2路PWM 内置BOR复位电路 16个双向可中断IO口 内置电能计量模块 后缀带A的,比如FE425A带硬件乘法器,青岛大学-TI 大学生创新中心 傅强,8,1.1.3 MSP430单片机最小系统,一般单片机最小系统需要什么? 电源、晶振、复位、下载/仿真接口 MSP430FE425自带片内数控时钟(DCO),掉电复位电路(BOR) 一般讲究一点都需要“电源监视芯片” 内部时钟一般不如晶振精确 可以外接手表晶振32.768k,然后倍频 JTAG、SBW、可下载+调试,BSL只能下载,青岛大学-TI 大学生创新中心 傅强,9,1.2 MSP430单片机开发软件入门,一般用IAR430软
6、件 工程管理、程序编辑、代码编译下载、仿真调试 工程管理:管理外部函数、头文件。 程序编辑:写代码 编译:替程序员处理所有打杂的事情C-汇编 调试:真正的考验水平的地方,不是所有单片机都支持调试,这是要硬件支持的。,青岛大学-TI 大学生创新中心 傅强,10,全速执行、单步执行。 执行到光标处 设置断点(在程序中加一个空操作,然后用来设断点) 查看变量寄存器 查看调用函数关系,青岛大学-TI 大学生创新中心 傅强,11,1.3 MSP430单片机C语言基础,在写C语言的过程中,尽量消除不同CPU的差异,或者将差异集中到一个地方做修改,那么就能方便的实现代码移植。 我们现在写C程序,就必须按此要
7、求严格要求自己。 这样才能一通百通,才能减少重复劳动。,青岛大学-TI 大学生创新中心 傅强,12,1.3.1 变量,我们为什么要定义各种变量? 定义短字节变量有哪些好处和坏处? 定义长字节变量有哪些好处和坏处? 一些特殊的关键字 const unsigned char Table7=1,2,3,4,5,6,7 static int a;/本地全局变量 volatile int b;/不被优化 _no_init int c; /不对其初始化 为什么要慎重使用全局变量?,青岛大学-TI 大学生创新中心 傅强,13,1.3.2 数学运算,尽可能避免浮点数运算。 运算慢、非常慢。占用RAM多。 所以
8、应尽量避免使用浮点数float。 防止定点数溢出。 long int x; int a; x=a*1000;/a和1000都是int型,a65溢出 应改为 x=a*long(1000) 或 x=(long)a*1000,青岛大学-TI 大学生创新中心 傅强,14,小数的处理 例如,温度的最后计算公式为: Deg_C=ADC*1.32/1.25-273 为避免浮点数可改为: Deg_C=(long)ADC*132/125-273 如需保留1位小数,则: Deg_C=(long)ADC*1320/125-2730 /扩大10倍 程序中加上明确注释 在显示时,将小数点移位即可 减小乘除法 取平均时,
9、尽量取2、4、8等2次幂,这样可以用移位代替乘除(可编译器自动) 后缀带A的型号有硬件乘法器(自动使用),青岛大学-TI 大学生创新中心 傅强,15,1.3.3 位操作,精简指令处理器如何写IO口? P2OUT = P2OUT | 0x01; /P2.0置高 按位或 P2OUT |=0x01; /一般均简写成这样 P2OUT ,青岛大学-TI 大学生创新中心 傅强,16,1.3.4 寄存器操作,如何理解寄存器操作? 如果设计模拟电路算是天才干的事情,那么操作单片机的寄存器就算是傻瓜干的事情。 处理器把能干的事全干了,需要人过问的事情,通过一系列开关让人来选择,所以这是傻瓜就能干的事情 越是功能
10、强大的处理器,需要配置的寄存器越多。 处理器说明书就是用来查寄存器功能的,青岛大学-TI 大学生创新中心 傅强,17,宏定义帮助我们理解抽象的数字,查说明书,找到控制串口收发的是IE1寄存器的最高两位,我们可以用下面的赋值。 IE1 |= BIT6 /开串口收中断 IE1 |= BIT7 /开串口发中断 为便于记忆和理解,头文件中有如下宏定义: #define URXIE0 (0x40)/在MSP430x42x.h #define UTXIE0 (0x80)/头文件中已有 IE1 |=URXIE0+UTXIE0 以后我们接触高级处理器的程序中,大部分都是这么写,不会像51里面直接写TMOD=0
11、x20这样,青岛大学-TI 大学生创新中心 傅强,18,特别注意:使用“|=”赋值不会影响其他位,但要搞清楚是不是要先对标志位清0。 例如:PWM控制器输出模式有3个控制位,可以表示8种模式。头文件中定义了OUTMODE_0OUTMODE_7宏定义,000-111。TACCTL1 |= OUTMODE_3;/011 . TACCTL1 |= OUTMODE_6;/110 实际效果是111,也就是模式7,青岛大学-TI 大学生创新中心 傅强,19,1.3.5 中断,中断的作用是快速响应事件 430中几乎所有“资源”都带中断,为的是休眠cpu后,唤醒CPU。 Cpu发送指令给模块,然后休眠。模块执
12、行完毕后,中断唤醒CPU。 中断向量表位于ROM最高段0xFE000xFFFF(512B) 特别注意!430的中断没有中断嵌套的优先级 #pragma vector=BASICTIMER_VECTOR _interrupt void BT_ISR(void).,青岛大学-TI 大学生创新中心 傅强,20,Msp430x42x头文件中的中断向量表 #define BASICTIMER_VECTOR (0*2u) /*0xFFE0 基础定时器) PORT2_VECTOR (1 * 2u) /* 0xFFE2 P2 */ PORT1_VECTOR (4 * 2u) /* 0xFFE8 P1 */ T
13、IMERA1_VECTOR (5 * 2u) /* 0xFFEA Timer A CCR1/2*/ TIMERA0_VECTOR (6 * 2u) /* 0xFFEC Timer A CCR0 */ USART0TX_VECTOR (8 * 2u) /* 0xFFF0 串口发送 */ USART0RX_VECTOR (9 * 2u) /* 0xFFF2 串口接收*/ WDT_VECTOR (10 * 2u) /* 0xFFF4 Watchdog Timer */ SD16_VECTOR (12 * 2u) /* 0xFFF8 16位ADC */ NMI_VECTOR (14 * 2u) /*
14、0xFFFC Non-maskable */ RESET_VECTOR (15 * 2u) /* 0xFFFE Reset */,青岛大学-TI 大学生创新中心 傅强,21,中断的具体过程,事先将中断服务程序入口地址装入中断向量表。 中断发生后,如果中断被允许(可屏蔽中断),CPU将当前程序地址和CPU状态寄存器SR压入堆栈。 跳转到中断服务程序入口,备份寄存器入堆栈。 开始执行中断服务程序。 退出中断前,恢复寄存器。CPU取回SR寄存器,跳转回中断前主程序地址。,青岛大学-TI 大学生创新中心 傅强,22,退出中断时唤醒CPU,进中断前CPU休眠,那么退出中断后CPU仍然休眠。 可以在中断子
15、程序中修改堆栈中的SR,使得中断结束后,CPU不休眠。 #pragma vector=BASICTIMER_VECTOR _interrupt void BT_ISR(void) ._low_power_mode_off_on_exit();/此函数经汇编优化 ,青岛大学-TI 大学生创新中心 傅强,23,中断标志位,同类中断合并成一个总的中断。 由软件判断中断标志位flag来确定具体中断。 什么是标志位?不急用、待查询。 #pragma vector=PORT1_VECTOR _interrupt void P1_ISR(void) if(P1IFG ,青岛大学-TI 大学生创新中心 傅强,
16、24,1.3.6 内部函数,头文件instrinsic.h和in430.h _low_power_mode_0(); 或LPM0 _low_power_mode_off_on_exit(); _delay_cycles(long int cycles); _enable_interrupt(); 或_EINT(); _diaable_interrupt();或_DINT; _no_operation();或_NOP(); _swap_bytes(x); _bcd_add_short(unsigned int,unsigned int),青岛大学-TI 大学生创新中心 傅强,25,1.3.7 库
17、函数,IAR EW430提供100个库函数 Ctype.h 字符处理类 Math.h 数学类 Stdio.h 输入和输出类 Stdlib.h 通用子程序类 String.h 字符串处理类库函数是C语言通用的,内部函数与特定处理器有关。,青岛大学-TI 大学生创新中心 傅强,26,1.4 文件管理,将大程序划分为若干小的C文件,最常用的划分方法是按功能模块划分(对象)。,/* DataProcess.c */ int Sum(int a,int b,int c) int y;y=a+b+c;return(y); float Average(int a,int b,int c) float y;y
18、=a+b+c;return(y/3); ,/* DataProcess.h */ Extern int Sum (int a,int b,int c); Extern float Average(int a,int b.intc);,/* main.c */ #include “DataProcess.h” . void main() . Tem=Average(tem1,tem2,tem3); Weight=Sum(W1,W2,W3);. ,青岛大学-TI 大学生创新中心 傅强,27,全局变量的处理,隶属关系模糊的全局变量单独建global.c隶属于某模块的,写在模块c文件中,/* glob
19、al.c */ int BattVoltage; unsigned int SystemStatus; int Temperature;,/* global.h */ Extern int BattVoltage; Extern unsigned int SystemStatus; Extern int Temperature;,/* DataProcess.c */ Unsigned char OverflowFlag; int Sum(int a,int b,int c) int y;y=a+b+c;If(y65535)OverflowFlag=1;return(y); ,/* DataP
20、rocess.h */ Extern int Sum (int a,int b,int c); Extern unsigned char OverflowFlag;,青岛大学-TI 大学生创新中心 傅强,28,1.5 代码优化,编译器可自动对代码进行优化 速度快,但占rom多 速度慢,占rom少 可选优化等级,等级越高,占rom越小 调试阶段最好关闭优化 要是不优化还有变量改变,则加volatile,青岛大学-TI 大学生创新中心 傅强,29,1.6 风格,对于写程序代码,有3个层次 首先,计算机要能读 其次,要自己能读 最后,要别人能读,青岛大学-TI 大学生创新中心 傅强,30,1.6.1
21、 变量命名规则,变量名要有含义,不要随便用abcxyz 变量名最好是名词词性,1-4个单词 每个单词首字母大写 Int InputVoltage; Int Temperature; 必须出现空格时,用下划线代替 Int Degree_C; Int Degree_F; 单词较长,适当简写 int NumOfInputChr; Int Deg_F; 多个模块都有的变量,按“模块名_变量名” Char ADC_Status; Int UART1_RxcharCnt; 约定俗成的变量不要改动 I、j作为循环变量,p、q作为指针,s、t表示字符串,青岛大学-TI 大学生创新中心 傅强,31,1.6.2
22、函数命名规则,模块名_不及物动词 模块名_及物动词+名词 首字母大写 专用名词全部大写 长单词缩写 返回值是布尔型的,对返回1还是0的表意清晰,Unsigned int ADC16_Sample(); Char LCD_Init(); Char RTC_Get(); Void PWM_SetPeriod(); Void Flash_WriteChar(); Void UART_GetChar(); Char Key_GetKey(); Char TouchPad_GetKey();,Transmit Tx Recetive Rx Count Cnt To 2,Char UART_CheckTx
23、Buff();/检查缓冲区 Char UART_IsTxBuffFull();/缓冲区是否已满,青岛大学-TI 大学生创新中心 傅强,32,1.6.3 表达式,好的表达式可以朗读出来消除歧义,运算优先级拿不准就加括号,表达式复杂就拆开。,If ( UART_IsTxBuffFull() ) UART_ClearTxBuff(); Else UART_PutChar(0x55);,i=5; j=+i; 结果i=6,j=6 等效于i=5; i=i+1; j=i; i=5; j=i+; 结果i=6,j=5 等效于i=5; j=i; i=i+1;,青岛大学-TI 大学生创新中心 傅强,33,1.6.4
24、 风格一致性,For(i=0;i100;i+) for(j=0;j200;j+)Count+; While(a=b) if(c=d)Flag=1;elseFlag=0; ,青岛大学-TI 大学生创新中心 傅强,34,1.6.5 注释,注释不是对代码的重复。要意译,不要直译。,For(i=6;iDOT;i- -) /从第6位到小数点依次递减 if(DispBuffi=0) DispBuffi= ; /如果该位数值是0,则替换成空格else break; /如果不是,则跳出循环 ,For(i=6;iDOT;i- -) /对全部6位显示数据进行判断 if(DispBuffi=0) DispBuffi
25、= ; /消隐小数点前的无效0else break; ,青岛大学-TI 大学生创新中心 傅强,35,功能函数的注释,/* * 名 称: LCD_DisplayDigit() * 功 能: 在LCD上任意位置显示一个数字 * 入口参数: Digit: 待显示数字 (09)Location: 显示位置 从左至右对应76543210 * 出口参数: 无 * 说 明: 调用该函数不影响LCD其他位的显示。 * 范 例: LCD_DisplayDigit(3,0); /在第一位(右侧最低位)显示3LCD_DisplayDigit(2,1); /在第二位显示2LCD_DisplayDigit(1,2);
26、/在第三位显示1 - 显示结果: 123 */ void LCD_DisplayDigit ( char Digit, char Location ) char DigitSeg; / 存放字形笔划的变量char *pLCD; / 存放LCD显存指针的变量DigitSeg = LCD_TabDigit; / 得到待显示数字的字形笔划pLCD= / 在LCDM1之后Location个单元显示出数字 ,青岛大学-TI 大学生创新中心 傅强,36,1.6.6 宏定义,宏定义给予常数可读性 宏定义全部大写,以区别变量数据加括号,注释使用/*/,#define TXBUFF_SIZE (128) /*发
27、送缓冲区大小*/ #define LCD_ROW (128) /*点阵液晶行数*/ #define LCD_CLUMN (128) /*点阵液晶列数*/ #define LCD_BUF_SIZE (LCD_CLUMN*LCD_ROW/8) /*点阵液晶缓冲区大小*/,Unsigned char TxBuffTXBUFF_SIZE; /定义发送缓冲区 Char IsTxBuffFull() if(NumOfTxChars=TXBUFF_SIZE) return(1); /缓冲区是否满?else return(0); ,青岛大学-TI 大学生创新中心 傅强,37,使用宏定义时,要防止定点数溢出,宏
28、定义的用途广泛 增强程序的可移植性 进行软件版本的管理,#define VOLT_RATE (1000) /*比例系数*/ . long Voltage; Int InputValue; . Voltage=InputValue*VOLT_RATE; /*可能溢出*/,#define VOLT_RATE (long(1000) /*比例系数,强行整成long*/,青岛大学-TI 大学生创新中心 傅强,38,1.7 可移植性,可移植性是嵌入式软件设计的重要思想 用到不同的硬件,比如LCD到数码管 用到不同的处理器,比如51到430,ARM 编程时应尽量消除硬件差异 无法消除的差异,集中到小的局部
29、来修改,青岛大学-TI 大学生创新中心 傅强,39,1.7.1 消除CPU差异,例如用宏定义消除IO口操作的差异此后的程序不用修改,直接用LED_ON和LED_OFF来操作IO口,#include”MSP430x42x.h” #define LED_ON P2OUT |=BIT0 #define LED_OFF P2OUT &=BIT0,#include”reg51.h” Sbit LED=P20; #define LED_ON P2.0=1; #define LED_OFF P2.0=0;,青岛大学-TI 大学生创新中心 傅强,40,1.7.2 消除硬件差异,用宏定义消除硬件差异 例如LCD
30、或数码管的IO口,顺序经常会改变,/*宏定义,数码管ag、DP各段对应的比特,更换硬件只用改动以下8行*/ #define a 0x01 #define b 0x02 #define c 0x04 #define d 0x08 #define e 0x10 #define f 0x20 #define g 0x40 #define DP 0x80,/*宏定义自动生成段码表,不要改动*/ Const char LCD_Tab = a+b+c+d+e+f, /0 b+c, /1 a+b+d+e+g, /2 a+b+c+d+g, /3 b+c+f+g, /4 a+c+d+f+g, /5 a+c+d+
31、e+f+g, /6 a+b+c, /7 a+b+c+d+e+f+g, /8 a+b+c+d+f+g, /9 #undef a .,青岛大学-TI 大学生创新中心 傅强,41,用函数来消除硬件差异,例如控制电机的启和停 直流电机设置IO口高低电平即可 步进电机要输出一定规则的脉冲序列 大型交流电机需要用串口控制变频器 我们把具体操作“封”在函数里,Moter_ON( ) . Moter_OFF( ) . ,青岛大学-TI 大学生创新中心 傅强,42,1.7.3 软件层次划分,大量复杂的调用函数需要有清晰的层次关系,菜单/人机界面,Key_GetKey(); Key_WaitKey();,键盘缓冲
32、区【FIFO】,定时扫描并判断按键,LCD_Clear(); LCD_DisplayNumber(); LCD_DisplayDecimal(); LCD_InsertChar(); LCD_DeleteChar();,显示缓冲区【数组】,LED定时循环扫描,LED显存映射,应用层,功能函数层,硬件隔离层,硬件驱动层,青岛大学-TI 大学生创新中心 傅强,43,应用层:最终功能,往往和人的感官有关 功能层:功能函数,表达具体要干什么 硬件隔离层:宏定义、缓冲区、函数封装 硬件隔离层(硬件抽象层Hardware Abstract Layer,HAL)是非常重要的环节。 功能函数只操作硬件隔离层的
33、数据,与具体硬件无关。 硬件驱动层:和具体硬件有关的代码,硬件驱动层从硬件隔离层获得“指示”,或是将“结果”发送至硬件隔离层。,青岛大学-TI 大学生创新中心 傅强,44,软件划分层次的好处,每个层的函数只对上下层“可见”,这样设置好接口参数后,各单元软件可独立开发。 改动硬件时,只需配置相应的底层驱动,修改硬件隔离层,而整个大软件功能可以不变。软件的可移植性增强。 功能层的函数无需关心硬件如何实现,以及何时何处被应用层调用,专心做好自己的事情就行。 将大型软件任务分块,团队合作。只要明确了模块功能,接口规范,就能分派任务。,青岛大学-TI 大学生创新中心 傅强,45,1.7.4 接口,Voi
34、d RTC_Tick(int DivSec) /硬件无关的走时函数,DivSec入口 char Days; /每月的天数变量DSEC+; /定时中断加数DISABLE_INT; /宏定义关总中断If(DSEC=DivSec) SECOND+;DSEC=0 /校正中断时间If(SECOND=60) MINUTE+;SECOND=0If(MINUTE=60) HOUR+;MINUTE=0If(HOUR=24) DATE+;HOUR=0If(MONTH=2) /闰年判断if(YEAR%4=0) Days=29;else Days=28;else Days=MONTH_TableMONTH-1; /月
35、份天数查表If(DATEDays) MONTH+;DATE=1;If(MONTH12) YEAR+;MONTH=1;If(YEAR=100) YEAR=0; /只显近百年时间RESTORE_INT; /宏定义恢复中断 ,青岛大学-TI 大学生创新中心 傅强,46,1.7.5 屏蔽,一个好的子函数,应消灭细节特征,只暴露最基本的要程序员选择的选项。类似于处理器中,设置各种特殊功能寄存器。 比如,手机拨号。GSM手机和CDMA手机的实际拨号过程差异巨大,但是我们看起来的拨号程序都是一样操作的。,UART_Init(2400,n,8,1); /2400比特,无校验,8位数据,1位停止位,青岛大学-T
36、I 大学生创新中心 傅强,47,1.8 版本管理,如果一个程序一开始设计就有N个变种(版本),用移植的办法改写,变成N种程序是不明智的。因为维护起来困难。 例如,有一个版本的软件按键带声音、另一个不带声音。可以用宏定义+条件编译宏来实现版本管理。,/*Config.h*/ #define KEYTONE ON /*是否打开按键音*/ #define RS485 ON /*是否打开RS485通信口*/ #define SCREEN LCD /*显示屏驱动LED=数码管 LCD=液晶*/ ,Char Key_GetKey() /读键盘函数 char KeyKey=Key_ReadFIFO(); /
37、从键盘缓冲区读一个按键 #if(KEYTONE=ON) /KEYTONE打开才编译下一句if(Key!=NOKEY) Beep(30); /有效按键则蜂鸣30ms #endifreturn(Key); /返回键值 ,青岛大学-TI 大学生创新中心 傅强,48,本章小结,MSP430单片机的入门知识 特点(低功耗、混合信号) 资源与结构(精简指令、普林斯顿结构) C语言(位操作、寄存器操作、中断、内部函数、库函数) 软件工程的共性问题 文件管理(拆成小的c程序,头文件调用) 代码优化(编译器的作用,先不优化) 风格(命名规则、表达式、注释、宏定义) 可移植性(cpu差异、硬件差异、软件层次、接口、屏蔽) 版本管理(宏定义与条件编译宏),青岛大学-TI 大学生创新中心 傅强,49,