RS485通信
RS485——RS485通信基礎(chǔ)理論與STM32測試
口袋里のInit 2018-05-04 21:30:52 ?11872 ?收藏 97
分類專欄: 基礎(chǔ)知識 stm32 通信、總線協(xié)議 文章標(biāo)簽: RS485 STM32
版權(quán)
基礎(chǔ)知識
同時被 3 個專欄收錄
42 篇文章16 訂閱
訂閱專欄
stm32
6 篇文章0 訂閱
訂閱專欄
通信、總線協(xié)議
13 篇文章8 訂閱
訂閱專欄
1.優(yōu)劣
優(yōu)勢:RS485的可靠傳輸距離遠(yuǎn),接線簡單成為了相對于RS232的最大優(yōu)勢。
不足:RS485總線是一種常規(guī)的通信總線,它不能夠做總線的自動仲裁,也就是不能夠同時發(fā)送數(shù)據(jù)以避免總線競爭,所以整個系統(tǒng)的通信效率必然較低,數(shù)據(jù)冗余量較大,對于速度要求高的應(yīng)用場所不適應(yīng)用RS485總線。同時由于RS485總線上通常只有一臺主機,所以這種總線方式是典型的集中—分散型控制系統(tǒng)。一旦主機出現(xiàn)故障,會使整個系統(tǒng)的通信限于癱瘓狀態(tài),因此做好主機的在線備份是一個重要措施。
2. 硬件層協(xié)議
通訊協(xié)議主要是實現(xiàn)兩個設(shè)備之間的數(shù)據(jù)交換功能,通訊協(xié)議分硬件層協(xié)議和軟件層協(xié)議。硬件層協(xié)議決定數(shù)據(jù)如何傳輸問題,比如要在設(shè)備1向設(shè)備2發(fā)送0x63,0x63的二進(jìn)制數(shù)為0110 0011,這8個二進(jìn)制數(shù)從設(shè)備1傳輸?shù)皆O(shè)備2,涉及到1怎么傳,0怎么傳的問題,這就是硬件層要解決的問題。?
硬件層協(xié)議目前比較多見的有RS-232、RS-485、SPI、IIC等。RS-232規(guī)定,線上的電壓為x伏都表示傳輸?shù)氖?,y伏傳輸?shù)膭t是1。再者,比如要選擇多少條線傳輸數(shù)據(jù),選擇什么材質(zhì)的線傳輸輸入,這些也屬于硬件層協(xié)議約束的。
?
3.RS-485通訊協(xié)議
MCU管腳輸出TTL電平,TTL電平的意思是,當(dāng)MCU管腳輸出0電平時,一般情況下電壓是0V,當(dāng)MCU管腳輸出1電平時,電壓是5V。因TTL電平的是由一條信號線,一條地線產(chǎn)生,信號線上的干擾信號會跟隨有效信號傳送到接收端,使得有效信號受到干擾,485通訊實際上是把MCU出來的TTL電平通過硬件層的一個轉(zhuǎn)換器芯片進(jìn)行轉(zhuǎn)換:?
把MCU出來的一條的TTL信號經(jīng)過芯片轉(zhuǎn)換為兩根線(線A、線B)上的信號。當(dāng)MCU給轉(zhuǎn)換器輸入低TTL電平時,轉(zhuǎn)換器會使得B的電壓比A的電壓高,反之,A的電壓比B的電壓高。?
485協(xié)議規(guī)約兩條電平線上差值為多少表示0或者1,電壓是通過儀表可以測量得到的,所以說RS-485是硬件層協(xié)議。?
485協(xié)議的接收端可能是另一個MCU,MCU管腳也只接受TTL電平,轉(zhuǎn)換芯片過來的是兩條線的電壓,所以需要對此兩條線差分電壓轉(zhuǎn)換為TTL電平。?
把TTL轉(zhuǎn)為485,實質(zhì)是一個集成芯片,其間無任何程序代碼,純粹硬件邏輯。同理,將485電平轉(zhuǎn)為TTL也是如此?,F(xiàn)在很多芯片把接收和轉(zhuǎn)換都集成到一塊IC,注意,轉(zhuǎn)換器和接收器依舊是沒有同時工作的,常見的轉(zhuǎn)換芯片是MAX485。?
可以這樣理解,硬件層協(xié)議是公路,路的目的是為了讓車輛能夠過去。
?
4.半雙工通訊
首先了解什么是單工通訊,單工通訊是指數(shù)據(jù)只能朝著一個方向傳輸?shù)耐ㄓ嵎绞健6腚p工通訊則是指對于通訊兩端,不能同時相對方法發(fā)送數(shù)據(jù),必須錯開時間段發(fā)送。?
RS-485的通訊線只有2條,且這兩條通訊線在一次傳輸中都需要用到,因此485只可實現(xiàn)半雙工通訊。485實現(xiàn)半雙工通訊,會遇到一個問題,MCU1向MCU2發(fā)數(shù)據(jù)時,并不知道線上是否正傳來MCU2數(shù)據(jù),因為沒有其他線可用來判斷對方的收發(fā)狀態(tài),那么可能也會導(dǎo)致數(shù)據(jù)沖突。因此,RS-485要實現(xiàn)半雙工通訊,就需要上層的軟件協(xié)議加以規(guī)約,也就是做到”不能你想發(fā)數(shù)據(jù)就發(fā)數(shù)據(jù)”??梢岳斫?#xff0c;軟件層協(xié)議就好像交通規(guī)則,它能讓數(shù)據(jù)有序傳輸。
5.基本電路
?
三種常用電路如下:
?
5.1 基本的RS485電路
上圖是最基本的RS485電路,R/D為低電平時,發(fā)送禁止,接收有效,R/D為高電平時,則發(fā)送有效,接收截止。上拉電阻R7和下拉電阻R8,用于保證無連接的SP485R芯片處于空閑狀態(tài),提供網(wǎng)絡(luò)失效保護(hù),提高RS485節(jié)點與網(wǎng)絡(luò)的可靠性,R7,R8,R9這三個電阻,需要根據(jù)實際應(yīng)用改變大小,特別是使用120歐或更小的終端電阻時,R9就不需要了,此時R7,R8使用680歐電阻。正常情況下,一般R7=R8=4.7K,R9不要。
圖中鉗位于6.8V的管V4,V5,V6,都是為了保護(hù)RS485總線的,避免受外界干擾,也可以選擇集成的總線保護(hù)原件。另外圖中的L1,L2,C1,C2為可選安裝原件,用于提高電路的EMI性能.
?
5.2 帶隔離的RS485電路
根本原理與基本電路的原理相似。使用DC-DC器件可以產(chǎn)生1組與微處理器電路完全隔離的電源輸出,用于向RS485收發(fā)器提供+5V電源。電路中的光耦器件速率會影響RS485電路的通信速率。上圖中選用了NEC的光耦PS2501,受其影響,該電路的通訊速率控制在19200bps下。
?
5.3 自動切換電路
?
上圖中,TX,RX引腳均需要上拉電阻,這一點特別重要。
接收:默認(rèn)沒有數(shù)據(jù)時,TX為高電平,三極管導(dǎo)通,RE為低電平使能,RO收數(shù)據(jù)有效,MAX485為接收態(tài)。
發(fā)送:發(fā)送數(shù)據(jù)1時,TX為高電平時,三極管導(dǎo)通,DE為低電平,此時收發(fā)器處于接收狀態(tài),驅(qū)動器就變成了高阻態(tài),也就是發(fā)送端與A\B斷開了,此時A\B之間的電壓就取決于A\B的上下拉電阻了,A為高電平、B為低電平,也就成為了邏輯1了。
? ? ? ? ? ? 發(fā)送數(shù)據(jù)0時,TX為低電平,三極管截止,DE為高電平,驅(qū)動器使能,此時正好DI是接地的,也就是低電平,驅(qū)動器也就會驅(qū)動輸出B為1,A為0,也就是所謂的邏輯0了。
? ? ? ? ? 理解自收發(fā)的作用,關(guān)鍵是要理解RE和DE的作用,尤其是DE為0時,驅(qū)動器與A\B之間就是高阻態(tài),也就是斷開狀態(tài),而且A\B都要有上下拉電阻。然后就有了邏輯0-1之間的切換了。所以很巧妙,但是這里也有一個很明顯的bug,也就是只適用于“半雙工”,如果是全雙工,就不行了,因為TX為1時,接收使能,此時從機如果回復(fù)數(shù)據(jù),那么也就亂了。
? ? ?基本原理了解了,除了使用三極管實現(xiàn),還可以使用施密特觸發(fā)器,也就是所謂的“非”門,來顯現(xiàn),如下圖所示:
? ? ?
? ?基本原理與三極管相同,TX為1時,經(jīng)過施密特觸發(fā)器進(jìn)行“非”運算,DE為0,則接收使能,驅(qū)動器呈高阻態(tài),此時A\B的電平就是上下拉電阻的電平,也就是邏輯1。TX為0時,DE為1,發(fā)送使能,由于DI接地,也就是0,A\B輸出也是0.
?
6.SP3485內(nèi)部結(jié)構(gòu)圖:?
?
圖中:?
A、B總線接口,用于連接485總線。RO是接收輸出端,DI是發(fā)送數(shù)據(jù)收入端,RE是接收使能信號(低電平有效),DE是發(fā)送使能信號(高電平有效)。
SP3485硬件連接:?
?
注意:?
R55和R56是兩個偏置電阻,用來保證總線空閑時,AB之間的電壓差都會大約200mV,避免總線空閑時壓差不定邏輯混亂。
?
7. RS485串口編程
?
7.1 編程思路
使用RS485實現(xiàn)兩個MCU之間的通信,把接收到的數(shù)據(jù)通過串口助手顯示在超級終端上。首先對Usart1和Usart2進(jìn)行初始化,Usart1負(fù)責(zé)與串口助手通信,Usart2與RS485連接進(jìn)行兩個MCU之間的通信。然后編寫發(fā)送和接收函數(shù),接收函數(shù)在Usart2的中斷服務(wù)函數(shù)中實現(xiàn)。最后把接收到的數(shù)據(jù)和必要的提示信息發(fā)送到超級終端上顯示。
?
7.2 功能模塊代碼
①串口初始化
void Uart1_Init(void)
{
? ? //USART1 初始化
? ? GPIO_InitTypeDef GPIO_InitStructure;
? ? USART_InitTypeDef USART_InitStructure;
? ? NVIC_InitTypeDef NVIC_InitStructure;
?
? ? RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); ? ? ? ?//開啟GPIOA時鐘
? ? RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); ? ? ? //開啟USART1時鐘
?
? ? //串口1對應(yīng)引腳復(fù)用映射
? ? GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); ? ? //GPIOA9復(fù)用為USART1
? ? GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); ? ?//GPIOA10復(fù)用為USART1
?
? ? //USART1端口配置
? ? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; ? ? //GPIOA9,GPIOA10
? ? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; ? ? ? ? ? ? ? ?//復(fù)用功能
? ? GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; ? ? ? ? ? //速度50MHz
? ? GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; ? ? ? ? ? ? ?//推挽復(fù)用輸出
? ? GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; ? ? ? ? ? ? ? ?//上拉
? ? GPIO_Init(GPIOA,&GPIO_InitStructure); ? ? ? ? ? ? ? ? ? ? ? //初始化PA9,PA10
?
? ? //USART1 端口配置
? ? USART_InitStructure.USART_BaudRate ? ? ?= 115200;
? ? USART_InitStructure.USART_WordLength ? ?= USART_WordLength_8b;
? ? USART_InitStructure.USART_StopBits ? ? ?= USART_StopBits_1;
? ? USART_InitStructure.USART_Parity ? ? ? ?= USART_Parity_No;
? ? USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
? ? USART_InitStructure.USART_Mode ? ? ? ? ?= USART_Mode_Rx | USART_Mode_Tx;
? ? USART_Init(USART1, &USART_InitStructure);?
?
? ? USART_Cmd(USART1, ENABLE); ?//使能串口1
?
? ? USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); ? ? ? ? ?//開啟相關(guān)中斷
?
? ? NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
?
? ? //Usart1 NVIC 配置
? ? NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; ? ? ??
? ? NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;?
? ? NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; ? ? ??
? ? NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; ? ? ? ??
? ? NVIC_Init(&NVIC_InitStructure); ? ? ? ? ? ? ? ? ? ? ? ??
}
?
void Uart2_Init(void)
{
? ? GPIO_InitTypeDef GPIO_InitStructure;
? ? USART_InitTypeDef USART_InitStructure;
? ? NVIC_InitTypeDef NVIC_InitStructure;
?
? ? RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); ? ? ? ?
? ? RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); ? ? ??
?
? ? ?//串口2對應(yīng)引腳復(fù)用映射
? ? GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); ? ??
? ? GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); ? ??
?
? ? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; ?
? ? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; ? ? ? ? ? ? ? ? ? ? ? ?
? ? GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; ? ? ? ? ? ? ??
? ? GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; ? ? ? ? ? ? ? ? ?
? ? GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; ? ? ? ? ? ? ? ? ? ?
? ? GPIO_Init(GPIOA,&GPIO_InitStructure); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
? ? //USART2 端口配置
? ? USART_InitStructure.USART_BaudRate = 115200;
? ? USART_InitStructure.USART_WordLength = USART_WordLength_8b;
? ? USART_InitStructure.USART_StopBits = USART_StopBits_1;
? ? USART_InitStructure.USART_Parity = USART_Parity_No;
? ? USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
? ? USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;?
? ? USART_Init(USART2, &USART_InitStructure);?
?
? ? USART_Cmd(USART2, ENABLE); ?
?
? ? USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); ? ? ?
?
? ? //Usart2 NVIC 配置
? ? NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; ??
? ? NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
? ? NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
? ? NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; ? ??
? ? NVIC_Init(&NVIC_InitStructure);?
}
②接收數(shù)據(jù)
void USART2_IRQHandler(void)
{
? ? static u32 rx_i=0;
? ?if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
? ?{ ? ??
? ? ? ? USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除標(biāo)志位
? ? ? ? rx_buf[rx_i++] = USART_ReceiveData(USART2); ? ?//rx_buf是在main.c定義的全局變量
? ? ? ? while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);?
? ?}
? ?rx_flag = 1;
}
③RS485初始化?
(SP3485的RE,DE引腳與MCU的PG8引腳相連接)
void Rs485_Init(void)
{
? ? GPIO_InitTypeDef GPIO_InitStruct;
? ? RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); ??
?
? ? GPIO_InitStruct.GPIO_Pin ? = GPIO_Pin_8; ? ? ? ?
? ? GPIO_InitStruct.GPIO_Mode ?= GPIO_Mode_OUT;?
? ? GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;?
? ? GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;?
? ? GPIO_InitStruct.GPIO_PuPd ?= GPIO_PuPd_UP; ?
? ? GPIO_Init(GPIOG, &GPIO_InitStruct); ? ??
?
// ?RS485_TX_EN = 0; //默認(rèn)為接收模式
}
④主函數(shù)
int main(void)
{
? ? char *tx_buf = "I believe I can fly!";
? ? u8 len;
? ? Led_Init();
? ? Key_Init();
? ? Systick_Init();
? ? Uart1_Init();
? ? Uart2_Init();
? ? Rs485_Init();
?
? ? printf("Usart test succeeded!\r\n");
? ? while(1)
? ? {
? ? ? ? if(!KEY0) ? ? ? ? ? ?//KEY1按鍵按下
? ? ? ? { ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? delay_ms(10); ? ?//消抖動
? ? ? ? ? ? if(!KEY0)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? while(!KEY0);
? ? ? ? ? ? ? ? RS485_TX_EN = 1; //發(fā)送模式,RS485_TX_EN是自定義的一個宏,即對PG8進(jìn)行置位復(fù)位
? ? ? ? ? ? ? ? len = strlen(tx_buf);
? ? ? ? ? ? ? ? while(len--)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? USART_SendData(USART2, *tx_buf++);
? ? ? ? ? ? ? ? ? ? while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? printf("Send data succeeded!\r\n"); //printf函數(shù)已經(jīng)重定義
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if(!KEY1) ? ? ? ? ? ? ?//KEY1按鍵按下
? ? ? ? {
? ? ? ? ? ? delay_ms(10); ? ? ?//消抖動
? ? ? ? ? ? if(!KEY1) ? ? ? ? ?//等待按鍵松開
? ? ? ? ? ? {
? ? ? ? ? ? ? ? while(!KEY1);
? ? ? ? ? ? ? ? RS485_TX_EN = 0; ? ?//接收模式
? ? ? ? ? ? ? ? if(rx_flag)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? rx_flag = 0; //清除標(biāo)志
? ? ? ? ? ? ? ? ? ? printf("Receive data: %s\r\n", rx_buf);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
?
————————————————
版權(quán)聲明:本文為CSDN博主「口袋里のInit」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wangguchao/article/details/80200402
總結(jié)
- 上一篇: 睡眠阶段分期——SVM和ELM分别与粒子
- 下一篇: 7723java乐高,乐高EV3——le