类似于纯软件编程中的hello world,单片机的入门一般从翻转IO开始,可以看到无数的教材和实验都从点亮一颗LED开始,这个实验既可以检验我们的开发环境包括单板、IDE、基础代码等是否OK,又可以让初学者所见即所得,感受自己的代码转变为对应硬件行为的有趣之处。

这篇入门博客也不免俗,我们就从翻转IO开始,但我们要到做出一个好玩的呼吸灯才结束。

1、我要准备什么?

1.1、硬件

学习单片机,由于跟硬件有关系,所以还是推荐入手一块实际的板子,无论是什么外设都不带只有一块单片机芯片的最小系统,还是带整套外设的开发板都可以。但是我自己有下面几点建议:

  • 目前市场上,同等配置,430的板子一般比51和STM32的要贵,430芯片本身价格稍贵加之卖的没有51和STM32那么普遍,所以做好心理准备,充分了解430的优点和选型,嵌入式属于比较烧钱的兴趣,开发板会是你的第一笔投资。

  • 下载器是容易忽略又必不可少的部分,430正版的FET调试器比较贵,个人认为没有必要花2~300买这种全系的调试器,如果你入门使用的是比较新的系列,支持SBW调试,推荐直接入手一块官方的MSP430 LAUNCHPAD,70左右同时得到了调试器和2553/2452芯片两枚。

  • 如果入手淘宝上个人制作的开发板,请注意是否有调试器以及配置够用即可,没有必要花大价钱买所谓顶配开发板,不实用。

  • 最后,如果是在校大学生,可以关注TI官网,LAUNCHPAD系列有可能会打折甚至免费申请,TI对教育的支持力度很大,在校学生和老师可以在官网比较容易地申请到样片。

除此之外,一台电脑也是必须的,对配置没要求,能用就行。

1.2、软件

MSP430的软件开发推荐使用TI官方的CCS软件,在官网即可下载,也可以在我这里贴的WIKI地址下载
ccs_download_guide

除了CCS,还有其他的软件可以开发,比如IAR,或者使用VIM搭配430 GCC编译,后面这两种都不推荐。软件安装这里不作介绍,网上一大把,最常碰到的问题是杀毒软件拦截导致安装失败、GHOST系统被精简导致launchpad调试器驱动安装不上等,为了避免这些问题:

  • 1.关闭360等杀毒软件
  • 2.确保系统用户不是中文名
  • 3.安装位置有至少2G空间
  • 4.安装路径不要有中文或者特殊符号
  • 5.如果插上开发板驱动安装失败,无法下载程序,可以手动安装试试,CCS对应驱动目录:CCS安装目录\ ccsv6\ccs_base\emulation\drivers。在计算机上右键-设备管理器-找到要安装驱动的设备-右键更新驱动程序-从列表或者指定位置安装-点击浏览选择到驱动所在位置-确定,程序会自动搜索并安装驱动

如果你不想入手开发板,想先试试开发环境,还有一个PROTEUS软件,可以方针很多芯片,其中就有MSP430的部分基础型号,这个软件自带了一个仿真单片机电路的环境,自带一个代码编辑器,然后配合CCS或者GCC的编译器可以实现全套的运行和仿真。不过个人建议在资源紧缺的时候作补充验证作用,最后做出实物肯定还是要准备硬件的,毕竟站在岸上学不会游泳。
ccs_workspace

2、还是先点个LED试试吧

铺垫这么多,还是先老老实实点个LED试试吧。

2.1、熟悉CCS

如果一切顺利,安装完CCS并打开,你能看到这是基于ECLIPSE引入插件构成的IDE,所以具有ECLIPSE最典型的特征-workspace(工作空间)。Workspace实际上就相当于一个文件夹,你可以在里面建立很多工程。唯一值得注意的是,你对编辑器所做的所有设置均会存放于workspace下的.metadata(隐藏文件夹)文件夹中,换一个workspace,这个metadata配置不会转到新的工作空间中去,相当于换了一个新的编辑器,这也是Eclipse的优点之一。

ccs_workspace
2.2、熟悉快捷键
快捷键 功能
Ctrl + C\V\X\S 复制\粘贴\剪切\保存
Ctrl + F 查找\替换
Ctrl + H 高级查找
Ctrl + / 代码自动提示
Ctrl + Alt + 上\下 向上\向下复制当前行
Alt + 上\下 向上\向下移动当前行
Tab\Shift + Tab 向后\向前缩进
F11 下载当前工程
F8\Ctrl + F8 运行\暂停当前调试
Alt + Shift + R 重命名当前变量
其他 自己百度Eclipse快捷键
2.3、熟悉开发板

