STM32G0开发笔记-Platformio+libopencm3-FreeRTOS和FreeModbus库使用

这篇博客介绍了如何使用Platformio和libopencm3库在STM32G070开发板上搭建FreeRTOS实时操作系统,并结合FreeModbus库实现Modbus通信。首先,新建了名为freertos_modbus的项目,配置了上传和调试方式。接着,介绍了添加FreeModbus库的步骤,包括将库源码导入项目并配置library.json文件。在移植过程中,实现了FreeModbus所需的portevent、portserial和porttimer模块。此外,还创建了回调函数以处理Modbus的寄存器读写操作。最后,在main任务中启动Modbus处理任务,并进行了烧写测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title: STM32G0开发笔记-Platformio+libopencm3-FreeRTOS和FreeModbus库使用
tags:

  • STM32
  • MCU
  • STM32G070
  • libopencm3
  • MonkeyPi
  • FreeRTOS
  • Modbus
    categories:
    • STM32
      date: 2022-9-11 19:52:05

[原文:makerinchina.cn]

使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为FreeRTOS和FreeModbus库使用。

1 新建项目
  • 建立freertos_modbus项目

在PIO的Home页面新建项目,项目名称freertos_modbus,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;

  • 项目建立完成后在src目录下新建main.c主程序文件;
  • 修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:
upload_protocol = cmsis-dap
debug_tool = cmsis-dap
2 编写程序

直接在之前的FreeRTOS工程上进行添加;

2.1 添加 freeModbus 库

从git仓库下载源码: https://github.com/cwalter-at/freemodbus

将下载的源码中的mobus文件夹放置到工程的lib目录下,然后在modbus目录新建library.json文件,内容如下:

{
    "name": "FreeModbus",
    "version": "master",
    "repository":{
        "type":"git",
        "url":"https://github.com/cwalter-at/freemodbus"
    },
    "build": {
        "flags": [
            "-Iascii",
            "-Ifunctions",
            "-Iinclude",
            "-Irtu",
            "-Itcp"
        ],
        "srcFilter": [
            "+<*>"
        ]
    }
}

然后从FreeModbus源码中的 demo\BARE\port中复制文件到工程的src\modbus_port文件夹下,最后的文件夹结构如下:

在这里插入图片描述

2.2 移植
  • portevent:
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "FreeRTOS.h"
#include "task.h"

/* ----------------------- Variables ----------------------------------------*/
static eMBEventType eQueuedEvent;
static BOOL     xEventInQueue;
static uint32_t modbus_last_active_time = 0;

uint32_t get_modbus_last_active_time(void)
{
	return modbus_last_active_time;
}

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{
    xEventInQueue = FALSE;
    return TRUE;
}

BOOL
xMBPortEventPost( eMBEventType eEvent )
{
    xEventInQueue = TRUE;
    eQueuedEvent = eEvent;

	if (eEvent == EV_EXECUTE) {
		modbus_last_active_time = xTaskGetTickCount();
	}
    return TRUE;
}

BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
    BOOL            xEventHappened = FALSE;

    if( xEventInQueue )
    {
        *eEvent = eQueuedEvent;
        xEventInQueue = FALSE;
        xEventHappened = TRUE;
    }
    return xEventHappened;
}
  • portserial

这里使用RS485,因此需要对RS485使能端口进行配置,其他为串口的配置,然后在发送和接收中断时候调用modbus相关接口进行处理:

#include "port.h"

#include "FreeRTOS.h"
#include "queue.h"

#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/

xQueueHandle uart_queue;

#define RS485_1_CLOCK		RCC_GPIOB
#define RS485_1_EN_PORT		GPIOB
#define RS485_1_EN_PIN		GPIO8

static void rs485_delay(int n)
{
    while (--n) {
        __asm__ volatile ("nop");
    }
}

static inline void rs485_1_rx_mode(void)
{
    gpio_clear(RS485_1_EN_PORT, RS485_1_EN_PIN);
}

static inline void rs485_1_tx_mode(void)
{
    gpio_set(RS485_1_EN_PORT, RS485_1_EN_PIN);
}

static inline void rs485_gpio_init(void)
{
	rcc_periph_clock_enable(RS485_1_CLOCK);
	gpio_mode_setup(RS485_1_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_1_EN_PIN);

	rs485_1_rx_mode();
}

