这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 企业专区 » TI » 【TIMSPM0MCU焕新大作战】+课程2任务1:定时器方式实现数码管递减显示数

共6条 1/1 1 跳转至

【TIMSPM0MCU焕新大作战】+课程2任务1:定时器方式实现数码管递减显示数字

专家
2024-04-16 23:22:23     打赏

前面我们用延时方式实现了数码管的数字倒计时显示。这一次我们用定时器的方式显示倒计时。区别在于本次使用定时器中断方式来动态改变要显示的数字。而主处理中只需要不断循环显示这个数值就行。

由于处理逻辑很简单,就不绘制流程图了。

MSPM0L系列单片机本身有个通用定时器TIMG,是16位的自动重装定时器,支持向上和向下技术两种模式。同时还带有两个比较捕获单元,做到输出比较、输入捕获、PWM输出、单脉冲输出等功能。

TIMG可以选择BUSCLK、MFCLK、LFCLK作为时钟源,最大8分频时钟,再经过一个8位的预分频器,最终成为定时器的计数时钟。

因为对单片机的外设不是很懂,幸好有Sysconfig这个配置工具,就通过Sysconfig来配置定时器,双击工程区域中的gpio_toggle_output.syscfg文件,如果你安装了Sysconfig,会自动进入配置页面。按照我做的配置就可以实现定时器每10ms产生一次中断。

定时器1.png配置定时器需要的参数:

定时器2.png设置完成后,然后点击编译按钮(工具栏的小锤子)。回到“gpio_toggle_output.c”程序代码窗口,找到“SYSCFG_DL_init();”代码行,寻找这个函数的定义位置,看内部代码:


SYSCONFIG_WEAK void SYSCFG_DL_init(void)
{
    SYSCFG_DL_initPower();
    SYSCFG_DL_GPIO_init();
    /* Module-Specific Initializations*/
    SYSCFG_DL_SYSCTL_init();
    SYSCFG_DL_TIMER_0_init();
}

增加了一个和Timer0有关的处理代码,继续看SYSCFG_DL_TIMER_0_init这个函数的定义:


/*
 * Timer clock configuration to be sourced by BUSCLK /  (8000000 Hz)
 * timerClkFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1)))
 *   1000000 Hz = 8000000 Hz / (4 * (7 + 1))
 */
static const DL_TimerG_ClockConfig gTIMER_0ClockConfig = {
    .clockSel    = DL_TIMER_CLOCK_BUSCLK,
    .divideRatio = DL_TIMER_CLOCK_DIVIDE_4,
    .prescale    = 7U,
};
/*
 * Timer load value (where the counter starts from) is calculated as (timerPeriod * timerClockFreq) - 1
 * TIMER_0_INST_LOAD_VALUE = (10 ms * 1000000 Hz) - 1
 */
static const DL_TimerG_TimerConfig gTIMER_0TimerConfig = {
    .period     = TIMER_0_INST_LOAD_VALUE,
    .timerMode  = DL_TIMER_TIMER_MODE_PERIODIC,
    .startTimer = DL_TIMER_STOP,
};
SYSCONFIG_WEAK void SYSCFG_DL_TIMER_0_init(void) {
    DL_TimerG_setClockConfig(TIMER_0_INST,
        (DL_TimerG_ClockConfig *) &gTIMER_0ClockConfig);
    DL_TimerG_initTimerMode(TIMER_0_INST,
        (DL_TimerG_TimerConfig *) &gTIMER_0TimerConfig);
    DL_TimerG_enableInterrupt(TIMER_0_INST , DL_TIMERG_INTERRUPT_ZERO_EVENT);
    DL_TimerG_enableClock(TIMER_0_INST);
}

这一大段代码就是我们在SysConfig中通过设置得到的。

接下来我们要在主程序中加入Timer0的中断处理代码了。

// Timer0的中断处理,按照配置,每10ms完成一次中断,累计100次,得到1S的周期


