B001-Atmega16-数码管

本文详细介绍了如何在ATmega16芯片上实现数码管的动态显示、静态显示、余晖现象及其消除方法,通过74HC138和74HC573驱动芯片控制数码管,结合消息机制优化显示任务,最终实现按指定进制动态显示数值。
摘要由CSDN通过智能技术生成

一步步完成数码管

主要内容:
第一步:产生1ms的时基
第二步:静态显示
第三步:动态扫描
第四步:余晖 重影
第五步:带消息机制的任务函数
第六步:按指定进制显示


-------------------------------------------------------------------------------------------------------------------------------------
开发环境:AVR Studio 4.19 + avr-toolchain-installer-3.4.1.1195-win32.win32.x86
芯片型号:ATmega16
芯片主频:8MHz

-------------------------------------------------------------------------------------------------------------------------------------

第一步: 产生1ms的时基

说明:
1、使用定时器0的 CTC中断产生 1ms的时基信号, CTC模式下时自动重装初值、比较方便。
2、使用 OCF0中断、不需要 OC0引脚输出波形。

代码:

Drv_Timer.h中的相关定义:

// -------------------
// 定时器中断模式
typedef enum 
{
    INT_MODE_TOV = 0,
    INT_MODE_OCF = 1,
    INT_MODE_ICF = 2,
    INT_MODE_OCF1A = 3,
    INT_MODE_OCF1B = 4
} TIMER_INT_MODE;

// 定时器比较匹配引脚输出模式
typedef enum 
{
    COM_MODE_NONE   = 0,
    COM_MODE_TOGGLE = 1,
    COM_MODE_CLEAR  = 2,
    COM_MODE_SET    = 3,
} TIMER_COM_MODE;

// 定时器0
typedef enum 
{
    T0_WGM_NOMAL     = 0,
    T0_WGM_PHASE_PWM = 1,
    T0_WGM_CTC       = 2,
    T0_WGM_FAST_PWM  = 3,

    T0_CLK_SOURCE_NONE     = 0,
    T0_CLK_SOURCE_CLK_1    = 1,
    T0_CLK_SOURCE_CLK_8    = 2,
    T0_CLK_SOURCE_CLK_64   = 3,
    T0_CLK_SOURCE_CLK_256  = 4,
    T0_CLK_SOURCE_CLK_1024 = 5,
    T0_CLK_SOURCE_T0_FALL  = 6,
    T0_CLK_SOURCE_T0_RAISE = 7
} TIMER0_MODE;
Drv_Timer.c中的操作函数:
// ==========================================================================================================
// TIMER0 初始化
// 
// 参数:wave_mode       工作模式/波形产生模式选择
//       OC_mode         比较匹配/PWM输出模式选择
//       clk_source      时钟源和预分频选择
// 
// 写TCCR0时需要清除bit7=FOC0
// 
// 定时器溢出周期 T = ((1.0 / 8000000) * 1000000) * clk_source * 256 ( @ 8MHz )
// ==========================================================================================================
void Drv_Timer0_init(const uint8_t wave_mode, const uint8_t com_mode, const uint8_t clk_source)
{
    uint8_t wgm00,wgm01;

    wgm00 =  wave_mode & 0x01;
    wgm01 = (wave_mode & 0x02) >> 1;

    // 写TCCR0时需要将bit7=FOC0清0
    TCCR0 = (wgm00 << 6)|               // 工作模式/波形产生模式选择
            (wgm01 << 3)|
            ((com_mode & 0x03)   << 4)| // 比较匹配/PWM输出模式选择
            ((clk_source & 0x07) << 0); // 时钟源和预分频选择
}

