嵌入式计时器以及simulink设计实现

嵌入式技术

1330人已加入

描述

嵌入式计时器简介

嵌入式计时器是一种在嵌入式系统中用于计时、计数和测量时间间隔的设备。它们通常用于生成精确的延迟、测量输入信号的频率或产生PWM信号。嵌入式计时器主要包括硬件计时器(例如定时器/计数器、实时时钟)和软件计时器。

嵌入式计时器类型

    1. 硬件计时器 :硬件计时器通常集成在微控制器或处理器中,包括定时器/计数器、实时时钟(RTC)等。它们可以精确地计算时间间隔,适用于对时间精度要求较高的场景。
    1. 软件计时器 :软件计时器是使用软件代码实现的计时器,通常通过使用系统时钟或硬件计时器作为基准。它们相较于硬件计时器在精度上可能略有差距,但便于扩展和灵活控制。

嵌入式计时器应用

    1. 延时 :嵌入式计时器可以用于产生精确的延迟,例如在执行任务之间的间隔。
    1. 计数 :计时器可以用于计数操作,例如检测外部事件发生的次数。
    1. 频率测量 :计时器可以用于测量外部信号的频率,例如捕获输入信号的上升沿和下降沿。
    1. PWM信号生成 :嵌入式计时器可以用于生成PWM信号,用于控制电机、LED等设备。

示例代码(以STM32为例)

使用STM32F10x微控制器实现延时功能的示例代码( 硬件实现 )。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"

// 配置TIM2以生成时间基准
void TIM2_Configuration(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    // 使能TIM2时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // 配置TIM2基本参数
    TIM_TimeBaseStructure.TIM_Period = 999; // 设置计数器自动重装值
    TIM_TimeBaseStructure.TIM_Prescaler = 71; // 设置预分频
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    // 使能TIM2
    TIM_Cmd(TIM2, ENABLE);
}

void delay_ms(uint16_t ms)
{
    uint16_t i;
    for (i = 0; i < ms; i++)
    {
        TIM_SetCounter(TIM2, 0);
        while (TIM_GetCounter(TIM2) < 1000)
        {
        }
    }
}

int main(void)
{
    // 初始化系统配置
    SystemInit();

    // 配置TIM2以生成时间基准
    TIM2_Configuration();

    while (1)
    {
        // 延时1000毫秒
        delay_ms(1000);

        // ...其他代码(可以根据需要执行一些动作)...
    }
}

使用STM32F10x微控制器实现延时功能的示例代码( 软件实现 )。。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"

// 获取系统时钟
uint32_t SystemCoreClock;

// 延时微秒
void delay_us(uint32_t us)
{
    uint32_t i, j;
    uint32_t count = (SystemCoreClock / 8000000) * us;

    for (i = 0; i < count; i++)
    {
        for (j = 0; j < 2; j++)
        {
            __NOP(); // No Operation指令,用于延时
        }
    }
}

// 延时毫秒
void delay_ms(uint32_t ms)
{
    uint32_t i;
    for (i = 0; i < ms; i++)
    {
        delay_us(1000);
    }
}

int main(void)
{
    // 初始化系统配置
    SystemInit();

    // 获取系统时钟
    SystemCoreClock = SystemCoreClock;

    while (1)
    {
        // 延时1000毫秒
        delay_ms(1000);

        // ...其他代码(可以根据需要执行一些动作)...
    }
}

使用STM32F10x微控制器的计数器功能的示例代码( 硬件实现 )。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"

void TIM2_Counter_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;

    // 使能TIM2和GPIOA时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 配置GPIOA0作为输入,上拉
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置TIM2基本参数
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    // 配置TIM2的输入捕获
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕获
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0x0;
    TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);

    // 使能TIM2
    TIM_Cmd(TIM2, ENABLE);
}

int main(void)
{
    uint16_t counter_value;

    TIM2_Counter_Config(); // 配置TIM2计数器

    while (1)
    {
        counter_value = TIM_GetCounter(TIM2); // 读取TIM2计数器的值

        // ...其他代码(可以根据需要处理计数值)...
    }
}

使用STM32F10x微控制器的计数器功能的示例代码( 软件实现 )。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_exti.h"
#include "misc.h"

volatile uint32_t pulseCounter = 0;

void EXTI0_Configuration(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 配置GPIOA0作为输入,上拉
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 使能AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    // 配置GPIOA0为外部中断源
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

    // 配置EXTI线0
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    // 配置NVIC
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

void EXTI0_IRQHandler(void)
{
    if (EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        pulseCounter++; // 增加脉冲计数值

        EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志位
    }
}

int main(void)
{
    uint32_t counter_value;

    EXTI0_Configuration(); // 配置外部中断(EXTI)

    while (1)
    {
        counter_value = pulseCounter; // 读取脉冲计数值

        // ...其他代码(可以根据需要处理计数值)...
    }
}

使用STM32F10x微控制器生成PWM信号的示例代码( 硬件实现 )。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"

// 配置TIM3以生成PWM信号
void TIM3_PWM_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    // 使能TIM3和GPIOB时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // 配置GPIOB5为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 配置TIM3基本参数
    TIM_TimeBaseStructure.TIM_Period = 19999; // 设置计数器自动重装值
    TIM_TimeBaseStructure.TIM_Prescaler = 71; // 设置预分频
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    // 配置TIM3通道2的PWM输出
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 10000; // 初始占空比设置为50%
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

    // 使能TIM3
    TIM_Cmd(TIM3, ENABLE);
}

