使用芯片:stm32f103ret

开发环境:cubemx5.3 + keil5

freemodbus版本:V1.6

CUBEMX配置步骤:

  • 系统及时钟配置:使用外部时钟

freemodbus主机代码多少钱 freemodbus优缺点_freemodbus主机代码多少钱

freemodbus主机代码多少钱 freemodbus优缺点_嵌入式硬件_02

  • TIM4作为freemodbus串口定时器:

freemodbus主机代码多少钱 freemodbus优缺点_freemodbus主机代码多少钱_03

       在RTU模式下,串行链路是以byte为单位进行发送数据的。Modbus-RTU协议中有一个字符时间的概念,Modbus-RTU总线是通过时间间隔来判断一帧数据结束的。波特率小于19200时,3.5个字符时间内没有收到新的数据,则认为这一帧数据结束。将定时器设置为每50us的时长为一个基准,传入的usTim1Timerout50us变量给自动装载即可,prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符 的等待时间已经到;波特率大于19200时为固定时长1750us。

freemodbus主机代码多少钱 freemodbus优缺点_stm32_04

freemodbus主机代码多少钱 freemodbus优缺点_单片机_05

 图中位置在程序中传入。

  • 串口2用作modbus通信接口

freemodbus主机代码多少钱 freemodbus优缺点_单片机_06

 

  • NVIC配置:取消自动生成中断函数选项,最后自己在程序中添加修改
  • 串口的中断优先级高于定时器

freemodbus主机代码多少钱 freemodbus优缺点_嵌入式硬件_07

 

Freemodbus移植

  • 下载freemodbus-v1.6解压,
  • 需要用到的文件有:modbus文件中的所有文件,port中的文件

freemodbus主机代码多少钱 freemodbus优缺点_单片机_08

freemodbus主机代码多少钱 freemodbus优缺点_IT_09

 

第一次编译报错如下(需要删除掉demo.c中的main函数):

freemodbus主机代码多少钱 freemodbus优缺点_freemodbus主机代码多少钱_10

其中串口需要修改的文件有portserial.c 定时器需要修改的文件 有porttimer.c

寄存器的读写功能通过新建Modbusdemo.c中实现。

  1. portserial.c中的函数:保留prvvUARTTxReadyISR,prvvUARTRxISR
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */	 
    if(xRxEnable)//接收使能
    {		
        __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);	// 使能接收非空中断	
		//低电平接收  			
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET); //根据实际使用芯片决定是否有此步骤
    }
    else
    {
        __HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE);		// 禁能接收非空中断
    }
    if(xTxEnable)//发送使能
    {
	//高电平发送	
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);	  //根据实际使用芯片决定是否有此步骤 
        __HAL_UART_ENABLE_IT(&huart2, UART_IT_TXE);			// 使能发送为空中断			 
    }
    else
    {
        __HAL_UART_DISABLE_IT(&huart2, UART_IT_TXE);		// 禁能发送为空中断
    }
}
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    huart2.Instance = USART2;
    huart2.Init.BaudRate = ulBaudRate;
    huart2.Init.StopBits = UART_STOPBITS_1;
    huart2.Init.Mode = UART_MODE_TX_RX;
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart2.Init.OverSampling = UART_OVERSAMPLING_16;
    switch(eParity)
    {
    // 奇校验
    case MB_PAR_ODD:
        huart2.Init.Parity = UART_PARITY_ODD;
        huart2.Init.WordLength = UART_WORDLENGTH_9B;			// 带奇偶校验数据位为9bits
        break;
    // 偶校验
    case MB_PAR_EVEN:
        huart2.Init.Parity = UART_PARITY_EVEN;
        huart2.Init.WordLength = UART_WORDLENGTH_9B;			// 带奇偶校验数据位为9bits
        break;
    // 无校验
    default:
        huart2.Init.Parity = UART_PARITY_NONE;
        huart2.Init.WordLength = UART_WORDLENGTH_8B;			// 无奇偶校验数据位为8bits
        break;
    }
    return HAL_UART_Init(&huart2) == HAL_OK ? TRUE : FALSE;
}
BOOL xMBPortSerialPutByte( CHAR ucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
	 USART2->DR = ucByte;
	  while(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC)== RESET){}
    return TRUE;
}
BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
	 *pucByte = (USART2->DR & (uint16_t)0x00FF);
    return TRUE;
}
void USART2_IRQHandler(void)
{
   if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE))			// 接收非空中断标记被置位
    {
        __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE);			// 清除中断标记
        prvvUARTRxISR();						// 通知modbus有数据到达
    }
    if((__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE) == SET)&&(__HAL_UART_GET_IT_SOURCE(&huart2, UART_FLAG_TC) == RESET))				// 发送为空中断标记被置位
    {
        __HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TXE);			// 清除中断标记
        prvvUARTTxReadyISR();						// 通知modbus数据可以发松		
    }
}

porttimer.c中的函数如下:保留vMBPortTimersDisable,prvvTIMERExpiredISR

BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 7199;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = usTim1Timerout50us-1;   //
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);      // 清除一下定时器的中断标记,防止使能中断后直接触发中断
  __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);	      // 使能定时器更新中断
 
    return TRUE;
}

inline void vMBPortTimersEnable(  )
{
   __HAL_TIM_SET_COUNTER(&htim4, 0);		// 清空计数器
    __HAL_TIM_ENABLE(&htim4);			// 使能定时器
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
}

inline void vMBPortTimersDisable(  )
{
    __HAL_TIM_DISABLE(&htim4);			// 禁能定时器
    /* Disable any pending timers. */
}
/// 定时器4中断服务程序
void TIM4_IRQHandler(void)
{
    if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE))	       // 更新中断标记被置位
    {
        __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);		// 清除中断标记
        prvvTIMERExpiredISR();			               // 通知modbus3.5个字符等待时间到
    }
}

文件demo.c中主要实现0X04

freemodbus主机代码多少钱 freemodbus优缺点_嵌入式硬件_11

 

以上是移植过程,只实现了04测试。为了防止运行异常,可以加入看门狗定时器