0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32G0开发笔记:FreeRTOS和FreeModbus库使用

CHANBAEK 来源:电子技术攻城狮 作者:MakerInChina.cn 2023-01-16 14:44 次阅读

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

1 新建项目

  • 建立freertos_modbus项目

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

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

2 编写程序

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

2.1 添加 freeModbus 库

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

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

1{
 2    "name": "FreeModbus",
 3    "version": "master",
 4    "repository":{
 5        "type":"git",
 6        "url":"https://github.com/cwalter-at/freemodbus"
 7    },
 8    "build": {
 9        "flags": [
10            "-Iascii",
11            "-Ifunctions",
12            "-Iinclude",
13            "-Irtu",
14            "-Itcp"
15        ],
16        "srcFilter": [
17            "+<*>"
18        ]
19    }
20}

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

image-20220928231915800

2.2 移植
  • portevent:
1/* ----------------------- Modbus includes ----------------------------------*/
 2#include "mb.h"
 3#include "mbport.h"
 4#include "FreeRTOS.h"
 5#include "task.h"
 6
 7/* ----------------------- Variables ----------------------------------------*/
 8static eMBEventType eQueuedEvent;
 9static BOOL     xEventInQueue;
10static uint32_t modbus_last_active_time = 0;
11
12uint32_t get_modbus_last_active_time(void)
13{
14    return modbus_last_active_time;
15}
16
17/* ----------------------- Start implementation -----------------------------*/
18BOOL
19xMBPortEventInit( void )
20{
21    xEventInQueue = FALSE;
22    return TRUE;
23}
24
25BOOL
26xMBPortEventPost( eMBEventType eEvent )
27{
28    xEventInQueue = TRUE;
29    eQueuedEvent = eEvent;
30
31    if (eEvent == EV_EXECUTE) {
32        modbus_last_active_time = xTaskGetTickCount();
33    }
34    return TRUE;
35}
36
37BOOL
38xMBPortEventGet( eMBEventType * eEvent )
39{
40    BOOL            xEventHappened = FALSE;
41
42    if( xEventInQueue )
43    {
44        *eEvent = eQueuedEvent;
45        xEventInQueue = FALSE;
46        xEventHappened = TRUE;
47    }
48    return xEventHappened;
49}
  • portserial

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