int main(void)
{
    // 配置TIM3以生成PWM信号
    TIM3_PWM_Configuration();

    while (1)
    {
        // ...其他代码(可以根据需要调整PWM信号的占空比等)...
    }
}

使用STM32F10x微控制器生成PWM信号的示例代码( 软件实现 )。

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"

// 配置TIM2以生成时间基准
void TIM2_Configuration(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    // 使能TIM2时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    // 配置TIM2基本参数
    TIM_TimeBaseStructure.TIM_Period = 999; // 设置计数器自动重装值
    TIM_TimeBaseStructure.TIM_Prescaler = 71; // 设置预分频
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    // 使能TIM2
    TIM_Cmd(TIM2, ENABLE);
}

void delay_us(uint16_t us)
{
    uint16_t i;
    for (i = 0; i < us; i++)
    {
        TIM_SetCounter(TIM2, 0);
        while (TIM_GetCounter(TIM2) < 1)
        {
        }
    }
}

// 配置GPIOB5输出
void GPIOB5_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能GPIOB时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // 配置GPIOB5为推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

void Software_PWM(uint16_t onTime, uint16_t offTime)
{
    GPIO_SetBits(GPIOB, GPIO_Pin_5);
    delay_us(onTime);
    GPIO_ResetBits(GPIOB, GPIO_Pin_5);
    delay_us(offTime);
}

int main(void)
{
    uint16_t onTime = 5000;   // 设定PWM信号的占空比时间 (单位:微秒)
    uint16_t offTime = 5000;  // 设定PWM信号的空闲时间 (单位:微秒)

    // 配置TIM2以生成时间基准
    TIM2_Configuration();

    // 配置GPIOB5输出
    GPIOB5_Configuration();

    while (1)
    {
        // 使用软件PWM模拟信号输出
        Software_PWM(onTime, offTime);

        // ...其他代码(可以根据需要调整PWM信号的占空比等)...
    }
}

硬件实现和软件实现在嵌入式系统中通常涉及到相同的功能,但它们的实现方式和性能有所不同。以下是硬件实现与软件实现的主要区别:

    1. 资源占用:硬件实现通常利用专用的外设(如定时器、PWM模块等)来实现功能。这些外设通常能够在微控制器的内部并行工作,不占用CPU的计算资源。而软件实现主要依赖于CPU执行代码,因此会占用更多的CPU资源。
    1. 精度和稳定性:硬件实现通常具有更高的精度和稳定性,因为它们是由专门设计的电路实现的,不受CPU负载、中断等因素的影响。软件实现的精度和稳定性较低,因为它们受到CPU负载、中断处理和其他任务的影响。
    1. 可配置性和灵活性:硬件实现的功能通常具有较低的可配置性和灵活性,因为它们是由专用的硬件电路实现的,这些电路通常只能在一定范围内进行配置。软件实现具有更高的可配置性和灵活性,因为它们是由程序代码实现的,可以根据需求进行修改。
    1. 实现难度:硬件实现通常需要对微控制器的外设有较深入的了解,编程难度相对较高。软件实现相对简单,不需要对硬件电路有深入的了解。
    1. 开发和维护成本:硬件实现通常具有较高的开发和维护成本,因为它们需要专门的硬件电路和外设。软件实现的成本较低,因为它们只需要编写代码。

说了这么多,其实定时器的本质是一种特殊的计数器,它能够按照预定的频率自动递增(或递减)计数值。定时器在达到预设的计数值时会触发事件,例如产生中断、翻转输出引脚状态等。定时器是微控制器内部的一个硬件模块,可以独立于CPU运行,实现精确的时间间隔和事件触发。

定时器的工作原理如下:

    1. 时钟源:定时器需要一个稳定的时钟源作为基准,时钟源可以是内部的系统时钟,也可以是外部提供的时钟信号。时钟源的频率决定了定时器的最高计数速率。
    1. 预分频器:预分频器用于降低时钟源的频率,从而实现较低的计数速率。预分频器可以根据需要进行配置。
    1. 计数器:计数器是定时器的核心部分,它按照预分频后的时钟频率进行计数。计数器可以配置为向上计数或向下计数。
    1. 自动重载值:当计数器达到设定的自动重载值时,定时器会触发事件,例如产生中断、翻转输出引脚状态等。此外,计数器会根据配置自动回到初始值,重新开始计数。
    1. 输出比较、捕获等功能:许多定时器还具有输出比较、输入捕获等功能,用于生成PWM信号、测量外部信号的周期等。

定时器在嵌入式系统中有许多应用,如实现精确延时、周期性任务调度、PWM信号生成、脉冲宽度测量等。定时器提供了一种高精度、低资源占用的时间控制手段,可以有效地支持复杂的嵌入式应用。

