单片机学习小组
直播中

哈哈哈

9年用户 833经验值
擅长:可编程逻辑
私信 关注

如何让IO产生最快的翻转脉冲?

如何让IO产生最快的翻转脉冲?

回帖(1)

刘馨

2022-1-17 10:58:18
需求:IO产生最快的翻转脉冲。

结论:最快速度5MHz.

第一次尝试:

IRC使能16M,不分频。
IO设置为推挽高速输出。
While(1)死循环翻转。
代码如下:

void main(void)
{
    CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);  //16M
    GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
    while(1)
    {
        GPIO_WriteReverse(GPIOD, GPIO_PIN_3);
    }
}
实际波形如下:

结果是只有463KHz,达不到要求。
反思是有一层函数调用的原因,没有及时翻转。
第二次尝试:

把函数去掉直接操作。
代码如下:

void main(void)
{
    CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);  //16M
    GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
    while(1)
    {
        GPIOD->ODR ^= (uint8_t)GPIO_PIN_3;
    }
}
实际波形如下:

结果已经有好转,翻转有1.311MHz。离目标还是有点远。
反思通过结构体访问寄存器,使用的是间接寻址,消耗了时间。
第三次尝试:

直接对硬件地址操作。
代码如下:


void main(void)
{
    CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);  //16M
    GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
    while(1)
    {
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
    }
}
实际波形如下:

结果已经又有了进步,翻转速度是3.143Mhz.还是没有达到理想效果。
反思为什么是高电平比低电平多,是置高然后循环跳转回去指令耗时了。跳转指令有没有办法精简。
第四次尝试:

改循环语句。
代码如下:

void main(void)
{
    CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);  //16M
    GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
    /*
    while(1)
    {
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
    }
    */
    /*
    for(;;)
    {
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
    }
    */
_LoopWrite:
    *((uint8_t *)GPIOD_BaseAddress) = 0;
    *((uint8_t *)GPIOD_BaseAddress) = 0x08;
    goto _LoopWrite;


}
实际汇编翻译效果:


这三种循环汇编跳转都是JRA,所以没有达到提速的效果。尝试失败。
反思是跳转耽误了时间,那尽可能的翻转,少跳转。
第五次尝试:

用空间换速度,多重复几次翻转,然后才循环。
代码如下:


void main(void)
{
    CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);  //16M
    GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
   
    while(1)
    {
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
        *((uint8_t *)GPIOD_BaseAddress) = 0;
        *((uint8_t *)GPIOD_BaseAddress) = 0x08;
    }
}
实际波形图如下:

结果已经又有了进步,翻转速度是5.266Mhz.但是可以看到中间因为循环跳转指令带来的延时非常明显。
反思那足够多的重复指令,然后才接一次循环,就可以无限接近于没有跳转的效果。
尝试的结果是不会。当重复到一定次数,ROM存储空间存在跨页,跳转的时候会编译变长跳转,长跳转的指令周期比当前跳转机器周期长。
如果只是使用IO翻转,最大的速度只能到这么多了。
反思如果是有程序参与导致翻转速度受指令限制,那么硬件自己反应,应该比这个快。
第六次尝试:

使用PWM翻转。
代码如下:


void main(void)
{
    CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);  //16M
    GPIO_Init(GPIOD, GPIO_PIN_3, GPIO_MODE_OUT_PP_HIGH_SLOW);
   
    TIM2_DeInit();
    TIM2_TimeBaseInit(TIM2_PRESCALER_1, 0x0001);
    TIM2_OC2Init(TIM2_OCMODE_TOGGLE, TIM2_OUTPUTSTATE_ENABLE,
    0x0000, TIM2_OCPOLARITY_HIGH);
    TIM2_Cmd(ENABLE);
    while(1)
    {
    }
}
实际波形图如下:

结果是3.943MHz,没有比IO直接翻转来得快。
反思是比较器比较的时候消耗了时间。
结论,IO最大的翻转速度是访问硬件地址直接操作IO。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分