launchpad的主要结构如下图所示:
blink_led

其中,指示灯和按键的主要电路连接关系如下:
blink_led
可以看到LED1和LED2两个灯的正极就连在P1.0和P1.6上,负极都接地,那么当我们操作单片机让P1.0/P1.6输出高电平,就可以点亮了。

2.4、点亮LED代码

打开CCS,在最左边的工作空间右键->New->CCS Project,Target选择使用的芯片型号(比如MSP430G2553),Project name输入你的工程名字,然后下面的列表一般选择Empty Project(with main.c),但是今天我们选择Blink The LED,这样程序就自动帮我们生成了一个闪灯程序,代码如下:

#include <msp430.h>				


/**
 * blink.c
 */
void main(void)
{
	WDTCTL = WDTPW | WDTHOLD;		// 停止看门狗
	P1DIR |= BIT0;					// 设置P1.0管脚为输出脚

	volatile unsigned int i;		// volatile ,下面的延时代码不会被编译器优化

	while(1)
	{
		P1OUT ^= BIT0;				// 翻转 P1.0
		for(i=10000; i>0; i--);     // 延时一会
	}
}

接下来,插上launchpad,点击顶部小虫子按钮下载代码,然后点绿箭头全速运行,你就可以看到闪烁效果。

blink_led

代码解释:

P1DIR |= BIT0; // 设置P1.0管脚为输出脚
P1OUT ^= BIT0; // 翻转 P1.0
.
首先把P1设置成输出,430的管脚是可以设置的,是输出一个高低电压还是接收外界的电压输入,可以通过P1DIR来设置,BIT0BIT7分别对应PX.0PX.7管脚。那么:
将P2.3管脚设置为输出:P2DIR |= BIT3;
将P2.3管脚设置为输入:P2DIR &= ~BIT3;
.
第二句P1OUT ^= BIT0;中,P1OUT则不掌管输入输出方向了,而是管理输出的到底是高还是低电平。同理:
P2.3输出高电平:P2OUT |= BIT3;
P2.3输出低电平:P2OUT &= ~BIT3;
.
那这里代码中的^=表示,设置为和当前相反,当前为高则设为低,为低则设置为高。循环执行则实现了我们的闪烁效果。

3、什么是呼吸灯?

呼吸灯是LED灯在我们的控制下,有规律的逐渐从暗变亮又变暗的过程,当我们亮暗的程度和频率把握得当时,看起来灯好像是人在呼吸一样

下面是典型的呼吸灯应用场景:开关按钮

timg1

呼吸灯不再是我们闪烁那样一高一低的变化规律,而是有渐变性,是连续变化的。从亮度上来看,闪烁和呼吸灯我们可以猜想变化规律是这样的:
light_change

所以呼吸灯核心是,实现亮度的均匀变化!

那么单片机是数字系统,只有高低两种状态。渐变的话要有中间不高不低的这种亮度,怎么实现呢?有一种数字系统控制模拟系统经典的办法就是---PWM调制

PWM调制的具体内容初学的话可以不用纠结,只需要跟我一起,动手改改代码就能感受到!

  1. 首先,我们把代码中的延时取消试试会有什么现象!
	while(1)
	{
		P1OUT ^= BIT0;				// 翻转 P1.0
		//for(i=10000; i>0; i--);     // 加"//"注释掉这个延时
	}

有什么变化?灯是不是不闪了?那亮度呢?

什么,你跟我说很亮?那你记住现在这个亮度,让后把代码改成这样试试:
P1OUT |= BIT0; // P1.0常亮

P1不再翻转,一直亮,现在这个亮度和刚刚的亮度,哪个亮?-----一样亮???好吧。。。

看到上面插USB线的那里,是不是有绿色的电源灯,很亮吧。我们下面第二个灯也是绿色的,改变下面两行,我们把灯切换到绿色的上面:
P1DIR |= BIT6; // 0改成6
P1OUT ^= BIT6; // 0改成6,注意还原成^=翻转

然后,跑起来,下面的绿灯是不是亮了,和上面的电源灯比呢?是不是下面的亮度低!

