MODBUS移植STM32,分别配置STM32做从机和主机
近期自学了MODBUS通信协议,也从网上找了很多资料,自己也分别做了从机和主机的配置,现在进行配合操作
- MCU采用STM32F103C8T6
- 实现功能,主机分别对从机实现读和写的操作
- 主机要用到一个外部中断实现发数据的操作
一、配置从机
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、配置系统实现串口接收中断的功能

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

被折叠的 条评论
为什么被折叠?



