MODBUS移植STM32,分別配置STM32作從機和主機

MODBUS移植STM32,分別配置STM32作從機和主機

近期自學了MODBUS通訊協議,也從網上找了不少資料,本身也分別作了從機和主機的配置,如今進行配合操做緩存

  1. MCU採用STM32F103C8T6
  2. 實現功能,主機分別對從機實現讀和寫的操做
  3. 主機要用到一個外部中斷實現發數據的操做

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步後復位爲原來值致使的。

相關文章
相關標籤/搜索