欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

STM32F107 移植 canfestival

程序员文章站 2022-06-24 23:37:32
简介 以下内容为学习Canopen时对canfestival协议栈的移植记录,参考链接有 "strongerhuang" 。 平台介绍 MCU : STM32F107VCT6 RTOS : RT Thread RTOS ST_Lib : STM32F1xx HAL Driver version nu ......

简介

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

平台介绍

  • mcu : stm32f107vct6
  • rtos : rt-thread rtos
  • st_lib : stm32f1xx hal driver version number v1.1.4
  • 编译器 : mdk 5.28

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之后就可以启动协议栈了。新手上路,不足的地方希望指出,共同学习。