1#include "port.h"
  2
  3#include "FreeRTOS.h"
  4#include "queue.h"
  5
  6#include 3/cm3/nvic.h>
  7#include 3/stm32/usart.h>
  8#include 3/stm32/rcc.h>
  9#include 3/stm32/gpio.h>
 10
 11/* ----------------------- Modbus includes ----------------------------------*/
 12#include "mb.h"
 13#include "mbport.h"
 14
 15/* ----------------------- static functions ---------------------------------*/
 16
 17xQueueHandle uart_queue;
 18
 19#define RS485_1_CLOCK        RCC_GPIOB
 20#define RS485_1_EN_PORT        GPIOB
 21#define RS485_1_EN_PIN        GPIO8
 22
 23static void rs485_delay(int n)
 24{
 25    while (--n) {
 26        __asm__ volatile ("nop");
 27    }
 28}
 29
 30static inline void rs485_1_rx_mode(void)
 31{
 32    gpio_clear(RS485_1_EN_PORT, RS485_1_EN_PIN);
 33}
 34
 35static inline void rs485_1_tx_mode(void)
 36{
 37    gpio_set(RS485_1_EN_PORT, RS485_1_EN_PIN);
 38}
 39
 40static inline void rs485_gpio_init(void)
 41{
 42    rcc_periph_clock_enable(RS485_1_CLOCK);
 43    gpio_mode_setup(RS485_1_EN_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_1_EN_PIN);
 44
 45    rs485_1_rx_mode();
 46}
 47
 48/* ----------------------- Start implementation -----------------------------*/
 49void
 50vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
 51{
 52    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
 53     * transmitter empty interrupts.
 54     */
 55    if (xRxEnable) {
 56        rs485_delay(10000);
 57        rs485_1_rx_mode();
 58        rs485_delay(10000);
 59        usart_enable_rx_interrupt(USART1);
 60    }
 61    else {
 62        usart_disable_rx_interrupt(USART1);
 63    }
 64
 65    if (xTxEnable) {
 66        rs485_delay(10000);
 67        rs485_1_tx_mode();
 68        rs485_delay(10000);
 69        usart_enable_tx_interrupt(USART1);
 70    }
 71    else {
 72        usart_disable_tx_interrupt(USART1);
 73
 74    }
 75}
 76
 77BOOL
 78xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
 79{
 80    nvic_enable_irq(NVIC_USART1_IRQ);
 81
 82    rcc_periph_clock_enable(RCC_GPIOB);
 83    gpio_mode_setup(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO7);
 84    gpio_set_af(GPIOB, GPIO_AF0, GPIO6 | GPIO7);
 85
 86    rcc_periph_clock_enable(RCC_USART1);
 87
 88    /* Set up USART/UART parameters using the libopencm3 helper functions */
 89    usart_set_baudrate(USART1, ulBaudRate);
 90    usart_set_databits(USART1, ucDataBits);
 91    usart_set_stopbits(USART1, USART_STOPBITS_1);
 92    usart_set_mode(USART1, USART_MODE_TX_RX);
 93
 94    switch (eParity) {
 95        case MB_PAR_ODD:
 96            usart_set_parity(USART1, USART_PARITY_ODD);
 97            break;
 98        case MB_PAR_EVEN:
 99            usart_set_parity(USART1, USART_PARITY_EVEN);
100            break;
101        default:
102            usart_set_parity(USART1, USART_PARITY_NONE);
103            break;
104    }
105
106    usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
107
108    usart_enable(USART1);
109
110    rs485_gpio_init();
111
112    return TRUE;
113}
114
115BOOL
116xMBPortSerialPutByte( CHAR ucByte )
117{
118
119    usart_send_blocking(USART1, (uint16_t) ucByte);    
120
121    return TRUE;
122}
123
124BOOL
125xMBPortSerialGetByte( CHAR * pucByte )
126{
127    *pucByte = usart_recv(USART1);
128
129    return TRUE;
130}
131
132
133uint32_t uart1_isr, uart1_icr;
134
135void usart1_isr(void)
136{
137
138    /* Check if we were called because of RXNE. */
139    if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
140        ((USART_ISR(USART1) & USART_ISR_RXNE) != 0)) {
141
142        /* Retrieve the data from the peripheral. */
143        // usart_recv(USART1);
144
145        pxMBFrameCBByteReceived();
146
147    }
148
149
150    /* Check if we were called because of TXE. */
151    if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) &&
152        ((USART_ISR(USART1) & USART_ISR_TXE) != 0)) {
153
154        /* Put data into the transmit register. */
155        //usart_send(USART1, data);
156
157        pxMBFrameCBTransmitterEmpty();
158
159    }
160
161}
  • porttimer
1#include "port.h"
 2
 3#include 
 4#include 
 5#include 
 6
 7/* ----------------------- Modbus includes ----------------------------------*/
 8#include "mb.h"
 9#include "mbport.h"
