設置 | 登錄 | 註冊

目前共有19篇帖子。

【实验】使用定时器的外部时钟模式对多谐振荡器的性能进行评估

1樓 巨大八爪鱼 2017-4-2 11:59
【实验时用到的程序】
#include <stm32f10x.h>

const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};

void delay(void)
{
    uint16_t i;
    for (i = 0; i < 20000; i++);
}

void ser_in(uint8_t data)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        GPIO_ResetBits(GPIOB, GPIO_Pin_9); // SCLK=>PB9
        if (data & 0x80)
            GPIO_SetBits(GPIOB, GPIO_Pin_7); // DIO=>PB7
        else
            GPIO_ResetBits(GPIOB, GPIO_Pin_7);
        data <<= 1;
        GPIO_SetBits(GPIOB, GPIO_Pin_9);
    }
}

void par_out(void)
{
    GPIO_ResetBits(GPIOB, GPIO_Pin_8); // RCLK=>PB8
    GPIO_SetBits(GPIOB, GPIO_Pin_8);
}

void seg_scan(void)
{
    uint8_t numbuf = TIM_GetCounter(TIM2);
    uint8_t i;
    for (i = 0; i < 2; i++) // 只显示两位数字
    {
        ser_in(seg8[numbuf % 10]);
        ser_in(1 << i);
        par_out();
        delay();
        numbuf /= 10;
    }
}

int main(void)
{
    GPIO_InitTypeDef gpio;
    TIM_ICInitTypeDef timic;
    TIM_TimeBaseInitTypeDef tim;
   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
   
    // 时钟输入引脚配置
    gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpio.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOA, &gpio);
   
    // 数码管扫描管脚配置
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio);
   
    // 配置定时器的计数范围
    TIM_TimeBaseStructInit(&tim);
    tim.TIM_CounterMode = TIM_CounterMode_Down; // 向下计数
    tim.TIM_Period = 99; // 最大计数值
    tim.TIM_Prescaler = 0; // 不分频
    TIM_TimeBaseInit(TIM2, &tim);
   
    // 配置定时器2的通道1
    timic.TIM_Channel = TIM_Channel_1;
    timic.TIM_ICFilter = 0; // 不消抖
    timic.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿触发 (注意: 不能设为TIM_ICPolarity_BothEdge, 根本就没有双边沿检测功能)
    //timic.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 这个选项在外部时钟模式中无效(输入捕获模式才会用到)
    timic.TIM_ICSelection = TIM_ICSelection_DirectTI; // TI1->IC1=PA0
    TIM_ICInit(TIM2, &timic);
   
    // 外部时钟模式1
    TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); // TRGI=TI1
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_External1); // 把时钟信号设为TRGI
    TIM_Cmd(TIM2, ENABLE);
   
    while (1)
        seg_scan();
}
2樓 巨大八爪鱼 2017-4-2 12:55
【多谐振荡器的电路配置】
充电端接200Ω电阻和一个LED灯(电阻的大小决定输出端上升沿的跳变时间)。
放电端接100kΩ电阻(电阻大小决定输出端脉冲的宽度)。
这里要注意的是,放电电阻不可以接的太大(如200kΩ),否则三极管会处于放大状态,不仅使输出的低电平电压升高(如升高到2V甚至更高,单片机端将无法正确地检测出输出电平),而且很可能使整个多谐振荡器不能正常工作,甚至无法起振。
电容器为10μF。电容器靠近LED的那一端为正极,接三极管基极的那一端为负极。

当右输出端接地时,两个LED灯都亮,用万用表测得左输出端(三极管的集电极)的电压为0.97V(输出的低电平电压)。这表明三极管导通时处于放大状态,不过这并没有什么影响。
现在悬空右输出端,将左输出端接到单片机的PA0口上(定时器2的输入通道1)。

