前段時間一直在做超聲波的調(diào)試,很多模擬量的問題沒有解決這段時間降模擬量和通信做了調(diào)整,目前完成了Modbus-RTU的數(shù)據(jù)通信以及超聲波測距的工作,整體的項目進展目前已經(jīng)基本完成了,所以這里來和大家做一下匯報,分享一下自己的成果,首先從整體的項目規(guī)劃以及框圖開始說起,可能會比較繁瑣一點,但是我還是覺得盡量詳細的介紹我們現(xiàn)在的東西,也許會對大家?guī)硪恍﹩l(fā)。
1 項目介紹
AGV在現(xiàn)在物流運輸和分揀中起到了重要作用,通常情況下是由上級電腦完成系統(tǒng)路徑的規(guī)劃,然后車輛按照分配路線將物流包裹配送到對應的區(qū)域內(nèi),從而完成大量包裹的分揀的工作,節(jié)約了很多人力成本,也避免了由于人為操作失誤造成物流停滯、延遲的負面影響。因此AGV在物流分揀的重要性可想而知。在AGV的整體路徑規(guī)劃中,AGV的智能性也將起到至關重要的作用,其中最主要的一點就是安全距離檢測,可以判斷對于障礙物的判斷,以及車輛急停、車輛會根據(jù)障礙物距離做出相應的減速、停止、轉(zhuǎn)向等動作。
2 硬件方案
整體的硬件方案是STM32F103CBT6作為主控,通過分時復用完成多多個超聲波探頭的然后通過RS485或者CAN總線完成數(shù)據(jù)的傳輸,通常情況下,RS485和CAN總線在實時性上是有一些距離的。目前的話只做了RS485這塊,支持Modbus_RTU的協(xié)議。整體還配備了溫度采集傳感器,可以通過溫度來進行聲音測距的溫度補償,計算出當前溫度的聲速,從而對測量距離進行修正。
2.1 硬件原理圖
整體的原理圖如上圖,采用八個國內(nèi)的超聲波測距芯片,然后使用DCDC,完成12V到3.3V的電壓轉(zhuǎn)換,通過兩個8通道的選擇開關完成對每個傳感器的數(shù)據(jù)選擇和讀取,通過TTL串口實現(xiàn)數(shù)的采集。軟件內(nèi)配置狀態(tài)機,可以正常的完成采集。
3 軟件流程圖
3.1 狀態(tài)機切換機制
狀態(tài)機是在控制最常用的一種機制,可以避免阻塞,完成程序的流暢運行,同樣避免了不必要的程序運行占用資源。目前我使用的狀態(tài)機一共設置了幾個狀態(tài),每個狀態(tài)有對應狀態(tài)的工作任務。
目前一共分為:空閑狀態(tài)、命令發(fā)送狀態(tài)、回復等待狀態(tài)、超時狀態(tài)、異常狀態(tài)。
3.2 代碼展示
3.2.1 狀態(tài)機代碼
void get_Data(struct CSB_Get *CSB_Struct)
{
int i=0;
unsigned int Status=CSB_Struct->Stttus;
unsigned int Data=0;
if(CSB_Struct->Channel_Count>CSB_Struct->Channel_MAX)
{
CSB_Struct->Channel_Count=0;/*-- 如果當前通道計數(shù)大于 最大通道--那么這次就作廢---*/
return ;
}
else
{
switch(Status)
{
/*--------*/
case Status_Ideal:/*--數(shù)據(jù)采集之前的數(shù)據(jù)初始化工作 ---*/
{
for(i=0;i<CSB_Struct->Channel_MAX;i++)
{
CSB_Struct->CSB_Data_Stable[i]=CSB_Struct->CSB_Curent_Data[i];
SysTemInfo.Sys_Data.CSB_Data[i]=CSB_Struct->CSB_Curent_Data[i]/1000;
}
select(CSB_Struct->Channel_Count); /*-- 選擇發(fā)送通道 --*/
CSB_Struct->TimeOut_Flag=0; /*-- 超時標志清零--*/
CSB_Struct->UART_GetData_Flag=0; /*-- 串口接收數(shù)據(jù)標志清零--*/
CSB_Struct->Uart_GetData_Count=0; /*-- 串口接收數(shù)量計數(shù)清零 ---*/
CSB_Struct->CSB_Curent_Data[CSB_Struct->Channel_Count]=0;/*當前通道長度清零--*/
CSB_Struct->TimeStart_Flag=1;
CSB_Struct->TimeOutCount=0;
CSB_Struct->Uart_Data[0]=0;
CSB_Struct->Uart_Data[1]=0;
CSB_Struct->Uart_Data[2]=0;
CSB_Struct->Stttus=Status_SendCND;
}break;
case Status_SendCND:
{
select(CSB_Struct->Channel_Count); /*-- 選擇發(fā)送通道 --*/
Send_Cmd(); /*-- 發(fā)送超聲波的數(shù)據(jù)采集命令 --*/
timer_enable(TIMER1); /*-- 開啟定時器 -------*/
CSB_Struct->Uart_Data[2]=0;
CSB_Struct->Stttus=Status_WaitReply;
}break;
case Status_WaitReply :
{
if(CSB_Struct->UART_GetData_Flag==1)
{
Data =(Data|(CSB_Struct->Uart_Data[0]<<16) |(CSB_Struct->Uart_Data[1]<<8)|(CSB_Struct->Uart_Data[2]));
CSB_Struct->CSB_Curent_Data[CSB_Struct->Channel_Count]=Data;
CSB_Struct->Channel_Count++;
CSB_Struct->Stttus=Status_Ideal;
}
/*-- 如果超時 --*/
if(CSB_Struct->TimeOut_Flag==1)
{
CSB_Struct->Stttus=Status_TimeOut;
}
}break;
case Status_TimeOut: /*-- 直接跳轉(zhuǎn)到空閑模式,繼續(xù)下一個通道的測量 ----*/
{
CSB_Struct->Channel_Count++;
CSB_Struct->TimeStart_Flag=0;
CSB_Struct->TimeOutCount=0;
timer_disable(TIMER1); /*-- 開啟定時器 -------*/
CSB_Struct->Stttus=Status_Ideal;
}break;
/*--------*/
default:
{
CSB_Struct->TimeOut_Flag=0; /*-- 超時標志清零--*/
CSB_Struct->UART_GetData_Flag=0; /*-- 串口接收數(shù)據(jù)標志清零--*/
CSB_Struct->Uart_GetData_Count=0; /*-- 串口接收數(shù)量計數(shù)清零 ---*/
CSB_Struct->Uart_Data[0]=0;
CSB_Struct->Uart_Data[1]=0;
CSB_Struct->Uart_Data[2]=0;
CSB_Struct->Channel_Count=0;
CSB_Struct->TimeStart_Flag=0;
timer_disable(TIMER1); /*-- 開啟定時器 -------*/
}
}
}
}
3.2.2 通信代碼展示 這里僅展示部分代碼
case ReadMultRegister : //--功能碼:03 -- 讀多個寄存器----
{
MB->ProtocalStr.MB_DataNum=MB->MB_RxData[MB_DataNum]<<8|MB->MB_RxData[MB_DataNum+1];
MB->ProtocalStr.CRCData=MB->MB_RxData[MB->MB_RxData_Len-1]<<8|MB->MB_RxData[MB->MB_RxData_Len-2];//--填入CRC數(shù)值--
//--進入這里,說明地址和功能碼都滿足了,現(xiàn)在需要做的事判斷輸出數(shù)量是否在規(guī)定范圍內(nèi)--
//--IO數(shù)量的計算應當從起始地址+數(shù)量來計算--如果起始地址加上數(shù)量之后大于IO的點數(shù),
//--此時應當報錯處理--
DataLimit=MB->ProtocalStr.MB_StartAddr + MB->ProtocalStr.MB_DataNum;//--起始地址+讀取數(shù)量---
if((DataLimit>=1)&&(DataLimit<MaxiunReadREG_Data))
{
MB->MB_TxData[TX_MB_Addr]=MB->MB_RxData[MB_Addr];//--填充站號---
MB->MB_TxData[TX_MB_FunCode]=MB->MB_RxData[MB_FunCode];//--填充功能碼--
//--讀取線圈的函數(shù)----unsigned short ReadMultReg_03(unsigned char *Buffer,unsigned short StartAddr,unsigned short Length)
RTN_DataLen=ReadMultReg_03(&MB->MB_TxData[TX_MB_DataBase],MB->ProtocalStr.MB_StartAddr,MB->ProtocalStr.MB_DataNum);
//--填充字節(jié)數(shù)--
MB->MB_TxData[TX_MB_TxNum]=(unsigned char )(RTN_DataLen&0xff); //--填充字節(jié)數(shù)--
//--開始填充數(shù)據(jù)--
//計算CRC---校驗和----校驗是按照從包頭開始 -- 一直到數(shù)據(jù)結束---
CRC_Data=usMBCRC16( MB->MB_TxData, RTN_DataLen+3 );//--計算CRC的數(shù)值
RTN_DataLen=RTN_DataLen+5; //--這里是包含了CRC校驗的----站號 +功能碼 +數(shù)量+CRCData*2=5
MB->MB_TxData_Len=RTN_DataLen;//--傳入帶發(fā)送的字節(jié)長度--
MB->MB_TxData[RTN_DataLen-2]=(CRC_Data&0xff); //--CRC-H
MB->MB_TxData[RTN_DataLen-1]=((CRC_Data>>8)&0xff); //--CRC-L
// 完成數(shù)據(jù)包的組包
//-- 發(fā)送標志----
MB->SendFlag=1;
}
else/*--說明--超出范圍了---應當返回異常碼------ --*/
{
MB->MB_TxData[MB_Addr]=(0x80+ MB->MB_RxData[MB_FunCode]);//--填充站號--
MB->MB_TxData[MB_FunCode]=MB_ERR_Output_Outof_RangeNum;//--填充功能碼--錯誤碼--
MB->MB_TxData_Len=2;
MB->SendFlag=1;
}
}
break ;
//--
case ReadinputRegister : //--功能碼:04 -- 讀輸入寄存器----
{
MB->ProtocalStr.MB_DataNum=MB->MB_RxData[MB_DataNum]<<8|MB->MB_RxData[MB_DataNum+1];
MB->ProtocalStr.CRCData=MB->MB_RxData[MB->MB_RxData_Len-1]<<8|MB->MB_RxData[MB->MB_RxData_Len-2];//--填入CRC數(shù)值--
DataLimit=MB->ProtocalStr.MB_StartAddr + MB->ProtocalStr.MB_DataNum;//--起始地址+讀取數(shù)量---
if((DataLimit>=1)&&(DataLimit<MaxiunReadREG_Data))
{
MB->MB_TxData[TX_MB_Addr]=MB->MB_RxData[MB_Addr];//--填充站號---
MB->MB_TxData[TX_MB_FunCode]=MB->MB_RxData[MB_FunCode];//--填充功能碼--
//--讀取線圈的函數(shù)----unsigned short ReadMultReg_03(unsigned char *Buffer,unsigned short StartAddr,unsigned short Length)
RTN_DataLen=ReadInputReg_04(&MB->MB_TxData[TX_MB_DataBase],MB->ProtocalStr.MB_StartAddr,MB->ProtocalStr.MB_DataNum);
//--填充字節(jié)數(shù)--
MB->MB_TxData[TX_MB_TxNum]=(unsigned char )(RTN_DataLen&0xff); //--填充字節(jié)數(shù)--
//--開始填充數(shù)據(jù)--
//計算CRC---校驗和----校驗是按照從包頭開始 -- 一直到數(shù)據(jù)結束---
CRC_Data=usMBCRC16( MB->MB_TxData, RTN_DataLen+3 );//--計算CRC的數(shù)值
RTN_DataLen=RTN_DataLen+5; //--這里是包含了CRC校驗的----站號 +功能碼 +數(shù)量+CRCData*2=5
MB->MB_TxData_Len=RTN_DataLen;//--傳入帶發(fā)送的字節(jié)長度--
MB->MB_TxData[RTN_DataLen-2]=(CRC_Data&0xff); //--CRC-H
MB->MB_TxData[RTN_DataLen-1]=((CRC_Data>>8)&0xff); //--CRC-L
// 完成數(shù)據(jù)包的組包
//-- 發(fā)送標志----
MB->SendFlag=1;
}
else/*--說明--超出范圍了---應當返回異常碼------ --*/
{
MB->MB_TxData[MB_Addr]=(0x80+ MB->MB_RxData[MB_FunCode]);//--填充站號--
MB->MB_TxData[MB_FunCode]=MB_ERR_Output_Outof_RangeNum;//--填充功能碼--錯誤碼--
MB->MB_TxData_Len=2;
MB->SendFlag=1;
}
}
break ;
//----
case WriteSingelRegister : //--功能碼:06 -- 寫單個寄存器----
{
MB->ProtocalStr.MB_DataNum=1;
//--站號-功能碼-起始地址 - 數(shù)量 字節(jié)計數(shù)-- 寄存器數(shù)值--
DataLimit=MB->ProtocalStr.MB_StartAddr+MB->ProtocalStr.MB_DataNum;
//--進入這里,說明地址和功能碼都滿足了,現(xiàn)在需要做的事判斷輸出數(shù)量是否在規(guī)定范圍內(nèi)--
if((MB->ProtocalStr.MB_DataNum>=1)&&(MB->ProtocalStr.MB_DataNum<=0x07D0))
{
//--站號-功能碼-寄存器地址- 寄存器值
for(i=0;i<6;i++)
MB->MB_TxData[i]=MB->MB_RxData[i];
//--寫多個寄存器的函數(shù)----unsigned short WriteMultRegister_16(unsigned char *Buffer,unsigned short StartAddr,unsigned short Length)
RTN_DataLen=WriteSingelReg_06(&MB->MB_RxData[4],MB->ProtocalStr.MB_StartAddr,MB->ProtocalStr.MB_DataNum);
//--填充字節(jié)數(shù)--
// MB->MB_TxData[TX_MB_TxNum]=(unsigned char )(RTN_DataLen&0xff); //--填充字節(jié)數(shù)--
//--開始填充數(shù)據(jù)--
//計算CRC---校驗和----校驗是按照從包頭開始 -- 一直到數(shù)據(jù)結束---
CRC_Data=usMBCRC16( MB->MB_TxData, RTN_DataLen+6 );//--計算CRC的數(shù)值
RTN_DataLen=RTN_DataLen+8; //--這里是包含了CRC校驗的----站號 +功能碼 +數(shù)量+CRCData*2=5
MB->MB_TxData_Len=RTN_DataLen;//--傳入帶發(fā)送的字節(jié)長度--
MB->MB_TxData[RTN_DataLen-2]=(CRC_Data&0xff); //--CRC-H
MB->MB_TxData[RTN_DataLen-1]=((CRC_Data>>8)&0xff); //--CRC-L
// 完成數(shù)據(jù)包的組包
//-- 發(fā)送標志----
MB->SendFlag=1;
}
else/*--說明--超出范圍了---應當返回異常碼------ --*/
{
MB->MB_TxData[MB_Addr]=(0x80+ MB->MB_RxData[MB_FunCode]);//--填充站號--
MB->MB_TxData[MB_FunCode]=MB_ERR_Output_Outof_RangeNum;//--填充功能碼--錯誤碼--
MB->MB_TxData_Len=2;
MB->SendFlag=1;
}
}
break ;
4 實物測試圖
由于電路沒有處理好,所以目前可以穩(wěn)定測試的距離在40cm以內(nèi),下面是Modbus-RTU 的測試界面。