// ==========================================================================================================
// TIMER0 中断使能
// 
// 参数:int_mode  = INT_MODE_TOV 或 INT_MODE_OCF 或 INT_MODE_ICF
//       enable    = ENABLE 或 DISABLE
// 
// 说明:
// 1、OC0引脚要先配置成比较匹配引脚、再修改数据方向寄存器DDB3
// 2、可以单独使能/禁止一种模式的中断
// 
// ==========================================================================================================
void Drv_Timer0_INT_Enable(const uint8_t int_mode, const uint8_t enable)
{
    if(INT_MODE_TOV == int_mode)
    {
        if(DISABLE == enable)
        {
            TIMSK &= ~(1 << TOIE0);
        }
        else
        {
            TIMSK |=  (1 << TOIE0);
        }
        TIFR |= (1 << TOV0);
        return ;
    }
    if(INT_MODE_OCF == int_mode)
    {
        if(DISABLE == enable)
        {
            TIMSK &= ~(1 << OCIE0);
        }
        else
        {
            TIMSK |=  (1 << OCIE0);
        }
        TIFR |= (1 << OCF0);
    }
}

// ==========================================================================================================
//      设置TCNT0和OCR0的值
// 
// (1). 在比较匹配下、OCR0需要在TCNT0被设置之后设置
// ==========================================================================================================
void Drv_Timer0_set_TCNT0_OCR0(const uint8_t tcnt0, const uint8_t ocr0)
{
    TCNT0 = tcnt0;
    OCR0  = ocr0;
}
sys_timer.c中设置定时器0,并在OCF0中断中使用PA1测试定时时间:
#include <avr/interrupt.h>
#include "Drv_Timer.h"
#include "sys_timer.h"

// ==========================================================================================================
//      系统任务定时器
// 
// (1). 使用Timer0产生1ms的时标
//      定时周期 T = ((1.0/8000000)*1000000)*64*(124+1) = 1000us = 1ms
// 
// ==========================================================================================================
void sys_timer_init(void)
{
    // 定时器0初始化:CTC模式、OC0引脚不连接、64预分频
    Drv_Timer0_init(T0_WGM_CTC, COM_MODE_NONE, T0_CLK_SOURCE_CLK_64);
    // 设置初值:TCNT0=0、OCR0=122
    Drv_Timer0_set_TCNT0_OCR0(0, 122);
    // 使能OCF0中断
    Drv_Timer0_INT_Enable(INT_MODE_OCF, ENABLE);
}

// ==========================================================================================================
//      系统定时器中断
// 
// (1). 使用Timer0的CTC中断调度各个任务
// 
// ==========================================================================================================
ISR(TIMER0_COMP_vect)
{
    PORTA ^= (1 << PA1); // 使用PA1测试定时周期
}
main.c中完成初始化,并设置IO:
// ==========================================================================================================
// 主函数
// ==========================================================================================================
#include <avr/io.h>
#include "Drv_Timer.h"
#include "system.h"
#include "sys_timer.h"
#include "config.h"

// ==========================================================================================================
// main函数
// ==========================================================================================================
int main(void)
{
    // ---------
    // 关全局中断
    cli();

    // 系统初始化 ( 包含sys_timer_init() )
    sys_init();

    DDRA  |=   (1 << DDA0) | (1 << DDA1);
    PORTA &= ~((1 << PA0 ) | (1 << PA1 ));

    // OC0/PB3初始化为输出0
    DDRB  |=   (1 << DDB3);
    PORTB &= ~((1 << PB3 ));

    // 开全局中断
    sei();

    // ---------
    while(1)
    {
    }
    return 0;
}

测试结果:

示波器输出如下:

1、 PA1引脚输出方波,周期是 2*1.0ms,引脚电平每隔 1.0ms翻转一次。
      使用 OCR0=124、计算得到精确的 1.0ms,但进入中断函数是需要花费时间的。
      所以这里使用稍小的 OCR0=122,让从中断产生到进入中断函数为止的时间更精确为 1.0ms
      有些计时功能会积累时基的误差、越到后面积累的误差越大,所以这里能精确就尽量做的精确些。
      到此、 1.0ms定时完成。
2、 OC0引脚没有波形输出,我们也不需要用到这个引脚,就让他保持普通 IO的特性吧。


-------------------------------------------------------------------------------------------------------------------------------------

第二步: 静态显示

说明:

1、这一步需要根据电路图、在指定的数码管上显示指定的符号。

1、数码管驱动电路图:


电路中使用的是共阴极数码管:
1个数码管有8个LED,称为8段数码管

</

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值