10
11/* ----------------------- static functions ---------------------------------*/
12static void prvvTIMERExpiredISR( void );
13
14/* ----------------------- Start implementation -----------------------------*/
15BOOL
16xMBPortTimersInit( USHORT usTim1Timerout50us )
17{
18    rcc_periph_clock_enable(RCC_TIM2);
19    nvic_enable_irq(NVIC_TIM2_IRQ);
20    rcc_periph_reset_pulse(RST_TIM2);
21
22    timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
23
24    timer_set_prescaler(TIM2, (rcc_apb1_frequency/ 20000));
25
26    timer_disable_preload(TIM2);
27    timer_continuous_mode(TIM2);
28
29    timer_set_period(TIM2, usTim1Timerout50us);
30    timer_enable_counter(TIM2);
31
32    timer_enable_irq(TIM2, TIM_DIER_UIE);
33
34    return TRUE;
35}
36
37
38inline void
39vMBPortTimersEnable(  )
40{
41    timer_set_counter(TIM2, 0);
42    timer_enable_counter(TIM2);
43}
44
45inline void
46vMBPortTimersDisable(  )
47{
48    timer_disable_counter(TIM2);
49}
50
51/* Create an ISR which is called whenever the timer has expired. This function
52 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
53 * the timer has expired.
54 */
55static void prvvTIMERExpiredISR( void )
56{
57    ( void )pxMBPortCBTimerExpired(  );
58}
59
60void
61vMBPortTimersDelay( USHORT usTimeOutMS )
62{
63    vTaskDelay(pdMS_TO_TICKS(usTimeOutMS));
64}
65
66void tim2_isr(void)
67{
68    if (timer_get_flag(TIM2, TIM_SR_UIF)) {
69
70        /* Clear compare interrupt flag. */
71        timer_clear_flag(TIM2, TIM_SR_UIF);
72
73        prvvTIMERExpiredISR();
74
75    }
76}

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

2.3 使用

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

1/// CMD4
 2eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );
 3
 4/// CMD6、3、16
 5eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );
 6
 7/// CMD1、5、15
 8eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );
 9
10/// CMD4
11eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete );

基本的实现示例如下:

1#include "modbus_cb.h"
  2#include "stdbool.h"
  3
  4extern log(const char* fmt, ...);
  5
  6// 输入寄存器
  7#define REG_INPUT_SIZE  32
  8uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
  9
 10// 保持寄存器
 11#define REG_HOLD_SIZE   32
 12uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
 13
 14// 线圈寄存器
 15#define REG_COILS_SIZE 16
 16uint8_t REG_COILS_BUF[REG_COILS_SIZE];
 17
 18// 离散量
 19#define REG_DISC_SIZE  8
 20uint8_t REG_DISC_BUF[REG_DISC_SIZE];
 21
 22/// CMD4
 23eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
 24{
 25    USHORT usRegIndex = usAddress - 1; 
 26
 27    // 非法检测
 28    if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
 29    {
 30        return MB_ENOREG;
 31    }
 32
 33    log(" CMD4, 寄存器输入.");
 34
 35    // 填充数据
 36    REG_INPUT_BUF[0] = 0x01;
 37    REG_INPUT_BUF[1] = 0x02;
 38
 39    // 循环读取
 40    while ( usNRegs > 0 ) {
 41        *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
 42        *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
 43        usRegIndex++;
 44        usNRegs--;
 45    }
 46
 47    return MB_ENOERR;
 48}
 49
 50/// CMD6、3、16
 51eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
 52{
 53    USHORT usRegIndex = usAddress - 1;  
 54
 55    // 非法检测
 56    if((usRegIndex + usNRegs) > REG_HOLD_SIZE) {
 57        return MB_ENOREG;
 58    }
 59
 60    log(" CMD3,6,16, 保持寄存器读写.");
 61
 62    // 写寄存器
 63    if (eMode == MB_REG_WRITE) {
 64        while ( usNRegs > 0 ) {
 65            uint16_t value;
 66
 67            value = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
 68
 69            log("  写寄存器值:%d", value);
 70
 71            pucRegBuffer += 2;
 72            usRegIndex++;
 73            usNRegs--;
 74
 75        }
 76
 77    }
 78    // 读寄存器
 79    else {
 80
 81        log("  读寄存器.");
 82
 83        REG_HOLD_BUF[0] = 0x32;
 84        REG_HOLD_BUF[1] = 0x33;
 85
 86        while ( usNRegs > 0 ) {
 87            *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
 88            *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
 89            usRegIndex++;
 90            usNRegs--;
 91        }
 92    }
 93
 94    return MB_ENOERR;
 95}
 96
 97/// CMD1、5、15
 98eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
 99{
100
101    USHORT usRegIndex   = usAddress - 1;
102    USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
103    UCHAR  ucStatus     = 0;
104    UCHAR  ucBits       = 0;
105    UCHAR  ucDisp       = 0;
106
107    // 非法检测
108    if ((usRegIndex + usNCoils) > REG_COILS_SIZE) {
109        return MB_ENOREG;
110    }
111
112    log("  CMD1,5,15, 线圈读写.");
113
114    // 写线圈
115    if (eMode == MB_REG_WRITE) {
116
117        while (usCoilGroups--) {
118
119            ucStatus = *pucRegBuffer++;
120            ucBits   = 8;
121
122            while((usNCoils) != 0 && (ucBits) != 0) {
123                bool flag = ucStatus & 0x01;
124
125                switch (usRegIndex) {
126
127                    case 0:
128                        log(" 线圈0 : %d", flag);//
129                    break;
130
131                    case 1:
132                        log(" 线圈1 : %d", flag);
133                    break;
134
135                    default:
136
137                    break;
138
139                }
140
141                usRegIndex++;
142                ucStatus >>= 1;
143                usNCoils--;
144                ucBits--;
145            }
146
147        }
148    }
149    // 读线圈
150    else {
151
152        REG_COILS_BUF[0]  = 1;
153        REG_COILS_BUF[1]  = 0;
154
155        while (usCoilGroups--) {
156            ucDisp = 0;
157            ucBits = 8;
158            ucStatus = 0;
159
160            while((usNCoils) != 0 && (ucBits) != 0) {
161                ucStatus |= (REG_COILS_BUF[usRegIndex++] << (ucDisp++));
162                usNCoils--;
163                ucBits--;
164            }
165
166            *pucRegBuffer++ = ucStatus;
167        }
168    }
169
170    return MB_ENOERR;
171}
172
173/// CMD4
174eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
175{
176    USHORT usRegIndex   = usAddress - 1;
177    USHORT usCoilGroups = ((usNDiscrete - 1) / 8 + 1);
178    UCHAR  ucStatus     = 0;
179    UCHAR  ucBits       = 0;
180    UCHAR  ucDisp       = 0;
181
182    // 非法检测
183    if ((usRegIndex + usNDiscrete) > REG_DISC_SIZE) {
184        return MB_ENOREG;
185    }
186
187    log("  CMD4, 离散寄存器写入.");
188
189    // 读离散输入
190    while (usCoilGroups--) {
191        ucDisp = 0;
192        ucBits = 8;
193        ucStatus = 0;
194
195        while((usNDiscrete != 0) && (ucBits != 0))
196        {
197            switch (usRegIndex) {
198            case 0:
199                ucStatus = 0x10;
200                break;
201            }
202
203            usRegIndex++;
204            ucDisp++;
205            usNDiscrete--;
206            ucBits--;
207        }
208        *pucRegBuffer++ = ucStatus;
209    }
210
211        return MB_ENOERR;
212}