void TIMER_0_INST_IRQHandler(void) {
    switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) {
        case DL_TIMERG_IIDX_ZERO:
            // 定时器0完成一次中断
            cnt_10ms=(cnt_10ms+1)%100;
            if (cnt_10ms==0) {
                //DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_3_PIN);
                // 已经达到1秒钟,计数值减1
                curnum--;
                if (curnum<0) {
                    curnum=10;
                }
            }
            break;
        case DL_TIMERG_IIDX_LOAD:
        case DL_TIMERG_IIDX_CC0_DN:
        case DL_TIMERG_IIDX_CC1_DN:
        case DL_TIMERG_IIDX_CC0_UP:
        case DL_TIMERG_IIDX_CC1_UP:
        case DL_TIMERG_IIDX_OVERFLOW:
        default:
            break;
    }
}

因为我们设置的定时器的中断周期为10ms,所以这里需要进行累计,达到100次的时候,刚好是1秒的周期。为此需要设置一个变量cnt_10ms,一个用于统计中断次数。里面的而另一个变量curnum是用来设置数码管要显示的数字值的,每经过一次1秒钟,这个数值减1。在主程序中会不断显示这个数值,实现从10 到0 的倒计时。

整个过程就是这样,关于数码管的结构、驱动原理,数码管模块的驱动原理在上一个帖子中已经讲述的很详细了,本帖中就不再叙述了。

主程序代码如下(之前调试用的代码没有删除,被注释掉了):


