美文网首页嵌入式C语言&嵌入式单片机学习
带你AVR入个门:2.点亮LED(下)

带你AVR入个门:2.点亮LED(下)

作者: 乱世工人 | 来源:发表于2017-02-28 23:52 被阅读154次

    上回提到,通过与或非的组合使用可以控制特定寄存器的特定某个位,接下来玩点花样。

    让灯闪烁咋整?

    闪烁就是:亮灯、灭灯、亮灯、灭灯……

    一直闪的话,放在while(1) 循环里面就好了。

    比如让PC7的灯闪烁:

    while(1)
    {
        PORTC &= ~(1 << PC7);//亮灯
        PORTC |= (1 << PC7);//灭灯
    }
    

    编译下载运行,发现啥事都没发生,程序没运行?不是,程序正在运行,而且是以一种快到肉眼看不见的速度在闪烁。

    因为亮灯跟灭灯之间要加延时。

    普遍的延时函数就是两层循环语句在狂跑,这里介绍一个 Atmel Studio 自带的延时函数。

    首先你要包含头文件

    #include <util/delay.h>
    

    然后跟着我做一个小小的设置:

    菜单栏 Project > 你的项目名字 Properties > Toolchain > AVR/GCC C Compiler

    在 Defined symbols 新增一行

    F_CPU=7372800UL
    

    这里的7372800UL指的是你MCU的晶振频率,UL表示该数据是无符号长整型常量,假如你用的是8M的晶振,相应要写成

    F_CPU=8000000UL
    

    这样你就可以用自带的官方延时函数:

    _delay_ms()
    

    _delay_us()
    

    顾名思义,一个是毫秒延时,一个是微秒延时。

    把延时1秒(即1000毫秒)加到程序上:

    while(1)
    {
        _delay_ms(1000);
        PORTC &= ~(1 << PC7);//亮灯
        _delay_ms(1000);
        PORTC |= (1 << PC7);//灭灯
    }
    

    现在你就可以看到L1以1秒的间隔在一闪一闪亮晶晶了。

    但目前的代码还只是玩具级别,接下来我教你用工程的思维来组建程序。

    首先,像

    PORTC &= ~(1 << PC7);//亮灯
    

    这样的语句是不应该出现在主程序里面的,不但可读性不好,可移植性也不好。

    既然这个程序有点灯功能,我们给项目新增一个专门的light.c文件和与之配套的light.h。

    在右侧Solution Explorer的项目名称右键菜单选择Add>New Item:



    在弹出的窗口选择C File类型,文件名为light.c



    再Add一遍,这次文件类型选择Include File,文件名为light.h

    在main.c的#include <avr/io.h>下添加一行#include "light.h"

    #include <avr/io.h>
    #include "light.h"
    

    这样,main.c跟light.c就联系起来了。

    接下来的步骤是:

    在light.c里边写函数,在light.h里边声明函数,在main.c里边调用函数。

    首先,在light.c里添加一行

    #include <avr/io.h>
    

    接下来写个亮灯的函数。函数命名一般按照主谓或者主谓宾的结构:这是什么器件、它要操作什么、它要操作的对象是什么。

    在这里,一号灯,点亮,这么写:

    void light1_on(void)
    {
        PORTC &= ~(1 << PC7);
        DDRC |= (1 << PC7);
    }
    

    灭掉,这么写:

    void light1_off(void)
    {
        PORTC |= (1 << PC7);
        DDRC |= (1 << PC7);
    }
    

    接下来在light.h里写这两个函数的声明:

    #ifndef LIGHT_H_
    #define LIGHT_H_
    
    void light1_on(void);
    void light1_off(void);
    
    #endif /* LIGHT_H_ */
    

    现在main.c里边就可以直接调用了:

    while(1)
    {
        _delay_ms(1000);
        light1_on();
        _delay_ms(1000);
        light1_off();
    }
    

    看,我现在注释已经去掉了,但并不影响阅读,一份写得好的程序,是可以自注释的。

    可读性是有了,但还不具备可移植性。

    可移植性是在特定的范围下讨论的,有跨编译器的可移植性,跨芯片的可移植性,在这里,我们讨论的是同样编译器同样芯片的前提下,电路图稍微改变,如何在尽量少改动代码的前提下移植程序。

    目前的light1_on和light1_off函数都是控制PC7引脚的LED,假如换成了接在PB3呢?把程序里出现的PC7全部换成PB3?

    优雅的做法是:用宏定义把具体的标识符换个名字。

    在light.c开头这么写:

    #define LIGHT1_PORT PORTC
    #define LIGHT1_DDR DDRC
    #define LIGHT1 PC7
    

    然后重写两个函数:

    void light1_on(void)
    {
        LIGHT1_PORT &= ~(1 << LIGHT1);
        LIGHT1_DDR |= (1 << LIGHT1);
    }
    
    void light1_off(void)
    {
        LIGHT1_PORT |= (1 << LIGHT1);
        LIGHT1_DDR |= (1 << LIGHT1);
    }
    

    把这个程序移植到另一个LED接在PB3的电路板时,只需要把

    #define LIGHT1_PORT PORTC
    #define LIGHT1_DDR DDRC
    #define LIGHT1 PC7
    

    改成

    #define LIGHT1_PORT PORTB
    #define LIGHT1_DDR DDRB
    #define LIGHT1 PB3
    

    就可以了。

    最后放一下我的公众号:

    相关文章

      网友评论

        本文标题:带你AVR入个门:2.点亮LED(下)

        本文链接:https://www.haomeiwen.com/subject/alrtgttx.html