標(biāo)題:
stm32F407單片機(jī)實(shí)現(xiàn)RS485通訊程序,同時(shí)modbus從機(jī)測(cè)試,用串口3實(shí)現(xiàn)
[打印本頁(yè)]
作者:
li64331218
時(shí)間:
2023-3-30 22:45
標(biāo)題:
stm32F407單片機(jī)實(shí)現(xiàn)RS485通訊程序,同時(shí)modbus從機(jī)測(cè)試,用串口3實(shí)現(xiàn)
pcb1.png
(3.63 MB, 下載次數(shù): 64)
下載附件
2023-3-30 22:42 上傳
自己畫(huà)的板子,用串口3 (PD8,PD9實(shí)現(xiàn),PB15收發(fā)選擇電平)。
stm32F407單片機(jī)源程序如下:
/*485-2 */
#define RS485_RE_GPIO_PORT GPIOB
#define RS485_RE_GPIO_PIN SYS_GPIO_PIN15
#define RS485_RE_GPIO_CLK_ENABLE() do{ RCC->AHB1ENR |= 1 << 1; }while(0) /* PB口時(shí)鐘使能 */
#define RS485_TX_GPIO_PORT GPIOD
#define RS485_TX_GPIO_PIN SYS_GPIO_PIN8
#define RS485_TX_GPIO_AF 7 /* AF功能選擇 */
#define RS485_TX_GPIO_CLK_ENABLE() do{ RCC->AHB1ENR |= 1 << 3; }while(0) /* PD口時(shí)鐘使能 */
#define RS485_RX_GPIO_PORT GPIOD
#define RS485_RX_GPIO_PIN SYS_GPIO_PIN9
#define RS485_RX_GPIO_AF 7 /* AF功能選擇 */
#define RS485_RX_GPIO_CLK_ENABLE() do{ RCC->AHB1ENR |= 1 << 3; }while(0) /* PD口時(shí)鐘使能 */
#define RS485_UX USART3
#define RS485_UX_IRQn USART3_IRQn
#define RS485_UX_IRQHandler USART3_IRQHandler
#define RS485_UX_CLK_ENABLE() do{ RCC->APB1ENR |= 1 << 18; }while(0) /* USART3 時(shí)鐘使能 */
/*485-2 END */
/* 控制RS485_RE腳, 控制RS485發(fā)送/接收狀態(tài)
* RS485_RE = 0, 進(jìn)入接收模式
* RS485_RE = 1, 進(jìn)入發(fā)送模式
*/
#define RS485_RE(x) sys_gpio_pin_set(RS485_RE_GPIO_PORT, RS485_RE_GPIO_PIN, x)
#define RS485_REC_LEN 64 /* 定義最大接收字節(jié)數(shù) 64 */
#define RS485_EN_RX 1 /* 使能(1)/禁止(0)RS485接收 */
extern uint8_t g_RS485_rx_buf[RS485_REC_LEN]; /* 接收緩沖,最大RS485_REC_LEN個(gè)字節(jié) */
extern uint8_t g_RS485_rx_cnt; /* 接收數(shù)據(jù)長(zhǎng)度 */
void rs485_init(uint32_t sclk, uint32_t baudrate); /* RS485初始化 */
void rs485_send_data(uint8_t *buf, uint8_t len); /* RS485發(fā)送數(shù)據(jù) */
void rs485_receive_data(uint8_t *buf, uint8_t *len);/* RS485接收數(shù)據(jù) */
extern void UartDriver(void); /* 485接收數(shù)據(jù)處理 */
#endif
//串口驅(qū)動(dòng)函數(shù),檢測(cè)數(shù)據(jù)幀的接收,調(diào)度功能函數(shù),需在主循環(huán)中調(diào)用
void UartDriver(void)
{
unsigned char i=0,cnt;
unsigned int crc;
unsigned char crch,crcl;
static uint8_t len;
static uint8_t buf[60];
if(flagFrame) //幀接收完成標(biāo)志,即接收到一幀新數(shù)據(jù)
{
flagFrame=0; //幀接收完成標(biāo)志清零
len = UartRead(buf,sizeof(buf)); //將接收到的命令讀到緩沖區(qū)中
if(buf[0]==0x01) //判斷地址是不是0x01
{
crc=GetCRC16(buf,len-2); //計(jì)算CRC校驗(yàn)值,出去CRC校驗(yàn)值
crch=crc>>8; //crc高位
crcl=crc&0xFF; //crc低位
if((buf[len-2]==crch)&&(buf[len-1]==crcl)) //判斷CRC校驗(yàn)是否正確
{
switch (buf[1]) //按功能碼執(zhí)行操作
{
case 0x03: //讀數(shù)據(jù)
if((buf[2]==0x00)&&(buf[3]<=0x05)) //寄存器地址支持0x0000~0x0005
{
if(buf[3]<=0x04)
{
i=buf[3];//提取寄存器地址
cnt=buf[5]; //提取待讀取的寄存器數(shù)量
buf[2]=cnt*2; //讀取數(shù)據(jù)的字節(jié)數(shù),為寄存器*2,因modbus定義的寄存器為16位
len=3;
while(cnt--)
{
buf[len++]=0x00; //寄存器高字節(jié)補(bǔ)0
buf[len++]=regGroup[i++]; //低字節(jié)
}
}
break;
}
else //寄存器地址不被支持時(shí),返回錯(cuò)誤碼
{
buf[1]=0x83; //功能碼最高位置1
buf[2]=0x02; //設(shè)置異常碼為02-無(wú)效地址
len=3;
break;
}
case 0x06: //寫(xiě)入單個(gè)寄存器
if((buf[2]==0x00)&&(buf[3]<=0x05)) //寄存器地址支持0x0000-0x0005
{
if(buf[3]<=0x04)
{
i=buf[3]; //提取寄存器地址
regGroup[i]=buf[5]; //保存寄存器數(shù)據(jù)
LED0(0);
}
len -=2; //長(zhǎng)度-2以重新計(jì)算CRC并返回原幀
break;
}
else
{ //寄存器地址不被支持,返回錯(cuò)誤碼
buf[1]=0x86; //功能碼最高位置1
buf[2]=0x02; //設(shè)置異常碼為02-無(wú)效地址
len=3;
break;
}
default: //其他不支持的功能碼
buf[1]=0x80; //功能碼最高位置1
buf[2]=0x01; //設(shè)置異常碼為01—無(wú)效功能
len=3;
break;
}
crc=GetCRC16(buf,len); //計(jì)算CRC校驗(yàn)值
buf[len++]=crc>>8; //CRC高字節(jié)
buf[len++]=crc&0xff; //CRC低字節(jié)
// rs485_UartWrite(buf,len); //發(fā)送響應(yīng)幀
rs485_send_data(buf,len); //發(fā)送響應(yīng)幀
}
}
}
}
void UartRxMonitor(uint8_t ms) //串口接收監(jiān)控
{
static uint8_t USART3_RX_BKP=0; //定義USART2_RC_BKP暫時(shí)存儲(chǔ)數(shù)據(jù)長(zhǎng)度與實(shí)際長(zhǎng)度比較
static uint8_t idletmr=0; //定義監(jiān)控時(shí)間
if(g_RS485_rx_cnt>0)//接收計(jì)數(shù)器大于零時(shí),監(jiān)控總線空閑時(shí)間
{
if(USART3_RX_BKP!=g_RS485_rx_cnt) //接收計(jì)數(shù)器改變,即剛接收到數(shù)據(jù)時(shí),清零空閑計(jì)時(shí)
{
USART3_RX_BKP=g_RS485_rx_cnt; //賦值操作,將實(shí)際長(zhǎng)度給USART2_RX_BKP
idletmr=0; //將監(jiān)控時(shí)間清零
}
else ////接收計(jì)數(shù)器未改變,即總線空閑時(shí),累計(jì)空閑時(shí)間
{
//如果在一幀數(shù)據(jù)完成之前有超過(guò)3.5個(gè)字節(jié)時(shí)間的停頓,接收設(shè)備將刷新當(dāng)前的消息并假定下一個(gè)字節(jié)是一個(gè)新的數(shù)據(jù)幀的開(kāi)始
if(idletmr<5) //空閑時(shí)間小于1ms時(shí),持續(xù)累加
{
idletmr +=ms;
if(idletmr>=5) //空閑時(shí)間達(dá)到1ms時(shí),即判定為1幀接收完畢
{
flagFrame=1;//設(shè)置命令到達(dá)標(biāo)志,幀接收完畢標(biāo)志
}
}
}
}
else
{
USART3_RX_BKP=0;
}
}
/**
* @brief RS485發(fā)送len個(gè)字節(jié)
* @param buf : 發(fā)送區(qū)首地址
* @param len : 發(fā)送的字節(jié)數(shù)(為了和本代碼的接收匹配,這里建議不要超過(guò) RS485_REC_LEN 個(gè)字節(jié))
* @retval 無(wú)
*/
void rs485_send_data(uint8_t *buf, uint8_t len)
{
uint8_t t;
RS485_RE(1); /* 進(jìn)入發(fā)送模式 */
for (t = 0; t < len; t++) /* 循環(huán)發(fā)送數(shù)據(jù) */
{
while ((RS485_UX->SR & 0X40) == 0); /* 等待發(fā)送結(jié)束 */
RS485_UX->DR = buf[t];
}
while ((RS485_UX->SR & 0X40) == 0); /* 等待發(fā)送結(jié)束 */
g_RS485_rx_cnt = 0;
RS485_RE(0); /* 進(jìn)入接收模式 */
}
/**
* @brief RS485查詢(xún)接收到的數(shù)據(jù)
* @param buf : 接收緩沖區(qū)首地址
* @param len : 接收到的數(shù)據(jù)長(zhǎng)度
* @arg 0 , 表示沒(méi)有接收到任何數(shù)據(jù)
* @arg 其他, 表示接收到的數(shù)據(jù)長(zhǎng)度
* @retval 無(wú)
*/
void rs485_receive_data(uint8_t *buf, uint8_t *len)
{
uint8_t rxlen = g_RS485_rx_cnt;
uint8_t i = 0;
*len = 0; /* 默認(rèn)為0 */
delay_ms(10); /* 等待10ms,連續(xù)超過(guò)10ms沒(méi)有接收到一個(gè)數(shù)據(jù),則認(rèn)為接收結(jié)束 */
if (rxlen == g_RS485_rx_cnt && rxlen) /* 接收到了數(shù)據(jù),且接收完成了 */
{
for (i = 0; i < rxlen; i++)
{
buf[i] = g_RS485_rx_buf[i];
}
*len = g_RS485_rx_cnt; /* 記錄本次數(shù)據(jù)長(zhǎng)度 */
g_RS485_rx_cnt = 0; /* 清零 */
}
}
復(fù)制代碼
Keil代碼下載:
測(cè)試 485-2(串口3)modbus rtu從機(jī)實(shí)驗(yàn).7z
(216.49 KB, 下載次數(shù): 78)
2023-3-31 01:21 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
作者:
li64331218
時(shí)間:
2023-4-2 16:15
485通訊是半雙工的,注意收發(fā)控制引腳的電平切換。
作者:
aayon1979
時(shí)間:
2024-9-2 16:19
好東西!我來(lái)下一個(gè)
作者:
rongshengju
時(shí)間:
2024-9-18 08:22
學(xué)習(xí)。!
歡迎光臨 (http://www.torrancerestoration.com/bbs/)
Powered by Discuz! X3.1