Simulink实现

既然定时器的本质是一个累加逻辑,那接下来我们尝试在simulink中用多种方法实现定时器功能,例如当某个功能触发时开始计时,功能停止时计时保持不变,当计时累计达到某个设定值时触发另一个事件执行。(夏季空调内循环制冷,计时20min后开启30s外循环换气)

Matlab Function的实现方式

MATLAB Function 模块可以帮助我们在Simulink 模型中实现MATLAB函数的功能,也可以生成可读、高效、紧凑的 C代码,应用于嵌入式系统中。

PWM信号

图中u2模拟计时器的条件,我们设定为周期为5,占空比为50%的方波信号,t2等于Simulnk模型的仿真步长,x是上一时刻的计时数值。MATLAB Function中的代码如下:

function y = fcn(u,t,x)

if u == 1

x = x + t;

end

y = x;

配置参数

PWM信号

运行结果

PWM信号

Simulink基础模块的实现方式

使用基础模块搭建的Simulink模型如下图,主要借助switch和add来实现。

PWM信号

S****tateflow状态机的实现方式

计时器功能也可以分为两个状态,即累加状态和保持状态,所以也可以使用Stateflow来实现。

PWM信号

chart内部如下

PWM信号

Stateflow流程图的实现方式

在Simulink软件开发过程中,对于比较简单的Stateflow逻辑,尤其是条件选择逻辑,可以使用流程图代替状态图,以简化逻辑。

PWM信号

chart内部如下

PWM信号

simulink中还有一些和计数相关的模块如下

PWM信号

1. Counter Free-Running

自由计数器(Counter Free-Running)是一种无限制的计数器,可以无限地递增或递减。它通常用于计算脉冲信号的数量、测量时间等。在 Simulink 中,您可以使用Discrete库中的Counter Free-Running模块来创建自由计数器。

2. Counter

Conter模块可以通过指定的数据范围实现向上计数或向下计数。当选择Count direction参数为向上计数时,模块将使能 Inc (增量)端口;当选择Count direction参数为向下计数时,模块将使能 Dec (减量)端口。如果Count event参数为Free running,模块将禁用Inc或Dec端口,并且使用固定时间间隔进行计数。针对于Count event参数的所有其他设定,每当在Inc或Dec输入端口发生触发事件时,模块都会使计数器递增或递减。当触发事件发生在Rst端口时,模块复位,计数器恢复到初始设定状态。

PWM信号

3. Counter Limited

限制计数器(Counter Limited)是一种具有上限或下限的计数器。当计数器达到预设的限制值时,它会回到初始值重新开始计数。您可以使用Simulink > Discrete库中的Counter Limited模块来创建限制计数器。

让我们重新回到最开始的例子:夏季空调内循环制冷,计时20min后开启30s外循环换气,这个例子包含两个计时器和两次事件触发和状态切换,我们试着用simulink的基础模块实现此功能。

C代码如下:

#include < stdint.h >
#include < stdbool.h >
#include "stm32f10x.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_rcc.h"

// Define constants for internal and external circulation times in milliseconds
#define INTERNAL_CIRCULATION_TIME (20 * 60 * 1000) // 20 minutes
#define EXTERNAL_CIRCULATION_TIME (30 * 1000) // 30 seconds

// Function prototypes for internal and external circulation control
void set_internal_circulation();
void set_external_circulation();

// Function prototype for millisecond delay
void delay_ms(uint32_t ms);

int main(void)
{
    uint32_t internal_timer = 0; // Timer to track the time spent in internal circulation mode
    uint32_t external_timer = 0; // Timer to track the time spent in external circulation mode
    bool is_internal_circulation = true; // Flag to indicate whether the current mode is internal circulation

    // Set the initial mode to internal circulation
    set_internal_circulation();

    // Main loop
    while (1)
    {
        delay_ms(1); // 1 millisecond delay

        // If the current mode is internal circulation
        if (is_internal_circulation)
        {
            internal_timer += 1; // Increment the internal timer

            // If the internal timer reaches the predefined internal circulation time
            if (internal_timer >= INTERNAL_CIRCULATION_TIME)
            {
                // Switch to external circulation mode
                set_external_circulation();
                is_internal_circulation = false;
                // Reset the internal timer
                internal_timer = 0;
            }
        }
        else // If the current mode is external circulation
        {
            external_timer += 1; // Increment the external timer

            // If the external timer reaches the predefined external circulation time
            if (external_timer >= EXTERNAL_CIRCULATION_TIME)
            {
                // Switch to internal circulation mode
                set_internal_circulation();
                is_internal_circulation = true;
                // Reset the external timer
                external_timer = 0;
            }
        }
    }

    return 0;
}

// Function to set internal circulation mode
void set_internal_circulation()
{
    // Implement your logic to set internal circulation here
}

// Function to set external circulation mode
void set_external_circulation()
{
    // Implement your logic to set external circulation here
}

// Function to provide a millisecond delay
void delay_ms(uint32_t ms)
{
    // Implement a software delay function here, or use the provided hardware timer delay function
}

simulink模型如下:

PWM信号

PWM信号

PWM信号

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分