簡介
以下內容為學習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(¢er_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之后就可以啟動協議堆疊了,新手上路,不足的地方希望指出,共同學習,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/45036.html
標籤:C
上一篇:C語言記憶體管理
下一篇:C語言中常用的輸入和輸出函式
