嵌入式技术论坛
直播中

h1654155272.9717

8年用户 1261经验值
擅长:电源/新能源
私信 关注
[问答]

如何利用硬件定时器超时时间判断串口接收一帧结束呢

我的思路是,用标准库的时候,定时器向上计数,串口接收到数据后,setcounter为0,定时器的计数器从头开始计数,这样当接收数据的时候,定时器不会中断。当接完一帧数据,不会给setcounter设置为0了,定时器一直计数,就会产生中断,此时处理数据,关闭定时器。

但是现在硬件定时器框架只有write control,没有控制定时器计数器的接口,想实现类似的功能应该怎么写呢?谢谢。

回帖(11)

juju宇哥

2022-8-1 10:34:03
串口判断一帧结束应该是使用类似空闲中断的原理,比如说串口的通讯参数为 9600bps 8n1,那么一个字节的时间大约为 1ms,假设连续几个毫秒都没有收到新的数据,那么就认为这一帧数据结束了。
因此在使用定时器的时候可以每收到一个字节的数据将定时器清0,然后如果定时器的超时时间到了就认为该数据帧结束了。
举报

h1654155272.9717

2022-8-1 10:34:20
怎么把定时器清零呢?rtt提供的只有write开始,control啥的。然后我不是一个字节一个字节算的,因为DMA接收会把数据断开,用定时器是为了把DMA接收断开的帧,再拼上。
我是在while里接收一部分数据,比如A0个字节,write定时器,这样有一个超时时间,设为100ms,如果超时了,说明100ms内,没有进while函数,说明一帧接收完成。
超时函数的回调函数给一个标志位,然后读取数据,stop定时器。
如果还没超时,又收到数据,那就再write,不知道可不可以在没stop的情况下write定时器,stop定时器,应该可以把定时器清空了吧。

现在单步调试的时候,每一帧都能分开,全速跑起来,就分不开了。
每两条数据的时间间隔好几s,绝对比100ms大。
代码如下:

void read_uart(void *parameter)
{
    rt_uint32_t recv_cnt = 0, old_recv_cnt = 0;
    rt_uint8_t     recv_buff[4096] = {0};
    rt_uint8_t    *tmp_ptr= recv_buff;
    rt_uint16_t cnt = 0;//统计一次接收读取几次,可以知道一帧数据被dma分成几次
    rt_hwtimerval_t timeout_s;      /* 定时器超时值 */
    timeout_s.sec = 0;      /* 秒 */
    timeout_s.usec = 100000;     /* 微秒  250ms */
       hwtimer_sample();
    while(1)
    {
        if (rt_sem_take(&env_rx,500) != RT_EOK)
        {
            continue;
        }
        //循环从串口读数据,放到tmpptr里,然后tmpptr每次增加接收的字符数,避免覆盖
        while (recv_cnt = rt_device_read(dev, -1, tmp_ptr, 4096)  )
        {
            rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) ;
            cnt += recv_cnt;
            tmp_ptr += recv_cnt;
            old_recv_cnt = recv_cnt;
            // rt_kprintf("第 %d 次",cnt);
        }
//得到一帧数据,stop定时器,处理数据
        if (timeout == 1 && old_recv_cnt != 0 && recv_cnt == 0)
        {
            timeout = 0;
            old_recv_cnt = 0;
            tmp_ptr = recv_buff;
            rt_device_control(hw_dev, HWTIMER_CTRL_STOP, RT_NULL);
            dump_hex(recv_buff, cnt);
            cnt = 0;
        }
        rt_thread_delay(50);
    }
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
    // rt_kprintf("this is hwtimer timeout callback fucntion!n");
     rt_kprintf("tick is :%d !n", rt_tick_get());
    timeout = 1;
    return 0;
}
07AA是结束符,没有区分开
1.jpg
举报

juju宇哥

2022-8-1 10:34:27
直接使用 DMA + 空闲中断 也可以作为数据帧结束的判断,还能省去定时器
举报

juju宇哥

2022-8-1 10:34:33
我还没用过硬件定时器,用的都是软件定时器,你看看 HWTIMER_CTRL_STOP 停止定时器会把定时器清 0 吗?
举报

juju宇哥

2022-8-1 10:34:43
看了一下源码,下面这个函数可以清 0 定时器