原理解释:取消延时后,LED灯的亮和暗之间就不会等待一会了,会以极快的速度亮暗循环变化,亮度的变化波形和加延时是一样的,只不过速度更快了。由于人眼的暂留效应,反应不过来,就看到一直是亮的。但是由于亮和暗交替,LED实际只显示出了50%亮度的效果,看起来就比常亮的电源灯暗一半左右。

  1. 现在我们已经实现亮度控制到一般了,那么1/4怎么设置?思考一下,我们把亮的时间控制在1/4,暗的控制在3/4不就行了。
	while(1)
	{
		P1OUT |= BIT0;			  // 亮
		for(i=100; i>0; i--);     // 延时100
		P1OUT &= ~BIT0;           // 灭
		for(i=300; i>0; i--);     // 延时300
	}

是不是亮度更低了
3. 想办法,弄个不是固定而是逐渐变化的比例,把i的长度弄成变量就可以了

#include <msp430.h>

void main(void)
{
    WDTCTL = WDTPW | WDTHOLD;       // 停止看门狗
    P1DIR |= BIT6;                  // 设置P1.0管脚为输出脚

    volatile unsigned int i;        // volatile ,下面的延时代码不会被编译器优化
    volatile unsigned int j;        // volatile ,下面的延时代码不会被编译器优化

    while(1)
    {
        for(j=400; j>0; j--)
        {
            P1OUT |= BIT6;            // 亮
            for(i=j; i>0; i--);     // 延时100
            P1OUT &= ~BIT6;            // 灭S
            for(i=400-j; i>0; i--);     // 延时300
        }
    }
}
  1. 是不是感觉这是一个假的呼吸灯,根本不像呼吸的规律,突然变亮更像是心跳。这跟LED的导电电流变化规律有关系,为了使效果更逼真,我们稍微改造一下,让亮度按正弦规律变化而不是三角波的规律变化。代码如下:
#include <msp430.h>
#include <math.h>

void main(void)
{
    WDTCTL = WDTPW | WDTHOLD;       // 停止看门狗
    P1DIR |= BIT6;                  // 设置P1.0管脚为输出脚

    volatile unsigned int i;        // volatile ,下面的延时代码不会被编译器优化
    volatile unsigned int j;        // volatile ,下面的延时代码不会被编译器优化

    while(1)
    {
        for(j=500; j>0; j--)
        {
            P1OUT |= BIT6;            // 亮
            for(i=250 + 249 * sin((2*3.14*(double)j)/500); i>0; i--);     // 延时100
            P1OUT &= ~BIT6;            // 灭
            for(i=250 - 249 * sin((2*3.14*(double)j)/500); i>0; i--);     // 延时300
        }
    }
}

是不是觉得完全不是那么回事?甚至都看到灯在闪了,根本不是呼吸。这是因为代码中的sin对单片机的运算来说太复杂了,要花很长时间完成一步,打破了我们的规律性,所以我们可以把这个提前算好,放在数组里直接查询,这就是查表法,很多应用场景可以使用。这个表其实就是算好500个点从0~499对应的sin值,可以用excel生成或者matlab生成均可。
EXCEL公式:=250SIN(23.1415*(A1)/500)

#include <msp430.h>
#include <math.h>

