• STM32F107 移植 canfestival


    简介

    以下内容为学习Canopen时对canfestival协议栈的移植记录,参考链接有strongerhuang

    平台介绍

    • MCU : STM32F107VCT6
    • RTOS : RT-Thread RTOS
    • ST_Lib : STM32F1xx HAL Driver version number V1.1.4
    • 编译器 : MDK 5.28

    canfestival

    • 源码 : 进入canfestival官方网站下载。点击 Code 选项,从页面上继续点击 code.html.en 链接可以进入源码选择,这里选择了带有professional support 标识的类型源码(Repositories 列表第二项);点击 Documentation 选项,从页面上继续点击 doc.html.en 链接可以进入文档说明,里面有PDF手册和对象字典编辑工具(Objdictedit)介绍。
    • 对象字典编辑 : 下载的源码压缩包内包含一个对象字典包含工具(Objdictedit),这个工具的运行需要Python环境支持。为了使用该工具直接生成对象字典,安装Python-2.7.15和wxPython2.8;环境搭建完成后进入源码压缩包解压后的文件夹,将objdictgen/Gnosis_Utils-current.tar.gz压缩包解压,并在解压得到的文件夹内部提取名字为gnosis的文件夹,把该文件夹复制到objdictgen/目录下,这样打开objdictgen/objdictedit.py就能正常运行Objdictedit工具了。

    源码文件提取

    • src文件夹 : 只需要该目录下的.c文件,但是symbols.c没用到,而且timer.c最好更改文件名,防止在工程内名称冲突。
    • include文件夹 :该目录下的所有.h文件,还有cm4文件夹的.h文件,注意文件名。
    • examples文件夹 : 需要该文件夹下AVR/Slave/目录的config.h文件。

    drivers文件夹 :该文件夹下提供了一些移植参考,例如cm3、cm4文件夹,提供的移植示例基于标准库。

    移植过程

    • 准备好一个正常的工程,复制上面介绍的文件,并且在工程中添加这些.c文件并包含.h文件路径。
    • 打开复制的dcf.c文件,删除该文件下inline void start_node(CO_Data* d, UNS8 nodeId)inline void start_and_seek_node(CO_Data* d, UNS8 nodeId)的inline声明,分别位于该文件59行和98行。
    • 打开复制的canfentival.h文件,给文件添加#ifndef #define #endif三条。
    • 打开复制的config.h,删除或者屏蔽以下内容
    #ifdef  __IAR_SYSTEMS_ICC__
    #include <ioavr.h>
    #include <intrinsics.h>
    #include "iar.h"
    #else	// GCC
    #include <inttypes.h>
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/pgmspace.h>
    #include <avr/sleep.h>
    #include <avr/wdt.h>
    #endif	// GCC
    
    //#define WD_SLEEP
    // Needed defines by Atmel lib
    #define FOSC           8000        // 16 MHz External cristal
    #ifndef F_CPU
    #define F_CPU          (1000UL*FOSC) // Need for AVR GCC
    #endif
    #define CAN_BAUDRATE    250
    

    修改 #define REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERTS_TIMES(repeat)#define REPEAT_SDO_MAX_SIMULTANEOUS_TRANSFERS_TIMES(repeat),删去了TRANSFERTS中的T字符。

    实现API功能

    canfestival移植后有些函数需要自己实现,比如在 timer.h 中声明的两个函数,与协议栈内部定时器相关。

    /**
     * @ingroup timer
     * @brief Set a timerfor a given time.
     * @param value The time value.
     */
    void setTimer(TIMEVAL value);
    
    /**
     * @ingroup timer
     * @brief Get the time elapsed since latest timer occurence.
     * @return time elapsed since latest timer occurence
     */
    TIMEVAL getElapsedTime(void);
    

    canfestival.h中声明的函数,这些接口可以实现功能后直接调用,使用者也可以使用自定义函数,实现这些功能即可。

    void initTimer(void);
    void clearTimer(void);
    
    unsigned char canSend(CAN_PORT notused, Message *m);
    unsigned char canInit(CO_Data * d, uint32_t bitrate);
    void canClose(void);
    
    void disable_it(void);
    void enable_it(void);
    

    上面提到的 drivers 文件夹可以作为这些接口实现的参考。

    个人移植程序

    • setTimer 和 getElapsedTime:
    static TIMEVAL last_counter_val = 0;
    static TIMEVAL elapsed_time = 0;
    void setTimer(TIMEVAL value)
    {
        uint32_t timer = __HAL_TIM_GET_COUNTER(&can_tim);
        elapsed_time += timer - last_counter_val;
        last_counter_val = CANOPEN_TIM_PERIOD - value;
        __HAL_TIM_SET_COUNTER(&can_tim, CANOPEN_TIM_PERIOD - value);
        HAL_TIM_Base_Start_IT(&can_tim);
    }
    TIMEVAL getElapsedTime(void)
    {
        uint32_t timer = __HAL_TIM_GET_COUNTER(&can_tim);
    
        if(timer < last_counter_val)
        {
            timer += CANOPEN_TIM_PERIOD;
        }
    
        TIMEVAL elapsed = timer - last_counter_val + elapsed_time;
        return elapsed;
    }
    
    • initTimer:
    void initTimer(void)
    {
        can_tim.Instance               = CANOPEN_TIMER;
        can_tim.Init.Prescaler         = CANOPEN_PRESCALER;
        can_tim.Init.CounterMode       = TIM_COUNTERMODE_UP;
        can_tim.Init.Period            = CANOPEN_TIM_PERIOD;
        can_tim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
        HAL_TIM_Base_Init(&can_tim);
        __HAL_TIM_SET_COUNTER(&can_tim, 0);
        HAL_TIM_Base_Start_IT(&can_tim);
    }
    
    • canInit:
    unsigned char canInit(CAN_HandleTypeDef *handle)
    {
        CAN_FilterTypeDef hcan_filter;
    
        //CAN2控制器配置
        handle->Instance = CANOPEN_HANDLE;
        handle->Init.Prescaler = presc;
        handle->Init.Mode = CAN_MODE_NORMAL;
        handle->Init.SyncJumpWidth = sjw;
        handle->Init.TimeSeg1 = ts1;
        handle->Init.TimeSeg2 = ts2;
        handle->Init.TimeTriggeredMode = DISABLE;
        handle->Init.AutoBusOff = DISABLE;
        handle->Init.AutoWakeUp = DISABLE;
        handle->Init.AutoRetransmission = DISABLE;
        handle->Init.ReceiveFifoLocked = DISABLE;
        handle->Init.TransmitFifoPriority = DISABLE;
        HAL_CAN_Init(handle);
    
        //CAN滤波器配置
        hcan_filter.FilterBank = 14;
        hcan_filter.FilterMode = CAN_FILTERMODE_IDMASK;
        hcan_filter.FilterActivation = CAN_FILTER_ENABLE;
        hcan_filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
        hcan_filter.FilterScale = CAN_FILTERSCALE_32BIT;
        hcan_filter.FilterIdHigh = 0x0000;
        hcan_filter.FilterIdLow = 0x0000;
        hcan_filter.FilterMaskIdHigh = 0x0000;
        hcan_filter.FilterMaskIdLow = 0x0000;
        hcan_filter.SlaveStartFilterBank = 14;
        HAL_CAN_ConfigFilter(handle, &hcan_filter);
    
        //启动CAN2,使能中断
        HAL_CAN_Start(handle);
        HAL_CAN_ActivateNotification(handle, CAN_IT_RX_FIFO0_MSG_PENDING);
    
        return 0;
    }
    
    • canSend:
    unsigned char canSend(CAN_PORT notused, Message *m)
    {
        struct Can_Tx pre_send;
    
        pre_send.TxInfo.StdId = m->cob_id;
    
        if(m->rtr)
            pre_send.TxInfo.RTR = CAN_RTR_REMOTE;
        else
            pre_send.TxInfo.RTR = CAN_RTR_DATA;
    
        pre_send.TxInfo.IDE = CAN_ID_STD;
        pre_send.TxInfo.DLC = m->len;
    
        for(int i = 0; i < m->len; i++)
        {
            pre_send.Tx_data[i] = m->data[i];
        }
    
        if(rt_mq_send(can_txmq, &pre_send, sizeof(pre_send)) != RT_EOK)
            return 0xff;
    
        return 0;
    }
    
    • 回调函数:
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
        if(htim == (&can_tim))
        {
            last_counter_val = 0;
            elapsed_time = 0;
            TimeDispatch();
        }
    }
    void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
    {
        struct Can_Rx rs_msg;
        HAL_CAN_GetRxMessage(&canopen, CAN_RX_FIFO0, &rs_msg.RxInfo, rs_msg.Rx_data);
        rt_mq_send(can_rxmq, &rs_msg, sizeof(rs_msg));
        HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
    }
    

    收发功能作为单独的任务执行,采用队列方式

    • 接收任务:从接收队列中获取数据,提取相应的参数放入Message类型变量,送入协议栈
    if(rt_mq_recv(can_rxmq, &mq_recv, sizeof(mq_recv), RT_WAITING_FOREVER) == RT_EOK)
    {            
        rxmsg.cob_id = mq_recv.RxInfo.StdId;
        if(mq_recv.RxInfo.RTR == CAN_RTR_REMOTE)
            rxmsg.rtr = 1;
        else
            rxmsg.rtr = 0;
        rxmsg.len = (UNS8)mq_recv.RxInfo.DLC;
        for(int i = 0; i < mq_recv.RxInfo.DLC; i++)
        {
            rxmsg.data[i] = mq_recv.Rx_data[i];
        }
        HAL_TIM_Base_Stop_IT(&can_tim);
        canDispatch(&center_Data, &rxmsg);
        HAL_TIM_Base_Start_IT(&can_tim);
    }
    
    • 发送任务:从发送队列接收数据,调用发送接口
    if(rt_mq_recv(can_txmq, &msg_send, sizeof(msg_send), RT_WAITING_FOREVER) == RT_EOK)
    {
        HAL_CAN_AddTxMessage(&canopen, &msg_send.TxInfo, msg_send.Tx_data, &TxMailbox);
    }
    

    总结

    以上为移植内容,移植完成后建立任务初始化CAN之后就可以启动协议栈了。新手上路,不足的地方希望指出,共同学习。

  • 相关阅读:
    【洛谷P4708】—画画(Burnside引理)
    【HDU 6087】—Rikka with Sequence(可持久化平衡树)
    【HDU 6087】—Rikka with Sequence(可持久化平衡树)
    【Codeforces 643G】—Choosing Ads(线段树)
    【Codeforces 643G】—Choosing Ads(线段树)
    less sass学习总结(——待续哦——)
    网站常识
    网站发布流程
    解惑 和 遇到的问题
    页面无法访问 css文件加载问题
  • 原文地址:https://www.cnblogs.com/niu-li/p/12041048.html
Copyright © 2020-2023  润新知