HART原理
HART (Highway Addressable Remote Transducer)协议采用基于 Bell202 标准的 FSK频 移键控信号,在低频的 4-20mA模拟信号上叠加幅度为 0.5mA的音频数字信号进行双向数字通讯,数据传输率为1200bps。
HART具有三类命令,其中第一类称为通用命令,这是所有设备都理解、执行的命令;第二类称为普通应用命令,所提供的功能可以在许多现场设备(尽管不是全部)中实现;第三类称为设备专用命令,以便于工作在某些设备中实现特殊功能,这类命令既可以在基金会中开放使用,又可以为开发此命令的公司所独有。
HART位流之前是一个标准UART帧,该帧包含一个起始位、8位数据、一个奇偶校验和一个停止位。
想要知道更多,可以通过下面这些链接了解HART原理。
HART通信协议
讲一讲HART协议
讲一讲HART协议命令格式
AD5700芯片了解
AD5700不仅继承调制和解调功能,而且还内置基准电压源、接收带通滤波器(可根据需要灵活地进行旁路)和缓冲HART输出,能够提供高输出驱动能力且无需外部缓冲。并且AD5700能够发射或接收1.2 kHz和2.2 kHz载波信号。1.2 kHz信号表示数字1(即传号),2.2 kHz信号则表示0(即空号)。
在这里,我用的芯片是AD5700-1,AD5700和AD5700-1有什么区别,在芯片手册中写得很清楚,比较直观的一点就是,AD5700-1支持使能内部振荡器且使能CLKOUT,因为其中有一个低功耗RC振荡器,所以在配置内部时钟时,AD5700-1芯片可以利用这个内部振荡器来配置。
不过如果使用内部RC振荡器,此时钟输出只能为1.2288 MHz缓冲时钟。
HAL库配置及初始化
AD5700芯片引脚定义在STM32L496芯片引脚的示意图如下,仅供参考。
在本文,CLK_CFG0和CLK_CFG1共同用一个GPIO引脚来控制输出。
(还好评论指出错误,已修改)
在这里先使能RTS引脚,使5700一直在等待上位机有数据发给5700(5700一直在等消息接收)。
定义usart2为接受与发送HART命令的引脚。
时钟配置选项,这里使用内部振荡器,输出1.2288MHz的时钟。
针对HART调制解调命令流程,可以作如下片面理解:
1.发送命令0给从机,等待从机回应;
2.收到回应后,需要对回应进行相应的解析(此时会返回厂商代码、设备序列号、处理数据等);
3.利用返回的结果进行长指令通信,然后改命令,对不同命令进行解析,获得自己想要的信息。
部分代码
i.这里的部分代码指的是在main.c文件下配置HART的函数,并不包含HART接收命令后做数据处理的函数。
ii.HART协议的通用命令可以在网上找到。HART通用命令
iii.该工程的AD5700为搭配AD5421使用,如有需要,可浏览本人主页的【AD5421】文章。
HART时钟配置函数
float EnableHartClk(void)
{
uint32_t cnt = 0;
float freq = 0;
TIM3_CH2_Flag = 0;
TIM3_CH2_Overflow = 0;
TIM3_CH2_Count[0] = 0;
TIM3_CH2_Count[1] = 0;
TIM_RESET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_2);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_2,TIM_INPUTCHANNELPOLARITY_FALLING);
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);
while(TIM3_CH2_Flag < 2);
TIM3_CH2_Flag = 0;
cnt = TIM3_CH2_Overflow*(0xffff+1)+TIM3_CH2_Count[1]-TIM3_CH2_Count[0];
freq = 50000000.0f/cnt;
TIM3_CH2_Overflow = 0;
TIM3_CH2_Count[0] = 0;
TIM3_CH2_Count[1] = 0;
printf("Hart Clock = %0.4f Hz\n",freq);
return freq;
}
AD5700初始化函数
void Init_AD5700(void)
{
//ADC检测
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_SET);//enable HART_CLK_CFG
// //时钟检测
// enableHartClk();
// HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET);//Disable_HART_CLK_CFG;
ad5700Hart(NULL,0,false);
}
控制AD5700的调制与解调
//操作AD5700的HART调制or解调
void ad5700Hart(uint8_t *dat,uint8_t dsize,bool modulate)
{
if(modulate)//调制
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_RESET);
delay_ms(10);
HAL_UART_Transmit(&huart4,dat,dsize,0xFFFF);
}
else//解调
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_SET);//enable AD5700_RTS
delay_ms(10);
HAL_UART_Receive_IT(&huart4,&HART_RxBit,1);//开启中断,HART_RxBit为全局变量下定义的buf
}
}
回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart2)//5700
{
HART_RxBuffer[HART_RxFlag]=HART_RxBit;
HART_RxBit=0;
HART_Timer_Flag=0;
HAL_TIM_Base_Start_IT(&htim4);
HART_RxFlag++;
HAL_UART_Receive_IT(&huart2,&HART_RxBit,1);
}
}
控制HART的定时器回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim4)
{
HART_Timer_Flag++;
if(HART_Timer_Flag >= 10)
{
HART_RxFlag = 0;
HART_Timer_Flag = 10;
HAL_TIM_Base_Stop_IT(&htim4);
}
}
else if(htim == &htim3)
{
TIM3_CH2_Overflow++;
}
}
这里其实还有一个获得主变量PV值的一个函数,和一个控制电流输出PV值的函数。前者是通过ad5421_read函数读失调调整寄存器和增益调整寄存器的值;并判断是否为输出电流模式,是则判断固定电流模式是否为0,不为0则写入该电流值,否则计算相应的电流输出值(调用后者函数,控制AD5421的DAC)。
这部分内容还在整理… …
遇到的问题
在网上直接搜HART,可以搜到不少与其相关的概念知识(比如工作原理、消息结构[下图]这种),但是却很少能找到和hart相关的代码,刚开始接触HART的时候,配置完引脚之后根本不知道要从哪里下手写程序。
并且AD5700芯片也没接触过… …看它手册里写得头头是道的,但也仅限于按照原理图配引脚了,最后还是得找示例代码来抄,代码量不小,功能函数较多,前几天看的时候头挺晕的,后面跟着debug跑的时候也大概知道了它的工作流程是怎么样的了。
并且在一开始写(chao)功能代码的时候,控制调制解调的引脚不知道为什么被我配成了外部中断… …应该是输出才对… …最后还是靠同事来看才发现是配置开始就配错了,无语(ˉ▽ˉ川)…