当左LED灯熄灭时,左电容器通过充电小电阻快速充电,输出端迅速从低电平(0.97V)升高到高电平(用万用表测出来大约是3.4V,理论上是4.3V),并保持在高电平,直到右电容器放电完毕。
充电时间 = - 充电电阻大小 x 电容大小 x ln[(E - 末电压) / (E - 初电压)]
其中E为最大充电电压,由于充电时电容器和充电电阻并联在放电电阻两端,而放电电阻又和右三极管的发射结是串联的,发射结的导通电压是0.7V,因此实际上电容器最大只能充到E=5V-0.7V=4.3V
因此上升沿时间 = - 200 * 10 * 10^-6 * ln[(4.3-4.29) / (4.3-(-0.7))] (初电压是-0.7V,因为放电完毕时电容器被反向充电到了0.7V,现在充电前必须先释放掉这部分电压)
≈ 12.43ms

当左LED灯亮起时,由于左三极管导通,左电容器的正极外接电压<负极外接电压,而正极内部电压>负极内部电压(因为之前已经充好电了),所以此时电源对该电容器进行反向充电(也就是放电),先从4.3V放到0V,再充到0.7V使右三极管导通,可以看作是从-4.3V充电到0.7V。这段时间左输出端的电压一直为0.97V,输出低电平。右三极管的基极电压从负压(-4.3V)变到正压(0.7V)。
持续的时间 = - 放电电阻大小 x 电容大小 x ln[(E - 末电压) / (E - 初电压)]
= - 100 * 10^3 * 10 * 10^-6 * ln[(E - (+0.7))/(E - (-4.3))] (E=5V)
≈ 0.77s
(计算时假定的是三极管是饱和导通。因为现在三极管是处于放大状态,所以实际时间比这个时间要短。)

因此,左输出端的上升沿出现在左LED灯熄灭的瞬间(电容器充电的速度非常快),程序中的定时器2就是对上升沿进行计数。
3樓 巨大八爪鱼 2017-4-2 13:01
编译并下载单片机程序,实验发现,虽然在程序中没有开通道1的消抖功能,并且GPIO配置的是浮空输入,但数码管的数字显示很稳定,仅当左LED灯熄灭时才减1(向下计数)。
把左LED灯短路,计时速度明显加快,并且数码管有时减2有时减1,振荡变得不稳定。
如果把两个LED灯都短路,则数码管每次减去的数毫无规律,抖动非常厉害。
4樓 巨大八爪鱼 2017-4-2 13:05

以下是多谐振荡器的电路图。

本实验把充电电阻R1和R4的阻值大小改成了200Ω,同时串联了LED灯。

6樓 巨大八爪鱼 2017-4-2 13:07
在本次实验中,Q1、Q2采用的三极管型号是9013NPN型。
7樓 巨大八爪鱼 2017-4-2 13:13
库函数中有一个TIxExternalClockConfig函数可以更简单地设置外部时钟模式1:
TIM_TIxExternalClockConfig(TIM2, TIM_TIxExternalCLK1Source_TI1, TIM_ICPolarity_Rising, 0);
【示例程序】
int main(void)
{
    GPIO_InitTypeDef gpio;
    TIM_TimeBaseInitTypeDef tim;
   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
   
    // 时钟输入引脚配置
    gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpio.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOA, &gpio);
   
    // 数码管扫描管脚配置
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio);
   
    // 配置定时器的计数范围
    TIM_TimeBaseStructInit(&tim);
    tim.TIM_CounterMode = TIM_CounterMode_Down; // 向下计数
    tim.TIM_Period = 99; // 最大计数值
    tim.TIM_Prescaler = 0; // 不分频
    TIM_TimeBaseInit(TIM2, &tim);
   
    // 外部时钟模式1
    TIM_TIxExternalClockConfig(TIM2, TIM_TIxExternalCLK1Source_TI1, TIM_ICPolarity_Rising, 0);
    TIM_Cmd(TIM2, ENABLE);
   
    while (1)
        seg_scan();
}
8樓 巨大八爪鱼 2017-4-2 13:24
TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0);
这个函数也是配置的是模式1,但是使用的引脚却是ETR引脚,即TRGI=ETR。
而之前的TIM_TIxExternalClockConfig函数使用的是TI1引脚,即TRGI=TI1FP1。
定时器2的ETR引脚和TI1引脚都是PA0,但对于定时器1来说两个引脚是分开的。
9樓 巨大八爪鱼 2017-4-2 13:26
// 外部时钟模式2
// 使用ETR (External Trigger)引脚作为外部时钟信号
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0);
TIM_Cmd(TIM2, ENABLE);
10樓 巨大八爪鱼 2017-4-2 14:16
现在多谐振荡器又出现了不稳定情况:有时候数码管减的数字是2
11樓 巨大八爪鱼 2017-4-2 14:39
【实验2:使用STM32的PWM输入模式测量多谐振荡器的脉冲宽度】
#include <stm32f10x.h>

