流放之路做长老环的网站,切图网站建设,做网站的证书,工业设计网站排行榜前十名有哪些嵌入式系统通常都会与外部设备进行通讯#xff0c;这就涉及到通讯协议的问题。这些通讯协议有的是标准协议有的厂家自定义的协议#xff0c;如宇电的AI-BUS。在本篇中#xff0c;我们将讨论AI-BUS的驱动#xff0c;以便于与宇电设备的通讯。
1、功能概述
宇电的设备使用基…嵌入式系统通常都会与外部设备进行通讯这就涉及到通讯协议的问题。这些通讯协议有的是标准协议有的厂家自定义的协议如宇电的AI-BUS。在本篇中我们将讨论AI-BUS的驱动以便于与宇电设备的通讯。
1、功能概述
宇电的设备使用基于RS-485的自定义协议该协议称为AI-BUS。AI-BUS协议采用16进制数据格式来表示各种指令代码。数据协议本身比较简单标准的通讯指令只有两条一条为读指令一条为写指令
读地址代号52H82要读的参数代号00校验码
写地址代号43H67要写的参数代号写入数低字节写入数高字节校验码
具体结构如下图所示 地址代号为了在一个通讯接口上连接多台 AI 仪表需要给每台 AI 仪表编一个互不相同的通讯地址。 有效的地址为 0~80部分型号为 0~100所以一条通讯线路上最多可连接 81 台 AI 仪表仪表的通讯地址 由参数 Addr 决定。仪表内部采用两个重复的 128~20816 进制为 80H~D0H之间数值来表示地址代号由 于大于 128 的数较少用到如 ASC 方式的协议通常只用 0-127 之间的数因此可降低因数据与地址重复造 成冲突的可能性。 AI 仪表通讯协议规定地址代号为两个相同的字节数值为仪表地址80H。例如仪表参数 Addr10
16 进制数为 0AH0A80H8AH则该仪表的地址代号为8AH 8AH
参数代号仪表的参数用 1 个 8 位二进制数一个字节写为 16 进制数的参数代号来表示。它在指 令中表示要读/写的参数名。
校验码校验码采用16 位求和校验方式其中读指令的校验码计算方法为要读参数的代号×25682ADDR。写指令的校验码计算方法为以下公式做16位二进制加法计算得出的余数溢出部分不处理要写的参数代号×25667要写的参数值ADDR。
公式中的数字都为十进制公式中 ADDR 为仪表地址参数值范围是 0~80注意不要加上 80H。校验 码为以上公式做二进制 16 位整数加法后得到的余数余数为 2 个字节其低字节在前高字节在后。要写 的参数值用 16 位二进制整数表示。
返回的数据格式更是固定的无论是读还是写仪表都返回以下10个字节数据
测量值 PV给定值 SV输出值 MV 及报警状态所读/写参数值校验码。 其中 PV、 SV 及所读参数值均各占 2 个字节代表一个 16 位二进制有符号补码整数低位字节在前高位字节在后整数无法表示小数点要求用户在上位机处理 MV 占一个字节按 8 位有符号二进制数格式数值范围-110~110状态位占一个字节校验码占 2 个字节共 10 个字节。
返回校验码的计算公式PVSV报警状态*256MV参数值ADDR
2、驱动设计与实现
我们已经清楚了AI-BUS协议的的基本规则对于通讯协议的驱动开发只需要按照通讯协议来实现代码就可以了。
2.1、对象定义
同样的我们在操作AI-BUS设备之前我们先需要定义AI-BUS设备对象。然后针对AI-BUS设备的操作就是针对该对象的操作。
2.1.1、对象的抽象
根据AI-BUS设备对象的特点我们抽象对象类型。该对象各类型包括设备地址和状态2个属性和发送命令一个操作。而对于消息的接收我们一般采用串口中断方式。对象类型定义如下
/* 定义AI-BUS设备对象 */
typedef struct AIbusObject {uint8_t deviceAddr;uint8_t status;void (*SendBytes)(uint8_t *cmduint16_t size);
}AIbusObjectType;
2.1.2、对象初始化
定义了AI-BUS对象类型后我们就可以使用该类型声明不同的对象但是声明的对象仅为一个对象变量在使用之前必须对其进行初始化初始化函数如下
/* AI-BUS对象初始化 */
void AIbusInitialization(AIbusObjectType *aibusuint8_t addrAiBusSendBytes send)
{if((aibusNULL)||(sendNULL)){return;}aibus-deviceAddraddr;aibus-SendBytessend;
}
2.2、对象操作
完成了对象的初始化后就可以实现对对象的操作了。根据前面对AI-BUS协议的了解我们所需要完成的操作实际上就是3个方面。一是对目标设备参数的读操作二是对目标设备参数的写操作三是对接收到的消息进行解析。
2.2.1、读对象操作
对AI-BUS对象的读就是将读命令按一定格式下发就好了。读命令的格式为地址代号52H82要读的参数代号00校验码。可以据此编写读操作如下
/*读取目标设备的参数值*/
void ReadAiBusDeviceParameter(AIbusObjectType *aibusuint8_t paraAddr)
{uint8_t readCommand[INSTRUCTION_LENGTH];uint16_t index0;readCommand[index]0x80aibus-deviceAddr;readCommand[index]0x80aibus-deviceAddr;readCommand[index]READ_INSTRUCTION;readCommand[index]paraAddr;readCommand[index]0x0;readCommand[index]0x0;uint16_t checkSum(uint16_t)paraAddr*256READ_INSTRUCTION(uint16_t)aibus-deviceAddr;readCommand[index]checkSum;readCommand[index](checkSum8);aibus-SendBytes(readCommandINSTRUCTION_LENGTH);
}
2.2.2、写对象操作
同样对AI-BUS对象的写操作也是按照写命令的格式下发命令就可以了。写对象的命令格式为地址代号43H67要写的参数代号写入数低字节写入数高字节校验码。我们据此可以编写写操作函数
/*设置目标设备的参数值*/
void WriteAiBusDeviceParameter(AIbusObjectType *aibusuint8_t paraAddruint16_t data)
{uint8_t writeCommand[INSTRUCTION_LENGTH];uint16_t index0;writeCommand[index]0x80aibus-deviceAddr;writeCommand[index]0x80aibus-deviceAddr;writeCommand[index]WRITE_INSTRUCTION;writeCommand[index]paraAddr;writeCommand[index]data;writeCommand[index](data8);uint16_t checkSum(uint16_t)paraAddr*256WRITE_INSTRUCTION(uint16_t)aibus-deviceAddrdata;writeCommand[index]checkSum;writeCommand[index](checkSum8);aibus-SendBytes(writeCommandINSTRUCTION_LENGTH);
}
2.2.3、消息解析
我们已经知道AI-BUS对象的返回消息是一个固定的的格式。即测量值 PV给定值 SV输出值 MV 及报警状态所读/写参数值校验码。每一个字都是低字节在前而校验码则是返回的数据和加上设备地址我们据此编写解析函数
/*解析返回数据返回值为读或者写的参数值*/
int ParsingReturnData(uint8_t *receiveDatauint16_t *returnDataAIbusObjectType *aibusuint16_t deviceNum)
{int status-1;uint16_t pValue0;uint16_t sValue0;uint16_t mValue0;uint16_t alarmStatus0;uint16_t paraValue0;uint16_t checkSum0;pValuereceiveData[0]receiveData[1]*256;sValuereceiveData[2]receiveData[3]*256;mValue(uint16_t)receiveData[4];alarmStatus(uint16_t)receiveData[5];paraValuereceiveData[6]receiveData[7]*256;checkSumreceiveData[8]receiveData[9]*256;uint16_t chkpValuesValuealarmStatus*256mValueparaValue;for(int i0;ideviceNum;i){if(checkSumchkaibus[i].deviceAddr){statusi;returnData[0]pValue;returnData[1]sValue;returnData[2]mValue;returnData[3]alarmStatus;returnData[4]paraValue;break;}}if(status0){aibus[status].statusalarmStatus;}return status;
}
3、驱动的使用
AI-BUS协议设备驱动的使用主要按照三个步骤来操作声明并初始化对象发送操作命令接收并解析消息。接下来我们将据此完成驱动的使用。
3.1、声明并初始化对象
我们需要使用AIbusObjectType类型声明对象变量。同时我们要实现一个typedef void (*AiBusSendBytes)(uint8_t *cmduint16_t size)类型的操作函数。我们假设使用的USART1端口则具体实现如下
/*发送数据*/
void AiBusSendByte(uint8_t *instructionuint16_t length)
{/*RS485设置为发送模式准备发送*/TEMPCTL_TRANSMIT_ALLOW();aiBusRxLength0;uint16_t i;for(i0;ilength;i){/*传送寄存器不为空等待传送结束*/while(USART_GetFlagStatus(USART1 USART_FLAG_TXE) RESET){}/*写一个字节到对应的串口传送数据寄存器*/USART_SendData(USART1 instruction[i]);}Delayms(3);/*发送完毕将RS485改为接收模式准备接收*/TEMPCTL_RECIEVE_ALLOW();Delayms(20);
}
对于单个的对象我们直接使用其声明就可AIbusObjectType aiDev假设其设备地址为uint8_t addr0x01。然后调用初始化函数初始化。对于单台设备则可直接调用
AIbusInitialization(aiDevaddrAiBusSendByte);
而如果是在同一总线上有多个设备我们也可以将其定义为数组形式。假设有4台设备地址我分别为0x010x020x030x04则可定义为
AIbusObjectType aiDev[4]
uint8_t addr[4]{0x010x020x030x04};
然后同样调用初始化函数初始化
for(int i0;i4;i)
{
AIbusInitialization(aiDeviaddr[i]AiBusSendByte);
}
3.2、发送操作命令
初始化完成后就可以对其进行真正的操作读取或者写某个参数的值。对于读参数的值操作则只需要调用读操作函数来完成
ReadAiBusDeviceParameter(aiDevparaAddr);
而对于写参数值的操作也只是简单的调用写操作函数来完成
WriteAiBusDeviceParameter(aiDevparaAddrdata);
如果是多个对象与前面一样操作数组的方式来操作就可以了再次就不赘述。
3.3、接收并解析消息
接收消息我们采用串口中断接收。具体实现如下
void USART1_ReceiveDataHandle(void)
{if(aiBusRxLengthRETURNING_DATA_LENGTH){aiBusRxLength0;}/*接收寄存器为空等待字节被对应的串口完全接收*/if(USART_GetFlagStatus(USART1 USART_IT_RXNE) ! RESET){ /*获取接收到的字节数*/aiBusRxBuffer[aiBusRxLength] USART_ReceiveData(USART1);}
}
我们知道接受的消息格式是固定的我们调用消息解析函数来完成解析
ParsingReturnData(receiveDatareturnDataaiDevdeviceNum);
其中receiveData是长度为10的uint8_t类型数组。returnData是长度为5的uint16_t类型数组。
4、应用总结
我们完成了AI-BUS驱动的编写及应用。我们使用其同时操作4台温度控制器。我们操作数组的方式简化函数的调用过程。当然结果与我们的预期是相符的。
使用本驱动程序操作AI-BUS设备有一点需要注意对于不懂的设备类型参数的具体地址是用所不同的需要查看厂家的参数定义来操作。
欢迎关注