記錄一下,方便以後翻閱~
主要內容:
1) 485接口原理;
2) 相關實驗代碼解讀。
實驗功能:兩個開發板的485接口以A連接A,B連接B的方式直連。兩個開發版的代碼啓動後,主開發版上,按鍵KEY0控制W25Q128的寫入數據並傳至次開發版,且在串口調試助手上面顯示相關信息。同時,主開發版實時採集從次開發板傳來的數據,也顯示在串口調試助手上,LED0閃爍提示程序正在運行;另一個開發版,按鍵SW4按下後,寫入數據並傳至主開發板。
硬件連接:
主開發板
注意:
1)R19和R22是兩個偏置電阻,用來保證總線空閒時,AB之間的電壓差都會大約200mV,避免總線空閒時壓差不定邏輯混亂;
2)兩個485接口連接,A連接A,B連接B。
其它原理圖如下:
另一個開發板芯片採用STM32F103C8T6,原理圖如下所示:
備註:該開發板採用的MAX485收發器。
1. 485接口原理
1.1 串口
串口是一種接口標準,它規定了接口的電氣標準,簡單說只是物理層的一個標準。沒有規定接口插件電纜以及使用的協議,所以只要我們使用的接口插件電纜符合串口標準就可以在實際中靈活使用,在串口接口標準上使用各種協議進行通訊及設備控制。
典型的串行通訊標準是RS232和RS485,它們定義了電壓,阻抗等,但不對軟件協議給予定義。
1.2 RS232接口缺陷
缺點:
1.2.1 接口的信號電平值較高(+/-12V),易損壞接口電路的芯片;
1.2.2 傳輸速率較低,在異步傳輸時,波特率爲20Kbps;
1.2.3 接口使用一根信號線和一根信號返回線而構成共地的傳輸形式,這種共地傳輸容易產生共模干擾,所以抗噪聲干擾性弱;
1.2.4 傳輸距離有限,最大傳輸距離標準值爲50英尺,實際上也只能用在50米左右。
備註:早期設備大都採用RS232接口,因爲設備與設備之間的傳輸距離和速率要求都不高。
1.3 485接口簡述
485(一般稱作RS485/EIA-485)是隸屬於OSI模型物理層的電氣特性規定爲2線,半雙工,多點通信的標準。它的電氣特性和RS-232大不一樣。用纜線兩端的電壓差值來表示傳遞信號。RS485僅僅規定了接受端和發送端的電氣特性。它沒有規定或推薦任何數據協議。
1.4 485特點
1.4.1 接口電平低,不易損壞芯片。RS485的電氣特性:邏輯“1”以兩線間的電壓差爲+(26)V表示;邏輯“0”以兩線間的電壓差爲-(26)V表示。接口信號電平比RS232降低了,不易損壞接口電路的芯片;
1.4.2 傳輸速率高。10米時,RS485的數據最高傳輸速率可達35Mbps,在1200m時,傳輸速度可達100Kbps;
1.4.3 抗干擾能力強。RS485接口是採用平衡驅動器和差分接收器的組合,抗共模干擾能力增強,即抗噪聲干擾性好;
1.4.4 傳輸距離遠,支持節點多。RS485總線最長可以傳輸1200m以上(速率≤100Kbps)一般最大支持32個節點,如果使用特製的485芯片,可以達到128個或者256個節點,最大的可以支持到400個節點。
1.5 485連接方式
RS485推薦使用在點對點網絡中,線型,總線型,不能是星型,環型網絡。理想情況下RS485需要2個匹配電阻,其阻值要求等於傳輸電纜的特性阻抗(一般爲120Ω)。沒有特性阻抗的話,當所有的設備都靜止或者沒有能量的時候就會產生噪聲,而且線移需要雙端的電壓差。沒有終接電阻的話,會使得較快速的發送端產生多個數據信號的邊緣,導致數據傳輸出錯。485推薦的連接方式:
在上面的連接中,如果需要添加匹配電阻,我們一般在總線的起止端加入,也就是主機和設備4上面各加一個120Ω的匹配電阻。
2. 收發器SP3485
圖中A、B總線接口,用於連接485總線。RO是接收輸出端,DI是發送數據收入端,RE是接收使能信號(低電平有效),DE是發送使能信號(高電平有效)。
3. 相關實驗代碼解讀
這裏僅給出主開發板的實驗代碼,次開發板的實驗代碼跟主開發板的代碼基本差不多。
3.1 rs485.h頭文件代碼解讀
#ifndef __RS485_H
#define __RS485_H
#include "sys.h"
extern u8 RS485_RX_BUF[64]; //接收緩衝,最大64個字節//
extern u8 RS485_RX_CNT; //接收到的數據長度//
#define RS485_TX_EN PDout(7) //485模式控制.0,接收;1,發送//
#define EN_USART2_RX 1 //設0時不接收;設1時接收//
//三個函數申明//
void RS485_Init(u32 bound);
void RS485_Send_Data(u8 *buf,u8 len);
void RS485_Receive_Data(u8 *buf,u8 *len);
#endif
3.2 rs485.c文件代碼解讀
#include "sys.h"
#include "rs485.h"
#include "delay.h"
#include "usart.h"
#ifdef EN_USART2_RX //這裏EN_USART2_RX設1,接收//
u8 RS485_RX_BUF[64]; //接收緩存區,最大64個字節.
u8 RS485_RX_CNT=0;
void USART2_IRQHandler(void) //串口2中斷服務函數//
{
u8 res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //判斷USART2_SR寄存器的RXNE位是否不爲0,即是否接收到數據//
{
res =USART_ReceiveData(USART2); //如果不爲0,則接收到數據,讀取接收到的數據//
if(RS485_RX_CNT<64)
{
RS485_RX_BUF[RS485_RX_CNT]=res; //記錄接收到的值,放置在RS485_RX_BUF[]中//
RS485_RX_CNT++; //RS485_RX_CNT加1//
}
}
}
#endif
//初始化IO 串口2,pclk1:PCLK1時鐘頻率(Mhz),bound:波特率//
void RS485_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE); //使能GPIOA和D的時鐘//
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //使能USART2時鐘//
//初始化PD7,推輓輸出,用來控制SP3485的接受/發送使能//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //PD7端口配置//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推輓輸出//
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
//配置PA2爲複用推輓輸出//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓//
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置PA3爲浮空輸入//
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //PA3//
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入//
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE); //復位串口2//
RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE); //停止復位//
#ifdef EN_USART2_RX //如果使能了接收//
//如果EN_USART2_RX設1,爲接收,還要初始化USART//
USART_InitStructure.USART_BaudRate = bound; //波特率設置//
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //8位數據長度//
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);
//編寫串口2中斷優先級//
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串口2中斷//
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //搶佔優先級2級//
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //從優先級2級//
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道//
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //開啓接收中斷//
USART_Cmd(USART2, ENABLE); //使能串口//
#endif
RS485_TX_EN=0; //默認爲接收模式//
}
//RS485發送len個字節,buf:發送區首地址,len:發送的字節數(爲了和本代碼的接收匹配,這裏建議不要超過64個字節)//
void RS485_Send_Data(u8 *buf,u8 len)
{
u8 t;
RS485_TX_EN=1; //設置爲發送模式
for(t=0;t<len;t++) //循環發送數據
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); //判斷是否發送完成//
USART_SendData(USART2,buf[t]);
}
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
RS485_RX_CNT=0;
RS485_TX_EN=0; //設置爲接收模式
}
//RS485查詢接收到的數據,buf:接收緩存首地址,len:讀到的數據長度//
void RS485_Receive_Data(u8 *buf,u8 *len)
{
u8 rxlen=RS485_RX_CNT;
u8 i=0;
*len=0; //默認爲0
delay_ms(10); //等待10ms,連續超過10ms沒有接收到一個數據,則認爲接收結束//
if(rxlen==RS485_RX_CNT&&rxlen) //==的優先級比&&的高,判斷是否接收到數據//
{
for(i=0;i<rxlen;i++)
{
buf[i]=RS485_RX_BUF[i];
}
*len=RS485_RX_CNT; //記錄本次數據長度//
RS485_RX_CNT=0; //清零//
}
}
3.3 main.c文件代碼解讀
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "rs485.h"
int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 rs485buf[5];
delay_init(); //延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置中斷優先級分組爲組2:2位搶佔優先級,2位響應優先級//
uart_init(115200); //串口初始化爲115200//
LED_Init(); //初始化與LED連接的硬件接口//
KEY_Init(); //按鍵初始化//
RS485_Init(9600); //初始化RS485//
while(1)
{
key=KEY_Scan(0); //有鍵按下時,key就不爲0//
if(key==KEY0_PRES) //KEY0按下,發送一次數據//
{
for(i=0;i<5;i++)
{
rs485buf[i]=cnt+i; //填充發送緩衝區//
printf("\n發送數據爲:%d\r\n",rs485buf[i]);
}
RS485_Send_Data(rs485buf,5); //發送5個字節//
}
RS485_Receive_Data(rs485buf,&key); //每次循環都執行//
if(key) //接收到有數據//
{
if(key>5)key=5; //最大是5個數據//
for(i=0;i<key;i++)
printf("\n接收數據爲:%d\r\n",rs485buf[i]);
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0; //提示系統正在運行//
t=0;
cnt++;
}
}
}
4. 實驗結果
舊知識點
1)複習如何新建工程模板,可參考STM32學習心得二:新建工程模板;
2)複習基於庫函數的初始化函數的一般格式,可參考STM32學習心得三:GPIO實驗-基於庫函數;
3)複習寄存器地址,可參考STM32學習心得四:GPIO實驗-基於寄存器;
4)複習位操作,可參考STM32學習心得五:GPIO實驗-基於位操作;
5)複習寄存器地址名稱映射,可參考STM32學習心得六:相關C語言學習及寄存器地址名稱映射解讀;
6)複習時鐘系統框圖,可參考STM32學習心得七:STM32時鐘系統框圖解讀及相關函數;
7)複習延遲函數,可參考STM32學習心得九:Systick滴答定時器和延時函數解讀;
8)複習ST-LINK仿真器的參數配置,可參考STM32學習心得十:在Keil MDK軟件中配置ST-LINK仿真器;
9)複習ST-LINK調試方法,可參考STM32學習心得十一:ST-LINK調試原理+軟硬件仿真調試方法;
10)複習串口通信相關知識,可參考STM32學習心得十四:串口通信相關知識及配置方法。