static void timer_stop(rt_hwtimer_t *timer)
{
    TIM_HandleTypeDef *tim = RT_NULL;
    RT_ASSERT(timer != RT_NULL);
    tim = (TIM_HandleTypeDef *)timer->parent.user_data;
    /* stop timer */
    HAL_TIM_Base_Stop_IT(tim);
    /* set tim cnt */
    __HAL_TIM_SET_COUNTER(tim, 0);
}
static const struct rt_hwtimer_ops _ops =
{
    .init = timer_init,
    .start = timer_start,
    .stop = timer_stop,
    .count_get = timer_counter_get,
    .control = timer_ctrl,
};
举报

h1654155272.9717

2022-8-1 10:34:51
没有,开启dma接收模式,就是dma半满 全满 ,串口空闲中断了,这时候接收的数据会混在一起,分不清帧。
举报

juju宇哥

2022-8-1 10:34:58
你看看上面的 timer_stop() 函数,我看里面实现了定时器清 0 的功能。
举报

h1654155272.9717

2022-8-1 10:35:04
这个调用control可能调用了。你看我的思路有问题吗?
举报

juju宇哥

2022-8-1 10:35:14
调用下面这个代码 rt_device_control(hw_dev, HWTIMER_CTRL_STOP, RT_NULL); 应该是不能停止定时器的,我看这部分的源代码如下所示,这里面只实现了 HWTIMER_CTRL_FREQ_SET 这个指令。

static rt_err_t timer_ctrl(rt_hwtimer_t *timer, rt_uint32_t cmd, void *arg)
{
    TIM_HandleTypeDef *tim = RT_NULL;
    rt_err_t result = RT_EOK;
    RT_ASSERT(timer != RT_NULL);
    RT_ASSERT(arg != RT_NULL);
    tim = (TIM_HandleTypeDef *)timer->parent.user_data;
    switch (cmd)
    {
    case HWTIMER_CTRL_FREQ_SET:
    {
        rt_uint32_t freq;
        rt_uint16_t val;
        /* set timer frequence */
        freq = *((rt_uint32_t *)arg);
#if defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7)
        if (tim->Instance == TIM9 || tim->Instance == TIM10 || tim->Instance == TIM11)
#elif defined(SOC_SERIES_STM32L4)
        if (tim->Instance == TIM15 || tim->Instance == TIM16 || tim->Instance == TIM17)
#elif defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
        if (0)
#endif
        {
#if defined(SOC_SERIES_STM32L4)
            val = HAL_RCC_GetPCLK2Freq() / freq;
#elif defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7)
            val = HAL_RCC_GetPCLK2Freq() * 2 / freq;
#endif
        }
        else
        {
#if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32F2) || defined(SOC_SERIES_STM32F4) || defined(SOC_SERIES_STM32F7)
            val = HAL_RCC_GetPCLK1Freq() * 2 / freq;
#elif defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
            val = HAL_RCC_GetPCLK1Freq() / freq;
#endif
        }
        __HAL_TIM_SET_PRESCALER(tim, val - 1);
        /* Update frequency value */
        tim->Instance->EGR |= TIM_EVENTSOURCE_UPDATE;
    }
    break;
    default:
    {
        result = -RT_ENOSYS;
    }
    break;
    }
    return result;
}
举报

juju宇哥

2022-8-1 10:35:20
我认为 timer_ctrl 这个函数实现的不全,如果想通过 timer_ctrl 实现停止的话还需要自己把停止的代码加到这个函数里面。
举报

h1654155272.9717

2022-8-1 10:35:30
我把定时器改成oneshot模式了,感觉可能是跟程序流程有关,假如数据被dma接收分成了两段,第一段接收到信号量,执行while循环,再执行下面的判断超时语句,然后接收第二段数据,同样可以接收到信号量。然后没有新的数据,定时器会超时,此时判断超时的函数在获取信号量的后面,而两段数据已经传完,不会再获得信号量了,这样后面的代码就因为县城被挂起执行不到了。直到下一针数据来到,获得信号量,才会执行到判断超时函数的语句,而此时,就会上一针数据的第二段,和这一帧数据的第一段连起来

你看我分析的有没有道理。
谢谢
举报

更多回帖

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