在main中创建modbus任务:

1static void task_modbus_handle(void *param)
 2{
 3
 4    eMBErrorCode    eStatus;
 5
 6    log("  task modbus start.");
 7
 8    eStatus = eMBInit( MB_RTU, 0x01, 0, 9600, MB_PAR_NONE );
 9
10    /* Enable the Modbus Protocol Stack. */
11    eStatus = eMBEnable();
12
13    (void)eStatus;
14
15    for( ;; ) {
16        ( void )eMBPoll();
17        vTaskDelay(pdMS_TO_TICKS(10));
18    }
19
20}

3 烧写测试

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

image-20220928232358657

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • MODBUS
    +关注

    关注

    27

    文章

    1437

    浏览量

    75691
  • STM32
    +关注

    关注

    2239

    文章

    10671

    浏览量

    348735
  • 开发板
    +关注

    关注

    25

    文章

    4429

    浏览量

    93978
  • FreeRTOS
    +关注

    关注

    12

    文章

    473

    浏览量

    61347
  • FreeModbus
    +关注

    关注

    0

    文章

    16

    浏览量

    4359
收藏 人收藏

    评论

    相关推荐

    freeRTOS开发笔记】记一次坑爹的freeTOS升级

    freeRTOS开发笔记】记一次坑爹的freeTOS-v9.0.0升级到freeRTOS-v10.4.4
    的头像 发表于 07-11 09:15 3801次阅读
    【<b class='flag-5'>freeRTOS</b><b class='flag-5'>开发笔记</b>】记一次坑爹的freeTOS升级

    STM32F0的SPL能在STM32G0上用吗?

    原来用的STM32F0系列,使用的V3.5.0的标准外设。现在改用STM32G0系列,发现官网提供的固件STM32CubeG0,里面是
    发表于 04-08 08:29

    基于STM32的USB程序开发笔记 汇总

    忙了一下午终于有时间整理了,基于STM32的USB程序开发笔记汇总,需要的亲们点击链接阅读哈!{:4_95:}基于STM32的USB程序开发笔记(一)https://bbs.elecf
    发表于 03-20 16:08

    坛友经验分享之STM32的USB程序开发笔记

    基于STM32的USB程序开发笔记(一)基于STM32的USB程序开发笔记(二)基于STM32的USB程序
    发表于 09-04 17:42

    求大佬分享STM32CubeMX-HAL开发笔记

    求大佬分享STM32CubeMX-HAL开发笔记
    发表于 12-02 07:26

    STM32G0驱动无刷电机有吗?

    STM32G0驱动无刷电机,有
    发表于 09-22 06:04

    STM32的USB程序开发笔记

    STM32的USB程序开发笔记
    发表于 09-29 14:55 27次下载
    <b class='flag-5'>STM32</b>的USB程序<b class='flag-5'>开发笔记</b>

    STM32开发笔记56: STM32F407VGT6的解释

    STM32开发笔记56: STM32F407VGT6的解释
    发表于 12-02 16:51 47次下载
    <b class='flag-5'>STM32</b><b class='flag-5'>开发笔记</b>56: <b class='flag-5'>STM32</b>F407VGT6的解释

    STM32G0开发笔记FreeRTOS和CLI组件使用

    使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为FreeRTOS和CLI组件使用。
    的头像 发表于 01-16 14:47 2349次阅读
    <b class='flag-5'>STM32G0</b><b class='flag-5'>开发笔记</b>:<b class='flag-5'>FreeRTOS</b>和CLI组件使用

    STM32G0开发笔记:使用FreeRTOS系统的队列Queue

    使用Platformio平台的libopencm3开发框架来开发STM32G0,下面为使用FreeRTOS系统的队列Queue。
    的头像 发表于 01-16 14:50 961次阅读

    STM32G0开发笔记:使用FreeRTOS系统

    使用Platformio平台的libopencm3开发框架来开发STM32G0,下面为使用FreeRTOS系统。
    的头像 发表于 01-16 15:07 1569次阅读
    <b class='flag-5'>STM32G0</b><b class='flag-5'>开发笔记</b>:使用<b class='flag-5'>FreeRTOS</b>系统

    STM32G0开发笔记:SPI接口的基本使用

    使用Platformio平台的libopencm3开发框架来开发STM32G0,下面介绍SPI接口的基本使用。
    的头像 发表于 01-17 10:38 2504次阅读
    <b class='flag-5'>STM32G0</b><b class='flag-5'>开发笔记</b>:SPI接口的基本使用

    STM32G0开发笔记:多通道ADC与DMA的使用

    使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为多通道ADC与DMA的使用。
    的头像 发表于 01-17 10:41 5267次阅读
    <b class='flag-5'>STM32G0</b><b class='flag-5'>开发笔记</b>:多通道ADC与DMA的使用

    STM32G0开发笔记:LED灯示例

    使用Platformio平台的libopencm3开发框架来开发STM32G0,下面为LED灯示例。
    的头像 发表于 01-17 10:52 979次阅读
    <b class='flag-5'>STM32G0</b><b class='flag-5'>开发笔记</b>:LED灯示例

    STM32G0开发笔记:使用libopencm3库

    使用Platformio平台的libopencm3开发框架来开发STM32G0开发环境为VSCode+PIO插件,这里以开发
    的头像 发表于 01-17 10:56 1324次阅读
    <b class='flag-5'>STM32G0</b><b class='flag-5'>开发笔记</b>:使用libopencm3库