/*
 * Copyright (c) 2023, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "ti_msp_dl_config.h"
void delay(uint32_t cnt);
void DispNum(unsigned char num, unsigned char pos);
/* This results in approximately 0.5s of delay assuming 32MHz CPU_CLK */
#define DELAY (16000000)   // 500ms
#define ms50 (1600000)   // 50ms
#define s1 (32000000)   // 1s
#define ms1 (320)   // 1ms模式延迟
// 16进制数字显示信史段码(0-9,A-F),段码位=1时,数码管的对应的笔段不会被点亮
unsigned char TAB_SEG[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x77, 0x83, 0xC6, 0xA1, 0x86, 0x8E, 0xFF, 0xBF };
// 数码管模块的显示位置,自左向右
unsigned char TAB_POS[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
// 发送数据给数码管模块用的串行时钟,时钟上升沿有效
#define SCLK_0 DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN)
#define SCLK_1 DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN)
// 使数码管唯一寄存器数据被锁存到存储寄存器用的锁存信号,上升沿有效
#define RCLK_0 DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN)
#define RCLK_1 DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN)
// 发送数据给数码管模块用的串行数据位
#define DIO_0 {DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_3_PIN);}
#define DIO_1 {DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_3_PIN);}
void delay(uint32_t cnt) {
    uint32_t d=cnt;
    while(d--);
}
// 当前计数值
int curnum = 10;
// 10ms的累计计数,为了实现1S的处理
uint32_t cnt_10ms = 10;
/**
 * 显示一位数码管数据
 * chr : 显示的数值字符(‘0’-‘9’,‘A’-‘F’)
 * pos :显示的位置(最左侧开始:0-7, 对应的位置数据:0x80, 0x40,0x20,0x10,0x08,0x04,0x02,0x01)
 * 暂时不考虑小数点的处理。数字后面有小数点的场合,相当于该数字的小数点笔段位清0
 */
void DispNum(unsigned char chr, unsigned char pos) {
    unsigned char i, num;
    // 根据字符,获取对应的段位码数据
    if (chr>=0 && chr<16) {
        num=TAB_SEG[chr];
    } else if (chr>=' ') {
        // 空格,所有笔段都不显示
        num = 0xFF;
    } else if (chr>='-') {
        num = 0xBF;
    } else {
      return;
    }
    // 将段位码数据以串行方式发送给数码管显示模块
    // 每个字符有八个笔段,小数点通常不用
    for(i=0;i<8;i++) {
      // 输出笔段对应的数据位
      if ((num & 0x80)>0) {
          // 笔段数据为1 的,DIO对应GPIO口输出高电平
          DIO_1;
      } else {
          // 笔段数据为0 的,DIO对应GPIO口输出低高电平
          DIO_0;
      }
      // 下一个笔段
      num<<=1;
      // 发出移位用串行时钟上升沿脉冲
      SCLK_0;
      delay_cycles(ms1);
      SCLK_1;
      delay_cycles(ms1);
    }
    // 发送完笔段数据,发送显示位置数据
    num = TAB_POS[pos%8];
    for(i=0;i<8;i++) {
      // 输出对应的数据位
        if ((num & 0x80)>0) {
          DIO_1;
      } else {
          DIO_0;
      }
      // 下一个数码管位置
      num<<=1;
      // 发出移位用串行时钟上升沿脉冲
      SCLK_0;
      delay_cycles(ms1);
      SCLK_1;
      delay_cycles(ms1);
    }
    // 发出锁存信号,点亮数码管
    RCLK_0;
    delay_cycles(ms1);
    RCLK_1;
    delay_cycles(ms1);
}
int main(void) {
    uint32_t i=0, j=0;
    /* Power on GPIO, initialize pins as digital outputs */
    SYSCFG_DL_init();
    // 设置定时器0中断优先级
    NVIC_SetPriority(TIMER_0_INST_INT_IRQN, 1);
    // 允许定时器0中断
    NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
    // 启动定时器计数
    DL_TimerG_startCounter(TIMER_0_INST);
    /* Default: LED1 and LED3 ON, LED2 OFF */
    DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN);
    DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN |
                                        GPIO_LEDS_USER_LED_3_PIN |
                                        GPIO_LEDS_USER_TEST_PIN);
    DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_LED_3_PIN | GPIO_LEDS_USER_TEST_PIN);
    while (1) {
//        // 循环显示10递减到0 的过程
//        for (i=0; i<11; i++) {
//            if (i==0) {
//                // 因为笔段共用,10的显示不同于其他数字,只能通过动态扫描方式显示
//                for (j=0; j<1400; j++) {
//                    DispNum(1, 6);
//                    DispNum(0, 7);
//                }
//            } else {
//                // 显示字符9-0
//                DispNum(10-i, 7);
//                // 保持显示时长为1秒
//                delay_cycles(s1);
//            }
//
//        }
        if (curnum==10) {
            DispNum(1, 6);
            DispNum(0, 7);
        } else {
            DispNum(curnum, 7);
        }
    }
}
// Timer0的中断处理,按照配置,每10ms完成一次中断,累计100次,得到1S的周期
void TIMER_0_INST_IRQHandler(void) {
    switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) {
        case DL_TIMERG_IIDX_ZERO:
            // 定时器0完成一次中断
            cnt_10ms=(cnt_10ms+1)%100;
            if (cnt_10ms==0) {
                //DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_3_PIN);
                // 已经达到1秒钟,计数值减1
                curnum--;
                if (curnum<0) {
                    curnum=10;
                }
            }
            break;
        case DL_TIMERG_IIDX_LOAD:
        case DL_TIMERG_IIDX_CC0_DN:
        case DL_TIMERG_IIDX_CC1_DN:
        case DL_TIMERG_IIDX_CC0_UP:
        case DL_TIMERG_IIDX_CC1_UP:
        case DL_TIMERG_IIDX_OVERFLOW:
        default:
            break;
    }
}

关于中断函数名的问题,见文件“ti_msp_dl_config。h”中的定义

中断函数名称.png运行效果如下:

课程2定时器模式_H265.gif

因为担心数码管模块需要的工作电流比较大,没有敢使用开发板提供的电源,而是用的外部电源。




关键词: MSPM0焕新大作战     课程2     任务1    

专家
2024-04-17 00:22:40     打赏
2楼

感谢楼主分享


专家
2024-04-17 06:12:38     打赏
3楼

来学习一下


高工
2024-04-17 09:11:36     打赏
4楼

数码管供电用的3.3V还是5V?


高工
2024-04-17 16:22:55     打赏
5楼

谢谢分享


专家
2024-04-18 07:17:14     打赏
6楼

数码管供电用的3.3V,5V都可以。


共6条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]