嵌入式計時器簡介
嵌入式計時器是一種在嵌入式系統中用于計時、計數和測量時間間隔的設備。它們通常用于生成精確的延遲、測量輸入信號的頻率或產生PWM信號。嵌入式計時器主要包括硬件計時器(例如定時器/計數器、實時時鐘)和軟件計時器。
嵌入式計時器類型
- 軟件計時器 :軟件計時器是使用軟件代碼實現的計時器,通常通過使用系統時鐘或硬件計時器作為基準。它們相較于硬件計時器在精度上可能略有差距,但便于擴展和靈活控制。
嵌入式計時器應用
- 延時 :嵌入式計時器可以用于產生精確的延遲,例如在執行任務之間的間隔。
- 計數 :計時器可以用于計數操作,例如檢測外部事件發生的次數。
- 頻率測量 :計時器可以用于測量外部信號的頻率,例如捕獲輸入信號的上升沿和下降沿。
- 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信號的占空比等)...
}
}
硬件實現和軟件實現在嵌入式系統中通常涉及到相同的功能,但它們的實現方式和性能有所不同。以下是硬件實現與軟件實現的主要區別:
- 資源占用:硬件實現通常利用專用的外設(如定時器、PWM模塊等)來實現功能。這些外設通常能夠在微控制器的內部并行工作,不占用CPU的計算資源。而軟件實現主要依賴于CPU執行代碼,因此會占用更多的CPU資源。
- 精度和穩定性:硬件實現通常具有更高的精度和穩定性,因為它們是由專門設計的電路實現的,不受CPU負載、中斷等因素的影響。軟件實現的精度和穩定性較低,因為它們受到CPU負載、中斷處理和其他任務的影響。
- 可配置性和靈活性:硬件實現的功能通常具有較低的可配置性和靈活性,因為它們是由專用的硬件電路實現的,這些電路通常只能在一定范圍內進行配置。軟件實現具有更高的可配置性和靈活性,因為它們是由程序代碼實現的,可以根據需求進行修改。
- 實現難度:硬件實現通常需要對微控制器的外設有較深入的了解,編程難度相對較高。軟件實現相對簡單,不需要對硬件電路有深入的了解。
- 開發和維護成本:硬件實現通常具有較高的開發和維護成本,因為它們需要專門的硬件電路和外設。軟件實現的成本較低,因為它們只需要編寫代碼。
說了這么多,其實定時器的本質是一種特殊的計數器,它能夠按照預定的頻率自動遞增(或遞減)計數值。定時器在達到預設的計數值時會觸發事件,例如產生中斷、翻轉輸出引腳狀態等。定時器是微控制器內部的一個硬件模塊,可以獨立于CPU運行,實現精確的時間間隔和事件觸發。
定時器的工作原理如下:
- 時鐘源:定時器需要一個穩定的時鐘源作為基準,時鐘源可以是內部的系統時鐘,也可以是外部提供的時鐘信號。時鐘源的頻率決定了定時器的最高計數速率。
- 預分頻器:預分頻器用于降低時鐘源的頻率,從而實現較低的計數速率。預分頻器可以根據需要進行配置。
- 計數器:計數器是定時器的核心部分,它按照預分頻后的時鐘頻率進行計數。計數器可以配置為向上計數或向下計數。
- 自動重載值:當計數器達到設定的自動重載值時,定時器會觸發事件,例如產生中斷、翻轉輸出引腳狀態等。此外,計數器會根據配置自動回到初始值,重新開始計數。
- 輸出比較、捕獲等功能:許多定時器還具有輸出比較、輸入捕獲等功能,用于生成PWM信號、測量外部信號的周期等。
定時器在嵌入式系統中有許多應用,如實現精確延時、周期性任務調度、PWM信號生成、脈沖寬度測量等。定時器提供了一種高精度、低資源占用的時間控制手段,可以有效地支持復雜的嵌入式應用。
Simulink實現
既然定時器的本質是一個累加邏輯,那接下來我們嘗試在simulink中用多種方法實現定時器功能,例如當某個功能觸發時開始計時,功能停止時計時保持不變,當計時累計達到某個設定值時觸發另一個事件執行。(夏季空調內循環制冷,計時20min后開啟30s外循環換氣)
Matlab Function的實現方式
MATLAB Function 模塊可以幫助我們在Simulink 模型中實現MATLAB函數的功能,也可以生成可讀、高效、緊湊的 C代碼,應用于嵌入式系統中。
圖中u2模擬計時器的條件,我們設定為周期為5,占空比為50%的方波信號,t2等于Simulnk模型的仿真步長,x是上一時刻的計時數值。MATLAB Function中的代碼如下:
function y = fcn(u,t,x)
if u == 1
x = x + t;
end
y = x;
配置參數
運行結果
Simulink基礎模塊的實現方式
使用基礎模塊搭建的Simulink模型如下圖,主要借助switch和add來實現。
S****tateflow狀態機的實現方式
計時器功能也可以分為兩個狀態,即累加狀態和保持狀態,所以也可以使用Stateflow來實現。
chart內部如下
Stateflow流程圖的實現方式
在Simulink軟件開發過程中,對于比較簡單的Stateflow邏輯,尤其是條件選擇邏輯,可以使用流程圖代替狀態圖,以簡化邏輯。
chart內部如下
simulink中還有一些和計數相關的模塊如下
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端口時,模塊復位,計數器恢復到初始設定狀態。
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模型如下:
評論
查看更多