/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
    if (xRxEnable) {
        rs485_delay(10000);
        rs485_1_rx_mode();
        rs485_delay(10000);
		usart_enable_rx_interrupt(USART1);
    }
    else {
		usart_disable_rx_interrupt(USART1);
    }
    
    if (xTxEnable) {
        rs485_delay(10000);
        rs485_1_tx_mode();
        rs485_delay(10000);
		usart_enable_tx_interrupt(USART1);
    }
    else {
		usart_disable_tx_interrupt(USART1);

    }
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
	nvic_enable_irq(NVIC_USART1_IRQ);

	rcc_periph_clock_enable(RCC_GPIOB);
	gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7);
	gpio_set_af(GPIOB, GPIO_AF0, GPIO6 | GPIO7);

	rcc_periph_clock_enable(RCC_USART1);

	/* Set up USART/UART parameters using the libopencm3 helper functions */
	usart_set_baudrate(USART1, ulBaudRate);
	usart_set_databits(USART1, ucDataBits);
	usart_set_stopbits(USART1, USART_STOPBITS_1);
	usart_set_mode(USART1, USART_MODE_TX_RX);

    switch (eParity) {
        case MB_PAR_ODD:
			usart_set_parity(USART1, USART_PARITY_ODD);
            break;
        case MB_PAR_EVEN:
			usart_set_parity(USART1, USART_PARITY_EVEN);
            break;
        default:
			usart_set_parity(USART1, USART_PARITY_NONE);
            break;
    }

	usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);

	usart_enable(USART1);

    rs485_gpio_init();

    return TRUE;
}

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{

    usart_send_blocking(USART1, (uint16_t) ucByte);    
    
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
	*pucByte = usart_recv(USART1);

    return TRUE;
}


uint32_t uart1_isr, uart1_icr;

void usart1_isr(void)
{

	/* Check if we were called because of RXNE. */
	if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
	    ((USART_ISR(USART1) & USART_ISR_RXNE) != 0)) {

		/* Retrieve the data from the peripheral. */
        // usart_recv(USART1);

		pxMBFrameCBByteReceived();

	}


	/* Check if we were called because of TXE. */
	if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) &&
	    ((USART_ISR(USART1) & USART_ISR_TXE) != 0)) {

		/* Put data into the transmit register. */
		//usart_send(USART1, data);

		pxMBFrameCBTransmitterEmpty();

	}

}
  • porttimer
#include "port.h"

#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/timer.h>

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR( void );

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
	rcc_periph_clock_enable(RCC_TIM2);
	nvic_enable_irq(NVIC_TIM2_IRQ);
	rcc_periph_reset_pulse(RST_TIM2);

	timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);

	timer_set_prescaler(TIM2, (rcc_apb1_frequency/ 20000));

	timer_disable_preload(TIM2);
	timer_continuous_mode(TIM2);

	timer_set_period(TIM2, usTim1Timerout50us);
	timer_enable_counter(TIM2);

	timer_enable_irq(TIM2, TIM_DIER_UIE);

    return TRUE;
}


inline void
vMBPortTimersEnable(  )
{
	timer_set_counter(TIM2, 0);
	timer_enable_counter(TIM2);
}

inline void
vMBPortTimersDisable(  )
{
	timer_disable_counter(TIM2);
}

/* Create an ISR which is called whenever the timer has expired. This function
 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
 * the timer has expired.
 */
static void prvvTIMERExpiredISR( void )
{
    ( void )pxMBPortCBTimerExpired(  );
}

void
vMBPortTimersDelay( USHORT usTimeOutMS )
{
    vTaskDelay(pdMS_TO_TICKS(usTimeOutMS));
}

void tim2_isr(void)
{
	if (timer_get_flag(TIM2, TIM_SR_UIF)) {

		/* Clear compare interrupt flag. */
		timer_clear_flag(TIM2, TIM_SR_UIF);

        prvvTIMERExpiredISR();

	}
}

开启定时器和中断,用于modbus时序控制;

2.3 使用

在src目录新建 modbus_cb.h 和 modbus_cb.c 两个文件,实现寄存器、线圈的读写回调:

/// CMD4
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );

/// CMD6、3、16
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );

/// CMD1、5、15
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );

/// CMD4
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete );

基本的实现示例如下:

#include "modbus_cb.h"
#include "stdbool.h"

extern log(const char* fmt, ...);

// 输入寄存器
#define REG_INPUT_SIZE  32
uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];

// 保持寄存器
#define REG_HOLD_SIZE   32
uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];

// 线圈寄存器
#define REG_COILS_SIZE 16
uint8_t REG_COILS_BUF[REG_COILS_SIZE];

// 离散量
#define REG_DISC_SIZE  8
uint8_t REG_DISC_BUF[REG_DISC_SIZE];

