MODBUS移植STM32,分别配置STM32做从机和主机

本文介绍了如何将MODBUS协议移植到STM32上,详细讲解了从机和主机的配置步骤,包括定时1MS、串口接收中断、数据处理等功能,以及主机向从机的读写命令操作。通过STM32F103C8T6实现MODBUS通信,使用USART中断和外部中断功能。

MODBUS移植STM32,分别配置STM32做从机和主机

近期自学了MODBUS通信协议,也从网上找了很多资料,自己也分别做了从机和主机的配置,现在进行配合操作

  1. MCU采用STM32F103C8T6
  2. 实现功能,主机分别对从机实现读和写的操作
  3. 主机要用到一个外部中断实现发数据的操作

一、配置从机

1.1、配置系统实现定时1MS的功能

初始化系统时钟为72MHZ

/******************************************************************************
  * @brief  选择外部时钟或者内部时钟并进行倍频
  * @param  
	RCC_PLLSource:PLL时钟源 :
						可以选择:RCC_PLLSource_HSI_Div2、RCC_PLLSource_HSE_Div2、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包含函数

#include “USART.h”
#include “TIMER.h”

// 使用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中

/******************************************************************************
  * @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);//清除中断标志位
		}
}

主函数

int main(void)
{ 

	SysClock_Configuration(RCC_PLLSource_HSE_Div1,RCC_CFGR_PLLMULL9);//设置系统时钟,外部设置为72MHZ,内部设置为64MHZ
    MODBUS_TIM_Config();
     ALL_NVIC_Init();
}

运行程序是否到断点处,现象如下:

在这里插入图片描述

1.2、配置系统实现串口接收中断的功能

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoxiaodawei

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值