MODBUS移植STM32,分別配置STM32作從機和主機
近期自學了MODBUS通訊協議,也從網上找了不少資料,本身也分別作了從機和主機的配置,如今進行配合操做緩存
- MCU採用STM32F103C8T6
- 實現功能,主機分別對從機實現讀和寫的操做
- 主機要用到一個外部中斷實現發數據的操做
1、配置從機
1.一、配置系統實現定時1MS的功能
初始化系統時鐘爲72MHZ安全
/****************************************************************************** * @brief 選擇外部時鐘或者內部時鐘並進行倍頻 * @param RCC_PLLSource:PLL時鐘源 : 能夠選擇:RCC_PLLSource_HSI_Div二、RCC_PLLSource_HSE_Div二、RCC_PLLSource_HSE_Div1 PLLMUL:PLL輸入時鐘的倍頻係數 範圍:RCC_CFGR_PLLMULL2~16 PLL時鐘根據時鐘和倍頻來肯定,選擇內部時鐘最高64M * @retval ******************************************************************************/ void SysClock_Configuration(uint32_t RCC_PLLSource, uint32_t PLLMUL) { __IO uint32_t HSEStatus = 0; RCC_ClocksTypeDef get_rcc_clock; RCC_DeInit(); // Resets the RCC clock configuration to the default reset state. if(RCC_PLLSource_HSI_Div2 != RCC_PLLSource) //選擇外部時鐘 { RCC_HSEConfig(RCC_HSE_ON); //打開外部時鐘 if(RCC_WaitForHSEStartUp() == SUCCESS) //等待外部時鐘開啓 { HSEStatus = 1; } else { //外部時鐘打開失敗 RCC_PLLSource = RCC_PLLSource_HSI_Div2; //自動選擇內部時鐘 PLLMUL = RCC_CFGR_PLLMULL16; //配頻到64MHZ RCC_HSEConfig(RCC_HSE_OFF); //關閉外部時鐘 RCC_HSICmd(ENABLE); //打開內部時鐘 } } else { //內部時鐘 RCC_PLLSource = RCC_PLLSource_HSI_Div2; //自動選擇內部時鐘 PLLMUL = RCC_CFGR_PLLMULL16; //倍頻到64MHZ RCC_HSEConfig(RCC_HSE_OFF); //關閉外部時鐘 RCC_HSICmd(ENABLE); //打開內部時鐘 } RCC_HCLKConfig(RCC_SYSCLK_Div1); //HCLK(AHB)時鐘爲系統時鐘1分頻 RCC_PCLK1Config(RCC_HCLK_Div2); //PCLK(APB1)時鐘爲HCLK時鐘2分頻 RCC_PCLK2Config(RCC_HCLK_Div1); //PCLK(APB2)時鐘爲HCLK時鐘1分頻 //0-24MHz時,取FLASH_Latency_0; //24-48MHz,取FLASH_Latency_1; //48-72MHz時,取FLASH_Latency_2。 FLASH_SetLatency(FLASH_Latency_2); //不用到能夠不配置 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); RCC_PLLConfig(RCC_PLLSource, PLLMUL); //PLL時鐘配置,時鐘源 * PLLMUL RCC_PLLCmd(ENABLE); //開啓PLL時鐘,並等待PLL時鐘準備好 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //選擇PLL時鐘爲系統時鐘 while(RCC_GetSYSCLKSource() != 0x08); //Wait till PLL is used as system clock source RCC_ClockSecuritySystemCmd(ENABLE); //打開時鐘安全系統 RCC_GetClocksFreq(&get_rcc_clock); //仿真的時候就能夠在結構體get_rcc_clock中看見各個外設的時鐘了 }
配置TIM3時鐘函數
NVIC包含函數fetch
#include 「USART.h」
#include 「TIMER.h」
ui
// 使用TIM3,對MODBUS協議定時 #define MODBUS_TIM TIM3 #define MODBUS_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd #define MODBUS_TIM_CLK RCC_APB1Periph_TIM3 #define MODBUS_TIM_IRQ TIM3_IRQn #define MODBUS_TIM_IRQHandler TIM3_IRQHandler #define MODBUS_TIM_Period (1000-1) #define MODBUS_TIM_Prescaler (72-1)
/****************************************************************************** * @brief MODBUS_TIM_Config:TIM3初始化 * @param * @retval ******************************************************************************/ void MODBUS_TIM_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; MODBUS_TIM_APBxClock_FUN(MODBUS_TIM_CLK, ENABLE); //開啓定時器時鐘,即內部時鐘CK_INT=72M TIM_TimeBaseStructure.TIM_Period=MODBUS_TIM_Period; //自動重裝載寄存器周的值(計數值) // 累計TIM_Period 個頻率後產生一個更新或者中斷 // 時鐘預分頻數爲71,則驅動計數器的時鐘CK_CNT = CK_INT / (71+1)=1M TIM_TimeBaseStructure.TIM_Prescaler= MODBUS_TIM_Prescaler; TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 時鐘分頻因子 ,基本定時器沒有,不用管 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; // 計數器計數模式,基本定時器只能向上計數,沒有計數模式的設置 TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // 重複計數器的值,基本定時器沒有,不用管 TIM_TimeBaseInit(MODBUS_TIM,&TIM_TimeBaseStructure); // 初始化定時器 TIM_ClearFlag(MODBUS_TIM,TIM_FLAG_Update); // 清除計數器中斷標誌位 TIM_ITConfig(MODBUS_TIM,TIM_IT_Update,ENABLE); // 開啓計數器中斷 TIM_Cmd(MODBUS_TIM, ENABLE); // 使能計數器 } /****************************************************************************** * @brief ALL_NVIC_Init:配置各個中斷優先級 * @param * @retval ******************************************************************************/ void ALL_NVIC_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 設置中斷組爲1 NVIC_InitStructure.NVIC_IRQChannel = MODBUS_TIM_IRQ ; // 設置中斷來源 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 設置主優先級爲 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 設置搶佔優先級爲3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
配置中斷函數定時中斷函數放到了MODBUS_USART.c中spa
/****************************************************************************** * @brief MODBUS_TIM_IRQHandler:MODBUS定時器中斷函數 * @param * @retval ******************************************************************************/ void MODBUS_TIM_IRQHandler (void) //定時器中斷函數 { if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET ) { TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//清除中斷標誌位 } }
主函數3d
int main(void) { SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//設置系統時鐘,外部設置爲72MHZ,內部設置爲64MHZ MODBUS_TIM_Config(); ALL_NVIC_Init(); }
運行程序是否到斷點處,現象以下:調試
1.二、配置系統實現串口接收中斷的功能
使用USART2:PA2和PA3,配置串口GPIO口code
// 串口2-USART2 #define MODBUS_USART USART2 #define MODBUS_USART_CLK RCC_APB1Periph_USART2 #define MODBUS_USART_APBxClkCmd RCC_APB1PeriphClockCmd #define MODBUS_USART_BAUDRATE 9600 // USART GPIO 引腳宏定義 #define MODBUS_USART_GPIO_CLK RCC_APB2Periph_GPIOA #define MODBUS_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define MODBUS_USART_TX_GPIO_PORT GPIOA #define MODBUS_USART_TX_GPIO_PIN GPIO_Pin_2 #define MODBUS_USART_RX_GPIO_PORT GPIOA #define MODBUS_USART_RX_GPIO_PIN GPIO_Pin_3 // USART GPIO 中斷 #define MODBUS_USART_IRQ USART2_IRQn #define MODBUS_USART_IRQHandler USART2_IRQHandler
/****************************************************************************** * @brief MODBUS_USART_Config:MODBUS配置串口模式 * @param 無 * @retval 無 ******************************************************************************/ void MODBUS_USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; MODBUS_USART_GPIO_APBxClkCmd(MODBUS_USART_GPIO_CLK, ENABLE); // 打開串口GPIO 的時鐘 MODBUS_USART_APBxClkCmd(MODBUS_USART_CLK, ENABLE); // 打開串口外設的時鐘 // 將USART1 Tx 的GPIO 配置爲推輓複用模式 GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(MODBUS_USART_TX_GPIO_PORT, &GPIO_InitStructure); // 將USART Rx 的GPIO 配置爲浮空輸入模式 GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(MODBUS_USART_RX_GPIO_PORT, &GPIO_InitStructure); // 配置串口的工做參數 USART_InitStructure.USART_BaudRate = MODBUS_USART_BAUDRATE; // 配置波特率 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(MODBUS_USART, &USART_InitStructure); // 完成串口的初始化配置 USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE); // 使能串口接收中斷 USART_Cmd(MODBUS_USART, ENABLE); // 使能串口 } /****************************************************************************** * @brief ALL_NVIC_Init:配置各個中斷優先級 * @param * @retval ******************************************************************************/ void ALL_NVIC_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 設置中斷組爲1 NVIC_InitStructure.NVIC_IRQChannel = MODBUS_USART_IRQ ; // 設置中斷來源 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 設置主優先級爲 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 設置搶佔優先級爲0 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
配置串口發送字符函數,爲以後發數據作準備blog
/****************************************************************************** * @brief Usart_SendByte:發送一個字符 * @param * @retval ******************************************************************************/ void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch) { USART_SendData(pUSARTx,ch); // 發送一個字節數據到USART while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); // 等待發送數據寄存器爲空 }
配置串口接收中斷函數
/****************************************************************************** * @brief MODBUS_USART_IRQHandler:MODBUS串口中斷函數 * @param * @retval ******************************************************************************/ void MODBUS_USART_IRQHandler(void) { uint8_t ucTemp; if (USART_GetITStatus(MODBUS_USART,USART_IT_RXNE)!=RESET) //判斷是否有數據接收 {} }
主函數
int main(void) { SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//設置系統時鐘,外部設置爲72MHZ,內部設置爲64MHZ MODBUS_USART_Config(); ALL_NVIC_Init(); }
用串口助手配置爲9600BT/s,發送數據
1.三、配置系統實現收到數據後延時3.5T的功能
在配置延時3.5T功能同時,配置MODBUS結構體用來保存相關數據,創建MODBUS.c和MODBUS.h
嵌套關係
#include 「stm32f10x.h」
#include 「modbusCRC.h」
#include 「USART.h」
typedef struct { unsigned char myadd; //本設備的地址 unsigned char rcbuf[100]; //MODBUS接收緩衝區 unsigned int timout; //MODbus的數據斷續時間 unsigned char recount; //MODbus端口已經收到的數據個數 unsigned char timrun; //MODbus定時器是否計時的標誌 unsigned char reflag; //收到一幀數據的標誌 unsigned char Sendbuf[100]; //MODbus發送緩衝區 }MODBUS;
配置串口接收函數
/****************************************************************************** * @brief MODBUS_USART_IRQHandler:MODBUS串口中斷函數 * @param * @retval ******************************************************************************/ void MODBUS_USART_IRQHandler(void) { uint8_t ucTemp; if (USART_GetITStatus(MODBUS_USART,USART_IT_RXNE)!=RESET) //判斷是否有數據接收 { ucTemp = USART_ReceiveData( MODBUS_USART ); //將接收的一個字節保存 modbus.rcbuf[modbus.recount++]=ucTemp; //保存到MODBUS的接收緩存區 modbus.timout=0; //串口接收數據的過程當中,定時器不計時 if(modbus.recount==1) //收到主機發來的一幀數據的第一字節 { modbus.timrun=1; //啓動定時 } } }
配置定時器函數
/****************************************************************************** * @brief MODBUS_TIM_IRQHandler:MODBUS定時器中斷函數 * @param * @retval ******************************************************************************/ void MODBUS_TIM_IRQHandler (void) //定時器中斷函數 { if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET ) { if(modbus.timrun!=0) //串口發送數據是否結束,結束就讓定時器定時 { modbus.timout++; //定時器定時1毫秒,並開始記時 TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//清除中斷標誌位 } }
結合串口和定時器運行程序,用串口助手發送助手,調試程序是否進入斷點
1.四、配置系統處理數據的功能
配置到這裏咱們基本將MODBUS的時序配置好,可是數據不曾處理,接下來咱們對數據處理,並用MODBUS調試助手驗證
其中MODBUS包含
#include 「modbusCRC.h」
#include 「USART.h」
其中MODBUS_USART包含
#include 「USART.h」
#include 「MODBUS.h」
#include 「TIMER.h」
在MODBUS中定義寄存器
unsigned int Reg[]={0x0000, //本設備寄存器中的值 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, };
在MODBUS中包含下列函數,函數不一一講解,能夠細看,其中處理函數不發送錯誤碼,實際中功能碼夠了
/****************************************************************************** * @brief Modbud_fun3:3號功能碼處理 ---主機要讀取本從機的寄存器 * @param * @retval ******************************************************************************/ void Modbud_fun3(void) { u16 Regadd; u16 Reglen; u16 byte; u16 i,j; u16 crc; Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3]; //獲得要讀取的寄存器的首地址 Reglen=modbus.rcbuf[4]*256+modbus.rcbuf[5]; //獲得要讀取的寄存器的數量 i=0; modbus.Sendbuf[i++]=modbus.myadd; //發送本設備地址 modbus.Sendbuf[i++]=0x03; //發送功能碼 byte=Reglen*2; //要返回的數據字節數 //modbus.Sendbuf[i++]=byte/256; modbus.Sendbuf[i++]=byte%256; //發送要返回的數據字節數 for(j=0;j<Reglen;j++) { modbus.Sendbuf[i++]=Reg[Regadd+j]/256; //發送讀取數據字節數的高位 modbus.Sendbuf[i++]=Reg[Regadd+j]%256; //發送讀取數據字節數的低位 } crc=crc16(modbus.Sendbuf,i); //CRC校驗 modbus.Sendbuf[i++]=crc/256; //發送CRC的值高位 modbus.Sendbuf[i++]=crc%256; //發送CRC的值低位 for(j=0;j<i;j++) //經過串口逐個發送 Usart_SendByte( MODBUS_USART,modbus.Sendbuf[j]); } /****************************************************************************** * @brief Modbud_fun6:6號功能碼處理,寫寄存器 * @param * @retval ******************************************************************************/ void Modbud_fun6() { unsigned int Regadd; unsigned int val; unsigned int i,crc,j; i=0; Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3]; //獲得要修改的地址 val=modbus.rcbuf[4]*256+modbus.rcbuf[5]; //修改後的值 Reg[Regadd]=val; //修改本設備相應的寄存器 //如下爲迴應主機 modbus.Sendbuf[i++]=modbus.myadd; //發送本設備地址 modbus.Sendbuf[i++]=0x06; //發送功能碼 modbus.Sendbuf[i++]=Regadd/256; //發送修改地址高位 modbus.Sendbuf[i++]=Regadd%256; //發送修改地址低位 modbus.Sendbuf[i++]=val/256; //發送修改的值高位 modbus.Sendbuf[i++]=val%256; //發送修改的值低位 crc=crc16(modbus.Sendbuf,i); //校驗地址、功能碼、地址、數據 modbus.Sendbuf[i++]=crc/256; //發送CRC的值高位 modbus.Sendbuf[i++]=crc%256; //發送CRC的值低位 for(j=0;j<i;j++) //經過串口逐個發送 Usart_SendByte( MODBUS_USART,modbus.Sendbuf[j]); } /****************************************************************************** * @brief Mosbus_Event:MODBUS處理數據程序 * @param * @retval ******************************************************************************/ void Mosbus_Event(void) { unsigned int crc; unsigned int rccrc; unsigned char i=0; if(modbus.reflag==0) //沒有收到MODbus的數據包 { return ; //沒有收處處理指令,繼續等待下一條數據 } while(modbus.rcbuf[i++]!=modbus.myadd); crc= crc16(&modbus.rcbuf[0], modbus.recount-2); //計算校驗碼 rccrc=modbus.rcbuf[modbus.recount-2]*256 + modbus.rcbuf[modbus.recount-1]; //收到的校驗碼 if(crc == rccrc) //數據包符合CRC校驗規則 { if(modbus.rcbuf[0] == modbus.myadd) //確認數據包是不是發給本設備的 { switch(modbus.rcbuf[1]) //分析功能碼 { case 0: break; case 1: break; case 2: break; case 3: Modbud_fun3(); break; //3號功能碼處理 case 4: break; case 5: break; case 6: Modbud_fun6(); break; //6號功能碼處理 case 7: break; //.... } } else if(modbus.rcbuf[0] == 0) //廣播地址,不處理 { } } //數據包不符合CRC校驗規則 modbus.recount=0; //清除緩存計數 modbus.reflag=0; //從新開始執行處理函數 }
在MODBUS_USART中包含下列函數
/****************************************************************************** * @brief MODBUS_Init:MODBUS初始化 * @param * @retval ******************************************************************************/ void MODBUS_Init(void) { MODBUS_TIM_Config(); MODBUS_USART_Config(); modbus.myadd=4; //初始化本從設備的地址 modbus.timrun=0; //初始化MODbus定時器中止計時 } /****************************************************************************** * @brief MODBUS_USART_IRQHandler:MODBUS串口中斷函數 * @param * @retval ******************************************************************************/ void MODBUS_USART_IRQHandler(void) { uint8_t ucTemp; if (USART_GetITStatus(MODBUS_USART,USART_IT_RXNE)!=RESET) //判斷是否有數據接收 { ucTemp = USART_ReceiveData( MODBUS_USART ); //將接收的一個字節保存 modbus.rcbuf[modbus.recount++]=ucTemp; //保存到MODBUS的接收緩存區 modbus.timout=0; //串口接收數據的過程當中,定時器不計時 if(modbus.recount==1) //收到主機發來的一幀數據的第一字節 { modbus.timrun=1; //啓動定時 } } } /****************************************************************************** * @brief MODBUS_TIM_IRQHandler:MODBUS定時器中斷函數 * @param * @retval ******************************************************************************/ void MODBUS_TIM_IRQHandler (void) //定時器中斷函數 { if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET ) { if(modbus.timrun!=0) //串口發送數據是否結束,結束就讓定時器定時 { modbus.timout++; //定時器定時1毫秒,並開始記時 if(modbus.timout>=8) //間隔時間達到了時間,假設爲8T,實際3.5T便可 { modbus.timrun=0; //關閉定時器--中止定時 modbus.reflag=1; //收到一幀數據,開始處理數據 } } TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//清除中斷標誌位 } }
主函數
int main(void) { SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//設置系統時鐘,外部設置爲72MHZ,內部設置爲64MHZ MODBUS_Init(); ALL_NVIC_Init(); while(1) Mosbus_Event(); }
運行MODBUS調試助手,配置9600TB/s,地址設置爲4
發送:04 06 00 00 00 01 48 5F
表示向0號寄存器寫1
返回:04 06 00 00 00 01 48 5F
發送:04 03 00 00 00 01 84 5F
表示讀取0號地址後一個寄存器的值
返回:04 03 02 00 01 B5 84
2、配置主機
2.一、配置系統實現定時1MS的功能
如上配置點擊跳轉
2.二、配置系統實現串口接收中斷的功能
如上配置點擊跳轉
2.三、配置一個USART1和外部中斷功能
USART1配置,用來查看數據,這個串口僅僅用來查看數據,不用配置接收中斷
// 串口1-USART1 #define DEBUG1_USART USART1 #define DEBUG1_USART_CLK RCC_APB2Periph_USART1 #define DEBUG1_USART_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG1_USART_BAUDRATE 9600 // USART GPIO 引腳宏定義 #define DEBUG1_USART_GPIO_CLK RCC_APB2Periph_GPIOA #define DEBUG1_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG1_USART_TX_GPIO_PORT GPIOA #define DEBUG1_USART_TX_GPIO_PIN GPIO_Pin_9 #define DEBUG1_USART_RX_GPIO_PORT GPIOA #define DEBUG1_USART_RX_GPIO_PIN GPIO_Pin_10
/****************************************************************************** * @brief DEBUG_USART_Init:配置串口調試 * @param 無 * @retval 無 ******************************************************************************/ void DEBUG_USART_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; DEBUG1_USART_GPIO_APBxClkCmd(DEBUG1_USART_GPIO_CLK, ENABLE); // 打開串口GPIO 的時鐘 DEBUG1_USART_APBxClkCmd(DEBUG1_USART_CLK, ENABLE); // 打開串口外設的時鐘 // 將USART1 Tx 的GPIO 配置爲推輓複用模式 GPIO_InitStructure.GPIO_Pin = DEBUG1_USART_TX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(DEBUG1_USART_TX_GPIO_PORT, &GPIO_InitStructure); // 將USART Rx 的GPIO 配置爲浮空輸入模式 GPIO_InitStructure.GPIO_Pin = DEBUG1_USART_RX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(DEBUG1_USART_RX_GPIO_PORT, &GPIO_InitStructure); // 配置串口的工做參數 USART_InitStructure.USART_BaudRate = DEBUG1_USART_BAUDRATE; // 配置波特率 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(DEBUG1_USART, &USART_InitStructure); // 完成串口的初始化配置 USART_ITConfig(DEBUG1_USART, USART_IT_RXNE, ENABLE); // 使能串口接收中斷 USART_Cmd(DEBUG1_USART, ENABLE); // 使能串口 }
配置外部中斷,採用PA0按鍵的功能
//引腳定義 #define KEY_UP_INT_GPIO_PORT GPIOA #define KEY_UP_INT_GPIO_CLK (RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO) #define KEY_UP_INT_GPIO_PIN GPIO_Pin_0 #define KEY_UP_INT_EXTI_PORTSOURCE GPIO_PortSourceGPIOA #define KEY_UP_INT_EXTI_PINSOURCE GPIO_PinSource0 #define KEY_UP_INT_EXTI_LINE EXTI_Line0 #define KEY_UP_INT_EXTI_IRQ EXTI0_IRQn #define KEY_UP_IRQHandler EXTI0_IRQHandler
void EXTI_Key_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; RCC_APB2PeriphClockCmd(KEY_UP_INT_GPIO_CLK,ENABLE); //開啓按鍵GPIO 口的時鐘 /*--------------------------KEY1 配置---------------------*/ GPIO_InitStructure.GPIO_Pin = KEY_UP_INT_GPIO_PIN; // 選擇按鍵用到的GPIO GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 配置爲下拉輸入,由於浮空輸入其餘的管腳對他的干擾很大 GPIO_Init(KEY_UP_INT_GPIO_PORT, &GPIO_InitStructure); GPIO_EXTILineConfig(KEY_UP_INT_EXTI_PORTSOURCE,KEY_UP_INT_EXTI_PINSOURCE);// 選擇EXTI 的信號源 EXTI_InitStructure.EXTI_Line = KEY_UP_INT_EXTI_LINE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // EXTI 爲中斷模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿中斷 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中斷 EXTI_Init(&EXTI_InitStructure); }
這裏須要主要的一點,採用下拉的方式,不要採用浮空輸入的方式
在MODBUS_USART中配置外部中斷函數
/****************************************************************************** * @brief MODBUS_TIM_IRQHandler:MODBUS定時器中斷函數 * @param * @retval ******************************************************************************/ void MODBUS_TIM_IRQHandler (void) //定時器中斷函數 { if ( TIM_GetITStatus( MODBUS_TIM, TIM_IT_Update) != RESET ) { if(modbus.timrun!=0) //串口發送數據是否結束,結束就讓定時器定時 { modbus.timout++; //定時器定時1毫秒,並開始記時 if(modbus.timout>=8) //間隔時間達到了時間,假設爲8T,實際3.5T便可 { modbus.timrun=0; //關閉定時器--中止定時 modbus.reflag=1; //收到一幀數據,開始處理數據 } } TIM_ClearITPendingBit(MODBUS_TIM , TIM_FLAG_Update);//清除中斷標誌位 } } /****************************************************************************** * @brief KEY_UP_IRQHandler:外部發送數據方式 * @param * @retval ******************************************************************************/ void KEY_UP_IRQHandler(void) { if (EXTI_GetITStatus(KEY_UP_INT_EXTI_LINE) != RESET) //確保是否產生了EXTI Line 中斷 { Usart_SendByte( DEBUG1_USART,modbus.myadd); Usart_SendByte( DEBUG1_USART,0x03); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0x02); Usart_SendByte( DEBUG1_USART,0xC4); Usart_SendByte( DEBUG1_USART,0x5E); Usart_SendByte( MODBUS_USART,modbus.myadd); Usart_SendByte( MODBUS_USART,0x03); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0x02); Usart_SendByte( MODBUS_USART,0xC4); Usart_SendByte( MODBUS_USART,0x5E); EXTI_ClearITPendingBit(KEY_UP_INT_EXTI_LINE); //清除中斷標誌位 } }
打開串口USART2發送數據,用USART1接收數據
從機返回寫指令數據處理
發送:04 06 00 00 00 01 48 5F
對寄存器00 00 地址寫1
返回:00 01
從機返回讀指令數據處理
發送:04 03 02 00 01 B5 84
對寄存器00 00 地址d讀1
返回:00 01
2.四、配置系統實現收到數據後延時3.5T的功能
如上配置點擊跳轉
2.五、配置系統處理數據的功能
這個地方和從機不一樣
/****************************************************************************** * @brief Modbud_fun3:3號功能碼處理 ---主機要讀取從機的寄存器後,將信息保存到主機指定長度的寄存器 * @param * @retval ******************************************************************************/ void Modbud_fun3(void) { unsigned int Regadd=0,i=0,j,Reglen; Reglen=modbus.rcbuf[2]; //獲得讀取的寄存器的數量 for(i=0;i<Reglen;i++) //處理讀取的數據保存到主機相應寄存器 { //數據從寄存器的第一個保存到指定數量 Reg[Regadd]=modbus.rcbuf[3+i]*256; //將數據高位保存寄存器 Usart_SendByte( DEBUG1_USART,Reg[Regadd]/256); //發送到另外一個串口顯示 i++; //數據增長,處理低位 Reg[Regadd]=Reg[Regadd]+modbus.rcbuf[i+3]; //發送到另外一個串口顯示 Usart_SendByte( DEBUG1_USART,Reg[Regadd]%256); //將數據低位保存寄存器 Regadd++; //處理完高位和低位數據,進行下一個數據 } } /****************************************************************************** * @brief Modbud_fun6:6號功能碼處理,主機向從機指定寄存器寫數據,寫完後,返回修改的數據 * @param * @retval ******************************************************************************/ void Modbud_fun6() { unsigned int Regadd,i=0,crc,j,val; Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3]; //獲得要更改寄存器的地址 Reg[Regadd]=modbus.rcbuf[4]*256+modbus.rcbuf[5]; //將從機修改的數據再保存到主機寄存器中 Usart_SendByte( DEBUG1_USART,Reg[Regadd]/256); //另外一個串口顯示修改的數據 Usart_SendByte( DEBUG1_USART,Reg[Regadd]%256); //另外一個串口顯示修改的數據 }
主函數以下
int main(void) { SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//設置系統時鐘,外部設置爲72MHZ,內部設置爲64MHZ MODBUS_Init(); ALL_NVIC_Init(); Usart_SendByte( DEBUG1_USART,0x32); CODE_End(); while(1) Mosbus_Event(); }
3、主機向從機下發命令
3.一、主機向從機下發寫命令
將主機的串口鏈接從機的串口,按下主機的按鍵,在主機的另外一個串口查看是否有數據接收
按鍵中斷配置函數
Usart_SendByte( DEBUG1_USART,modbus.myadd); Usart_SendByte( DEBUG1_USART,0x06); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0xFF); Usart_SendByte( DEBUG1_USART,0xFF); Usart_SendByte( DEBUG1_USART,0x88); Usart_SendByte( DEBUG1_USART,0x2F); Usart_SendByte( MODBUS_USART,modbus.myadd); Usart_SendByte( MODBUS_USART,0x06); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0xFF); Usart_SendByte( MODBUS_USART,0xFF); Usart_SendByte( MODBUS_USART,0x88); Usart_SendByte( MODBUS_USART,0x2F);
至關於主機向從機發數據:0x04 0x06 0x00 0x00 0xFF 0xFF 0x88
返回:0xFF 0xFF
3.二、主機向從機下發讀命令
將主機的串口鏈接從機的串口,按下主機的按鍵,在主機的另外一個串口查看是否有數據接收
Usart_SendByte( MODBUS_USART,modbus.myadd); Usart_SendByte( MODBUS_USART,0x03); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0x00); Usart_SendByte( MODBUS_USART,0x01); Usart_SendByte( MODBUS_USART,0x84); Usart_SendByte( MODBUS_USART,0x5F); Usart_SendByte( DEBUG1_USART,modbus.myadd); Usart_SendByte( DEBUG1_USART,0x03); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0x00); Usart_SendByte( DEBUG1_USART,0x01); Usart_SendByte( DEBUG1_USART,0x84); Usart_SendByte( DEBUG1_USART,0x5F);
至關於主機向從機發數據:0x04 0x06 0x00 0x00 0x01 0x84 0x5F
返回:0x00 0x00
這裏返回00是因爲以前調試過程完3.1步後復位爲原來值致使的。