const static int sin_table[500] =
{3 , 6 , 9 , 13 , 16 , 19 , 22 , 25 , 28 , 31 , 34 , 38 , 41 , 44 , 47 , 50 , 53 , 56 , 59 ,
62 , 65 , 68 , 71 , 74 , 77 , 80 , 83 , 86 , 89 , 92 , 95 , 98 , 101 , 104 , 106 , 109 ,
112 , 115 , 118 , 120 , 123 , 126 , 129 , 131 , 134 , 137 , 139 , 142 , 144 , 147 , 149 ,
152 , 154 , 157 , 159 , 162 , 164 , 166 , 169 , 171 , 173 , 176 , 178 , 180 , 182 , 184 ,
186 , 189 , 191 , 193 , 195 , 197 , 198 , 200 , 202 , 204 , 206 , 208 , 209 , 211 , 213 ,
214 , 216 , 218 , 219 , 221 , 222 , 223 , 225 , 226 , 228 , 229 , 230 , 231 , 232 , 234 ,
235 , 236 , 237 , 238 , 239 , 240 , 241 , 241 , 242 , 243 , 244 , 244 , 245 , 246 , 246 ,
247 , 247 , 248 , 248 , 248 , 249 , 249 , 249 , 250 , 250 , 250 , 250 , 250 , 250 , 250 ,
250 , 250 , 250 , 250 , 249 , 249 , 249 , 248 , 248 , 248 , 247 , 247 , 246 , 246 , 245 ,
244 , 244 , 243 , 242 , 241 , 241 , 240 , 239 , 238 , 237 , 236 , 235 , 234 , 232 , 231 ,
230 , 229 , 228 , 226 , 225 , 223 , 222 , 221 , 219 , 218 , 216 , 214 , 213 , 211 , 209 ,
208 , 206 , 204 , 202 , 200 , 199 , 197 , 195 , 193 , 191 , 189 , 186 , 184 , 182 , 180 ,
178 , 176 , 173 , 171 , 169 , 167 , 164 , 162 , 159 , 157 , 154 , 152 , 149 , 147 , 144 ,
142 , 139 , 137 , 134 , 131 , 129 , 126 , 123 , 120 , 118 , 115 , 112 , 109 , 106 , 104 ,
101 , 98 , 95 , 92 , 89 , 86 , 83 , 80 , 77 , 74 , 71 , 68 , 65 , 62 , 59 , 56 , 53 , 50 ,
47 , 44 , 41 , 38 , 34 , 31 , 28 , 25 , 22 , 19 , 16 , 13 , 9 , 6 , 3 , 0 , -3 , -6 , -9 ,
-13 , -16 , -19 , -22 , -25 , -28 , -31 , -34 , -38 , -41 , -44 , -47 , -50 , -53 , -56 ,
-59 , -62 , -65 , -68 , -71 , -74 , -77 , -80 , -83 , -86 , -89 , -92 , -95 , -98 , -101 ,
-104 , -106 , -109 , -112 , -115 , -118 , -120 , -123 , -126 , -129 , -131 , -134 , -137 ,
-139 , -142 , -144 , -147 , -149 , -152 , -154 , -157 , -159 , -162 , -164 , -166 , -169 ,
-171 , -173 , -176 , -178 , -180 , -182 , -184 , -186 , -189 , -191 , -193 , -195 , -197 ,
-198 , -200 , -202 , -204 , -206 , -208 , -209 , -211 , -213 , -214 , -216 , -218 , -219 ,
-221 , -222 , -223 , -225 , -226 , -228 , -229 , -230 , -231 , -232 , -234 , -235 , -236 ,
-237 , -238 , -239 , -240 , -240 , -241 , -242 , -243 , -244 , -244 , -245 , -246 , -246 ,
-247 , -247 , -248 , -248 , -248 , -249 , -249 , -249 , -250 , -250 , -250 , -250 , -250 ,
-250 , -250 , -250 , -250 , -250 , -250 , -249 , -249 , -249 , -248 , -248 , -248 , -247 ,
-247 , -246 , -246 , -245 , -244 , -244 , -243 , -242 , -241 , -241 , -240 , -239 , -238 ,
-237 , -236 , -235 , -234 , -232 , -231 , -230 , -229 , -228 , -226 , -225 , -223 , -222 ,
-221 , -219 , -218 , -216 , -214 , -213 , -211 , -209 , -208 , -206 , -204 , -202 , -200 ,
-199 , -197 , -195 , -193 , -191 , -189 , -187 , -184 , -182 , -180 , -178 , -176 , -173 ,
-171 , -169 , -167 , -164 , -162 , -159 , -157 , -154 , -152 , -150 , -147 , -144 , -142 ,
-139 , -137 , -134 , -131 , -129 , -126 , -123 , -120 , -118 , -115 , -112 , -109 , -106 ,
-104 , -101 , -98 , -95 , -92 , -89 , -86 , -83 , -80 , -77 , -74 , -71 , -68 , -65 , -62 ,
-59 , -56 , -53 , -50 , -47 , -44 , -41 , -38 , -34 , -31 , -28 , -25 , -22 , -19 , -16 ,
-13 , -9 , -6 , -3 , 0};

/**
 * blink.c
 */
void main(void)
{
    WDTCTL = WDTPW | WDTHOLD;       // 停止看门狗
    P1DIR |= BIT6;                  // 设置P1.0管脚为输出脚

    volatile unsigned int i;        // volatile ,下面的延时代码不会被编译器优化
    volatile unsigned int j;        // volatile ,下面的延时代码不会被编译器优化

    while(1)
    {
        for(j=499; j>0; j--)
        {
            P1OUT |= BIT6;                           // 亮
            for(i=250 + sin_table[j]; i>0; i--);     // 延时
            P1OUT &= ~BIT6;                          // 灭
            for(i=250 - sin_table[j]; i>0; i--);     // 延时
        }
    }
}

把这个代码输入编辑器,编译运行,呼吸灯是不是做好了?

breath