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段数码管

</

好的,这是一个计数器的Verilog HDL设计,满足您的需求: ``` module counter( input clk, //输入时钟信号 input rst, //异步复位信号 input en, //同步使能信号 output reg [3:0] seg //四位一体数码管输出 ); reg [2:0] count; //计数器寄存器 reg [2:0] next_count; //下一个计数器值 //计数器状态转移规律 always @ (posedge clk or posedge rst) begin if (rst) begin count <= 3'b000; //复位时计数器清零 end else if (en) begin count <= next_count; //使能时根据规律更新计数器值 end end //根据规律生成下一个计数器值 always @ (count) begin case (count) 3'b000: next_count = 3'b001; 3'b001: next_count = 3'b011; 3'b011: next_count = 3'b111; 3'b111: next_count = 3'b101; 3'b101: next_count = 3'b100; 3'b100: next_count = 3'b110; 3'b110: next_count = 3'b010; 3'b010: next_count = 3'b000; default: next_count = 3'b000; endcase end //将计数器值转换为二进制数并输出到数码管上 always @ (count) begin case (count) 3'b000: seg = 4'b0000; 3'b001: seg = 4'b0001; 3'b010: seg = 4'b0010; 3'b011: seg = 4'b0011; 3'b100: seg = 4'b0100; 3'b101: seg = 4'b0101; 3'b110: seg = 4'b0110; 3'b111: seg = 4'b0111; default: seg = 4'b0000; endcase end endmodule ``` 该计数器模块包括异步复位、同步使能和计数器状态转移三个部分。在计数器状态转移部分,使用了case语句实现了计数器状态的转移。在计数器值转换部分,同样使用了case语句实现了计数器值到二进制数的转换,并将结果输出到四位一体数码管上进行实时、同步显示。 希望这个Verilog HDL设计能够满足您的需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值