/// CMD4
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    USHORT usRegIndex = usAddress - 1; 

	// 非法检测
	if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
	{
		return MB_ENOREG;
	}

    log(" CMD4, 寄存器输入.");

	// 填充数据
    REG_INPUT_BUF[0] = 0x01;
	REG_INPUT_BUF[1] = 0x02;

    // 循环读取
	while ( usNRegs > 0 ) {
		*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
		*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
		usRegIndex++;
		usNRegs--;
	}

	return MB_ENOERR;
}

/// CMD6、3、16
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
    USHORT usRegIndex = usAddress - 1;  

	// 非法检测
	if((usRegIndex + usNRegs) > REG_HOLD_SIZE) {
		return MB_ENOREG;
	}

    log(" CMD3,6,16, 保持寄存器读写.");
    
	// 写寄存器
	if (eMode == MB_REG_WRITE) {
		while ( usNRegs > 0 ) {
			uint16_t value;

			value = (pucRegBuffer[0] << 8) | pucRegBuffer[1];

			log("  写寄存器值:%d", value);

			pucRegBuffer += 2;
			usRegIndex++;
			usNRegs--;

        }

    }
    // 读寄存器
	else {

		log("  读寄存器.");
		
		REG_HOLD_BUF[0] = 0x32;
		REG_HOLD_BUF[1] = 0x33;

        while ( usNRegs > 0 ) {
			*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
			*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
			usRegIndex++;
			usNRegs--;
		}
	}

	return MB_ENOERR;
}

/// CMD1、5、15
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{

    USHORT usRegIndex   = usAddress - 1;
	USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
	UCHAR  ucStatus     = 0;
	UCHAR  ucBits       = 0;
	UCHAR  ucDisp       = 0;

	// 非法检测
	if ((usRegIndex + usNCoils) > REG_COILS_SIZE) {
		return MB_ENOREG;
	}

    log("  CMD1,5,15, 线圈读写.");

	// 写线圈
	if (eMode == MB_REG_WRITE) {

		while (usCoilGroups--) {

			ucStatus = *pucRegBuffer++;
			ucBits   = 8;

			while((usNCoils) != 0 && (ucBits) != 0) {
				bool flag = ucStatus & 0x01;

				switch (usRegIndex) {
					
                    case 0:
						log(" 线圈0 : %d", flag);//
                    break;

					case 1:
						log(" 线圈1 : %d", flag);
					break;

					default:

					break;

				}

				usRegIndex++;
				ucStatus >>= 1;
				usNCoils--;
				ucBits--;
			}

		}
	}
    // 读线圈
	else {
		
		REG_COILS_BUF[0]  = 1;
		REG_COILS_BUF[1]  = 0;

		while (usCoilGroups--) {
			ucDisp = 0;
			ucBits = 8;
			ucStatus = 0;

			while((usNCoils) != 0 && (ucBits) != 0) {
				ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));
				usNCoils--;
				ucBits--;
			}

			*pucRegBuffer++ = ucStatus;
		}
	}

	return MB_ENOERR;
}

/// CMD4
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    USHORT usRegIndex   = usAddress - 1;
	USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);
	UCHAR  ucStatus     = 0;
	UCHAR  ucBits       = 0;
	UCHAR  ucDisp       = 0;

	// 非法检测
	if ((usRegIndex + usNDiscrete) > REG_DISC_SIZE) {
		return MB_ENOREG;
	}

    log("  CMD4, 离散寄存器写入.");

	// 读离散输入
	while (usCoilGroups--) {
		ucDisp = 0;
		ucBits = 8;
		ucStatus = 0;

		while((usNDiscrete != 0) && (ucBits != 0))
		{
			switch (usRegIndex) {
			case 0:
				ucStatus = 0x10;
				break;
			}

			usRegIndex++;
			ucDisp++;
			usNDiscrete--;
			ucBits--;
		}
		*pucRegBuffer++ = ucStatus;
	}

    	return MB_ENOERR;
}

在main中创建modbus任务:

static void task_modbus_handle(void *param)
{

    eMBErrorCode    eStatus;

    log("  task modbus start.");

    eStatus = eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );

    /* Enable the Modbus Protocol Stack. */
    eStatus = eMBEnable();

	(void)eStatus;

    for( ;; ) {
        ( void )eMBPoll();
        vTaskDelay(pdMS_TO_TICKS(10));
    }

}
3 烧写测试

将开发板连接到USB转485模块,然后使用modbus poll程序进行测试:

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值