一、前言
本项目采用淘宝购买的STM32G031开发板,板上预留了oled和LORA模块的IO,一次性买了四套,资料虽然质量不高,但是覆盖面挺广,有一定的学习价值。
实验目的是需要三个从机实现时间轴的一致,考虑到现有环境下的各种干扰,希望做到1ms以内的误差。采用一个主机来完成对所有从机的时间校对,减小上位机的任务,同时也更符合后期调试的需要。
二、实现细节
1、定时器配置
每个单片机的时间轴采用定时器计数的方式,来作为本地的时间轴。因为需要尽可能的提高精度,所以采用了频率最高的TIM1做基准时钟(128MHz),采用计数周期最大的TIM2做计数时钟(32bit)。采用定时器级联的方式,也就是手册中的TIM1作为TIM2的预分频,最高可以实现约610.839小时的计数。
主从模式的配置需要注意几个点:
首先是TIM1:
作为基准时钟,正常配置为周期计数即可,如果需要PWM或者别的功能,每个通道也都可以正常使用。
然后是计数配置,这里设置向上计数,装载值64000,不分频,开启自动装载,其余部分默认即可。
TRGO部分开启,第一个设置为更新时间触发,就是满一个周期,TIM2计数加1。
TRGO没有使用,随意。
然后是TIM2:
从模式选择外部时钟,Trigger Source的选择具体要查看数据手册,这里放一张截图:
在数据手册定时器寄存器讲解部分可以找到这个图,需要选择对应的通道,这里选择IRT0。
计数配置如下:
TRGO部分用不到,关闭即可。
本实验将两个定时器的满装载值定为64000和172,800,000,TIM1一个周期0.5ms,TIM2一个周期24小时。
2、LORA配置
lora模块采用的是Ebyte的E22-400T22D,配置方面不做介绍,可以使用上位机配置,也可以根据官方的数据手册配置模块的寄存器。
本实验采用的配置为:
主机地址为0xFFFF,实现广播和监听;
从机地址0x03~0x05;
网络地址0x17;
波特率115200,配置寄存器时只能使用9600;
透传模式;
空中速率19.2k;
频率信道60;
以上配置仅作参考,读者需要根据实际需要进行调整。
注意M0和M1配置引脚的使用,lora芯片内部默认上拉,传输模式时两个引脚都需要接地。
实验采用单片机通过串口配置lora,所以贴出指令:
//主lora模块地址固定为0x01,其余保持默认值。C0+起始地址+长度+参数
//网络地址Cmd_Set_board[5]和信道控制Cmd_Set_board[8]可自行指定
const uint8_t Cmd_Set_master_board[12]={0xC0, 0x00, 0x09, 0xff, 0xff, 0x17, 0xe5, 0x00, 0x50, 0x00, 0x00, 0x00};
3、时间同步
同步方式采用最简单的双向交互,具体流程如下:
(1)主机发送时间同步请求,并记录当前时间;
(2)从机收到后立即做出应答;
(3)主机收到应答,计算发送时间与当前时间的误差,将偏移后的时间发送至从机。
发送时间的方式,是直接将两个定时器的计数值作为数据通过lora发送,通过一些固定位来作为标志位,来辨别是哪一帧数据,哪一个从机的数据,是否是有效数据。
本实验方法较为简单,有很多可以优化的流程,另外时间偏移的计算也可以放到从机。
具体实现细节读者可以自行设计,本实验的方法最后会贴出代码作为参考,也希望各位读者给出修改意见,提高同步精度。
4、oled刷新
由于之前的单片机项目,屏幕刷新几乎不会做太多文章。由于时间精度的需要,采用延时的方法已将无法达到要求。
尝试过放在定时器溢出中断来做刷新,可是刷新还是比较耗时,影响定时器效率,于是采用标志位的方式,把oled刷新放到主函数,但是这样代码难免会有所冗余。
最后是采用读取定时器计数值的方式,每隔50ms刷新一次,这样也能保持时间同步之后的刷新一致。
三、实验结果
测试方式,根据本地时间轴每隔50ms开关一次引脚,测量所有开发板的引脚波形,最后实测能够达到主从1ms误差,从设备0.5ms左右。
四、最后
实验将主从模块一起开发,通过main.h文件的开关切换主从代码。
实验中有很多细节没有提到,描述也比较简单,应该是我太懒了哈哈哈哈,各个模块之间耦合比较多,没有办法拆开一个个细说,还请读者在源码中查看。
代码根据CubeMX生成,添加了delay延时和uart驱动,可以作为STM32G031的例程模板来开发,代码风格是学习的正点原子,应该适合大家的阅读习惯。
更新:
代码将TIM2部分换成了变量计数,好像精度提高了,还有细节部分的更改,以下是gitee链接:
stm32: 一些stm32模块使用经验记录 - Gitee.comhttps://gitee.com/lrf1125962926/stm32/tree/Time_Sync/