基于STM32根据DL/T 645-2007通讯协议利用RS485进行抄表并将电压等数据利用HC-05蓝牙实时传输至上位机显示
RS485通訊類似串口通訊,本作品是利用金升陽RS485模塊,MCU是STM32F103C8T6最小系統(tǒng)板,電能表是正泰單相電能表,RS485 A端接電能表 A端,B端接B端,RX接MCU的RX,TX接MCU的TX,上位機是使用大佬寫的"藍牙調試器"具體見提高開發(fā)效率-藍牙調試器 - 簡書 (jianshu.com),在這里十分感謝這位大佬,讓我的作品能夠順利完成,也希望大家能夠多多支持。
本文章具體講述如何利用RS485及DL/T 645-2007通訊協(xié)議進行抄表以及相關代碼。
首先,想要利用DL/T 645-2007通訊協(xié)議進行抄表就得知道此協(xié)議分為請求幀和響應幀兩部分。
首先就以抄讀電能為例,根據(jù)DL/T 645-2007通訊協(xié)議電能的請求幀為
FE FE FE FE 68 34 54 00 08 02 22 68 11 04 33 33 34 33 98 16
前面四個FE是喚醒符用以喚醒RS485通訊,68為起始符1,34 54 00 08 02 22為電表地址的倒序,68為起始符2,11為命令碼,04為數(shù)據(jù)長度,33 33 34 33 為電能標識符,我所寫的程序是將電能標識符每位減33H后通過下面的請求幀發(fā)送函數(shù)每位加33H(這樣利于抄讀其他數(shù)據(jù))即0x33,0x33,0x34,0x33發(fā)送,98為起始符1到電能標識符最后一位的校驗碼,16為終止符
此函數(shù)用以打包請求幀并通過RS485串口發(fā)送,如下:
void Eleout_Encode(u8 *meter_id, u8 *src, u8 srcLenth, u8 *dst) {u8 i = 0, prt = 0;u8 CHK = 0;u16 packlen = srcLenth + 10;dst[prt++] = 0x68; /*STX*/memcpy((u8 *)&dst[prt], (u8 *)meter_id, 6);prt += 6;dst[prt++] = 0x68; /*STX*/dst[prt++] = src[i++]; /*控制碼*/dst[prt++] = src[i++]; /*數(shù)據(jù)域長度*/for( ; i < srcLenth; i++) /*DATA*/{ dst[prt++] = src[i] + 0x33; /*加33H處理*/}for(i = 0; i < packlen - 2; i++){CHK += dst[i]; /*CHK*/}dst[prt++] = CHK; /*CHK*/dst[prt++] = 0x16; /*ETX*/for(u8 i=0;i<4;i++){USART_SendData(USART2, 0xfe);while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET );}while(packlen--){USART_SendData(USART2, *dst++);while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET );}} u8 dst[34]; u8 src1[6] = {0x11, 0x04, 0x00, 0x00,0x01,0x00};//電能標識符 u8 App_E_Meters_id[6] = {0x34, 0x54, 0x00, 0x08, 0x02, 0x22};//電表ID將電能標識符即電能表地址填充至上述函數(shù)中即可發(fā)送電能請求幀,如下:
Eleout_Encode(App_E_Meters_id,src1,6,dst);返回的數(shù)據(jù)FE FE FE FE 68 34 54 00 08 02 22 68 91 08?33 33 34 33 88 39 33 33 11?16
前面四個FE同上,68為起始符1,34 54 00 08 02 22為電表地址的倒序,68為起始符2,91為命令碼,08為數(shù)據(jù)長度,33 33 34 33 為電能標識符,88 39 33 33這才是我們需要解析處理的數(shù)據(jù)!
88 39 33 33 對于這組數(shù)據(jù)我們依次減33H即55 06 00 00,再將其倒序得到00 00 06 55即真正的電能為6.55kwh(度)
將串口收到的上述所說真正需要處理的數(shù)據(jù)依次減33H后還需將BCD編碼轉為十進制編碼,如下:
u32 Ele_BCDCode_DECCode_Convert(u32 *bcd_code) {u8 i = 0;u32 val=0;if(bcd_code != 0){for(i = 0; i < 8; i++){val += pow(10, i) * ((*bcd_code >> (4 * i)) & 0x0f);}return val;}return val;}此函數(shù)用以解析收到的數(shù)據(jù)并將BCD編碼轉為十進制編碼,如下:
u32 Eleout_GetMeter(void) {u32 val=0;u32 dataflag;u8 i=0;u8 sumcheck=0;if(Serial_RxPacket[23]==0x16) //結束符標志{for(i = 4; i <=21 ; i++){sumcheck +=Serial_RxPacket[i];}if(sumcheck == Serial_RxPacket[22]) //檢測校驗碼{for(i=14;i<=21;i++) {Serial_RxPacket[i] = Serial_RxPacket[i]-0x33; //解出真實數(shù)據(jù) }dataflag = Serial_RxPacket[14]+(Serial_RxPacket[15]<<8)+(Serial_RxPacket[16]<<16)+(Serial_RxPacket[17]<<24);if(dataflag==0x00010000) //讀當前正向有功總電能{val = Serial_RxPacket[18];val+= Serial_RxPacket[19]<<8;val+= Serial_RxPacket[20]<<16;val+= Serial_RxPacket[21]<<24;val= Ele_BCDCode_DECCode_Convert(&val); //數(shù)值放大了100倍(還原回去必須除以100) 電能值格式xxxxxx.xx kWhval = val*10;//再放大10倍,單位為 Wh}}}return val;}注意!!!得到的val數(shù)據(jù)是放大1000倍的數(shù)據(jù)即單位為Wh!
到這里請求幀與響應幀就講完了。
對于RS485的初始化就和串口初始化一樣,不過要注意的是在抄表時,串口應初始化為9位數(shù)據(jù)位,偶校驗,代碼如下:
void RS485_Init(void) {RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 2400;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_Even;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_WordLength = USART_WordLength_9b;USART_Init(USART2, &USART_InitStructure);//USART_ClearITPendingBit(USART1,USART_IT_RXNE);USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART2, ENABLE); }中斷服務函數(shù)用一數(shù)組接收RS485收到的響應幀,再利用上述講到的Eleout_GetMeter()函數(shù)進行數(shù)據(jù)處理,代碼如下:
uint8_t Serial_RxPacket[100]; u16 count=0; void USART2_IRQHandler(void) {if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET){ USART_ClearITPendingBit(USART2, USART_IT_RXNE);RS485_RxFlag = 1;Serial_RxPacket[count] = USART_ReceiveData(USART2);count++;if(Serial_RxPacket[count-1]==0x16){count=0;}}}值得注意的是在用數(shù)組接收數(shù)據(jù)時千萬不可越界,在后面加一判斷是否接收到終止符代碼即可。
對于HC-05藍牙及上位機部分見(33條消息) 藍牙調試器-劃時代無線調試器_謝榮基的博客-CSDN博客_藍牙調試器
這位博主講得很仔細大家可以學習學習,如有需要上位機demo的小伙伴可評論區(qū)評論看到就會發(fā)。本人第一次在CSDN上發(fā)文章,如有寫的不好和不對的地方請各位小伙伴及時告知,我會及時改正。希望能和大家共同學習,一起進步。謝謝大伙的支持。如果調試過程遇到了一些問題可以發(fā)在評論區(qū)一起學習討論。
總結
以上是生活随笔為你收集整理的基于STM32根据DL/T 645-2007通讯协议利用RS485进行抄表并将电压等数据利用HC-05蓝牙实时传输至上位机显示的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三星手机动态修改分辨率信息
- 下一篇: 《ShareX 》截图神器