const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};

void delay(void)
{
    uint16_t i;
    for (i = 0; i < 20000; i++);
}

void ser_in(uint8_t data)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        GPIO_ResetBits(GPIOB, GPIO_Pin_9); // SCLK=>PB9
        if (data & 0x80)
            GPIO_SetBits(GPIOB, GPIO_Pin_7); // DIO=>PB7
        else
            GPIO_ResetBits(GPIOB, GPIO_Pin_7);
        data <<= 1;
        GPIO_SetBits(GPIOB, GPIO_Pin_9);
    }
}

void par_out(void)
{
    GPIO_ResetBits(GPIOB, GPIO_Pin_8); // RCLK=>PB8
    GPIO_SetBits(GPIOB, GPIO_Pin_8);
}

void seg_scan(void)
{
    uint16_t numbuf;
    uint8_t i;
    if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1) == SET)
        numbuf = TIM_GetCapture1(TIM2); // 按键松开时显示上次脉冲的总长度(单位: 秒)
    else
        numbuf = TIM_GetCapture2(TIM2); // 按键按下时显示高电平的长度(单位: 秒)
    for (i = 0; i < 5; i++)
    {
        if (i == 4)
            ser_in(seg8[numbuf % 10] & 0x7f); // 显示小数点
        else
            ser_in(seg8[numbuf % 10]);
        ser_in(1 << i);
        par_out();
        delay();
        numbuf /= 10;
    }
}

int main(void)
{
    GPIO_InitTypeDef gpio;
    TIM_ICInitTypeDef timic;
    TIM_TimeBaseInitTypeDef tim;
   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
   
    // 时钟输入引脚配置
    gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    gpio.GPIO_Pin = GPIO_Pin_0;
    GPIO_Init(GPIOA, &gpio);
   
    // 数码管扫描管脚配置
    gpio.GPIO_Mode = GPIO_Mode_Out_PP;
    gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio);
   
    // 按键管脚PC1配置
    gpio.GPIO_Mode = GPIO_Mode_IPU;
    gpio.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOC, &gpio);
   
    // 配置定时器的计数范围
    TIM_TimeBaseStructInit(&tim);
    tim.TIM_CounterMode = TIM_CounterMode_Up;
    tim.TIM_Period = 0xffff; // 最大计数值
    tim.TIM_Prescaler = 7199; // 7200分频, 得到基准时钟0.1ms
    TIM_TimeBaseInit(TIM2, &tim);
   
    // 配置定时器2的通道1
    timic.TIM_Channel = TIM_Channel_1;
    timic.TIM_ICFilter = 0; // 不消抖
    timic.TIM_ICPolarity = TIM_ICPolarity_Rising; // 通道1捕获上升沿
    timic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    timic.TIM_ICSelection = TIM_ICSelection_DirectTI; // TI1->IC1=PA0
    TIM_ICInit(TIM2, &timic);
   
    // 配置定时器2的通道2
    timic.TIM_Channel = TIM_Channel_2;
    timic.TIM_ICFilter = 0;
    timic.TIM_ICPolarity = TIM_ICPolarity_Falling; // 通道2捕获下降沿
    timic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    timic.TIM_ICSelection = TIM_ICSelection_IndirectTI; // TI2->IC1=PA0
    TIM_ICInit(TIM2, &timic);
   
    TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); // TRGI=TI1
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); // 上升沿使定时器归0
    TIM_Cmd(TIM2, ENABLE);
   
    while (1)
        seg_scan();
}

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。