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之后就可以啟動協議棧了。新手上路,不足的地方希望指出,共同學習。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM