日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

基于STM32F4的CANOpen移植教程(超级详细)

發(fā)布時(shí)間:2024/1/8 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于STM32F4的CANOpen移植教程(超级详细) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

CANopen移植到STM32F4平臺(tái)

  • 前言
  • 1 物品準(zhǔn)備
  • 2 相關(guān)軟件安裝
    • 2.1 CAN上位機(jī)
    • 2.2 對(duì)象字典生成工具objdictedit環(huán)境配置
  • 3 將CANopen移植到STM32F407
    • 3.1 基礎(chǔ)代碼移植
      • 3.11 h文件移植
      • 3.12 c文件移植
    • 3.2 建立自己的底層驅(qū)動(dòng)文件
    • 3.3 建立詞典
    • 3.4工程配置
      • 3.41 c文件添加
      • 3.42 頭文件路徑添加
      • 3.43 c99標(biāo)準(zhǔn)選擇
      • 3.44 調(diào)試串口設(shè)置
      • 3.45 程序啟動(dòng)
  • 4 末尾

本專題相關(guān)教程:
基于STM32F4的CANOpen移植教程

基于STM32F4的CANopen快速SDO通信

linux下CANopen for python的使用

基于Linux C的CANopen移植

CANopen補(bǔ)充–時(shí)間計(jì)算出錯(cuò)

CANopen補(bǔ)充–主站檢測(cè)節(jié)點(diǎn)是否在線

前言

為了在STM32F4上能夠運(yùn)行CANopen(CanFestival),跟著網(wǎng)上的教程操作,發(fā)現(xiàn)總是不夠詳細(xì)。主要是配置和代碼運(yùn)行部分基本沒(méi)有解釋。為了后來(lái)者能夠少走彎路,便有了這篇教程。關(guān)于CANopen協(xié)議本身本文不做過(guò)多介紹,主要是介紹如何使用軟件和代碼修改。

本文配套資料下載地址:https://pan.baidu.com/s/1FMp7xuJ1r3gJTPB0wf3Yrw?pwd=osxs
提取碼:osxs

廢話不多說(shuō),GOGOGO。

1 物品準(zhǔn)備

名稱用途
USB-CAN模塊/USB-CAN盒子用以監(jiān)聽(tīng)數(shù)據(jù)(如實(shí)在沒(méi)有的話,代碼里添加串口反饋也勉強(qiáng)能測(cè)試)
Canfestival- 3 源代碼CANopen源代碼 /本文資料里有
STM32F4 裸工程移植目標(biāo)平臺(tái)代碼,這里用正點(diǎn)原子的空白工程即可 /本文資料里有
CANopen輕松入門.pdf-周立功pdf書(shū)籍,用以學(xué)習(xí)CANopen協(xié)議 /本文資料里有

USB-CAN模塊,比如下面這個(gè),買啥都行,有這個(gè)功能就ok。

USB-CAN盒子,如下,相比模塊,多了一些功能(我用的就這個(gè),不過(guò)好像多的功能我并沒(méi)有用上)

CANopen輕松入門.pdf-周立功鏈接 下載地址

2 相關(guān)軟件安裝

2.1 CAN上位機(jī)

如果使用USB-CAN盒子,找店家要上位機(jī)資料即可。比如我用的這款資料如下:

驅(qū)動(dòng):驅(qū)動(dòng)下載
驅(qū)動(dòng)安裝教程:驅(qū)動(dòng)安裝視頻

上位機(jī)軟件:上位機(jī)下載地址


打開(kāi)設(shè)備-選擇設(shè)備-選擇對(duì)應(yīng)波特率即可。

如果是普通的USB-CAN模塊,找店家應(yīng)該也有資料。使用CANpro協(xié)議平臺(tái)分析軟件即可,這個(gè)網(wǎng)上搜很容易搜得到。附一個(gè)我隨便找的鏈接:CANPro協(xié)議分析平臺(tái)官方下載

同理,啟動(dòng)-選擇設(shè)備(不對(duì)就反復(fù)選)-選擇波特率。

2.2 對(duì)象字典生成工具objdictedit環(huán)境配置

? CANopen需要使用到字典,路徑:源代碼/objdictgen/objdictedit.py。這是個(gè)基于python2.7才能運(yùn)行的程序,因此我們先裝環(huán)境。

安裝環(huán)境,遇到了很多坑。主要是網(wǎng)上教程很多偏老,跟著操作,各種bug。最終成功的一個(gè)搭配是

軟件名字備注
python-2.7.15.amd64.msi
wxPython3.0-win64-3.0.2.0-py27.exe使用2.8會(huì)導(dǎo)致在安裝下邊軟件的時(shí)候,提示包缺失
Gnosis_Utils-1.2.2.zip

安裝教程參考:CanFestival中對(duì)象字典編輯器objdictedit的正確打開(kāi)環(huán)境_lei_g的博客-CSDN博客_canfestival中對(duì)象字典編輯器的打開(kāi)

備注:python2.7和自己之前安裝的如python3.7是不沖突的。

要使用objdictedit,可以使用這個(gè)方式固定到任務(wù)欄。方便以后打開(kāi)。

選擇默認(rèn)程序–>更多應(yīng)用–>在這臺(tái)電腦上查找其他應(yīng)用–>選擇python2.7文件夾里的python.exe


當(dāng)打開(kāi)下邊程序的時(shí)候,在桌面任務(wù)欄選擇:固定到任務(wù)欄。那么以后都可以右鍵這個(gè)圖標(biāo),點(diǎn)擊上邊的objdictedit.py即可打開(kāi)軟件。

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-8awyPdNN-1646308038808)(C:\Users\FEN\AppData\Roaming\Typora\typora-user-images\image-20220303104232531.png)]

3 將CANopen移植到STM32F407

首先,這是我給大家準(zhǔn)備的禮物,截圖如下:

名稱說(shuō)明
CanFestival-3源碼內(nèi)含1個(gè)官方源碼與3個(gè)分支,我們使用Mongo-canfestival,因?yàn)樗袑?duì)于cm4內(nèi)核的支持
CANopen裸工程本教程所使用的空白代碼
CANopen最終移植代碼本教程所使用的移植好的最終代碼
USBCAN調(diào)試軟件USB-CAN盒子的上位機(jī)
字典工具安裝字典工具安裝所需文件

3.1 基礎(chǔ)代碼移植

打開(kāi)我們的空白工程,界面如圖,空空如也。需要說(shuō)明的是,個(gè)人喜歡把所有頭文件放入main.h,這樣其他外設(shè)文件只用包含main.h即可。

文件夾如下:

我們新建一個(gè)文件夾,名為CANopen,用于存放所有與CANopen有關(guān)的代碼。

里面再新建幾個(gè)子文件夾。

說(shuō)明如下:

文件夾名說(shuō)明
dictionary存放字典和其對(duì)應(yīng)的.c /.h 文件
hardware外設(shè)的驅(qū)動(dòng)文件,如定時(shí)器,CAN,還有配置文件
inc由CANopen源代碼移植過(guò)來(lái)的h文件
src由CANopen源代碼移植過(guò)來(lái)的c文件

3.11 h文件移植

進(jìn)入源代碼/include目錄,先將該目錄下19個(gè)h文件,都復(fù)制到新工程/CANopen/inc 里,再?gòu)?fù)制cm4文件夾(內(nèi)含3個(gè)h文件)更名為stm32。如下:

修改一下stm32/canfestival.h文件,添加3行語(yǔ)句,防止遞歸調(diào)用。

進(jìn)入源代碼\examples\AVR\Slave目錄,把config文件,移植到新工程/CANopen/hardware

并對(duì)config做一點(diǎn)修改。

3.12 c文件移植

進(jìn)入源代碼/src目錄,將該目錄下除了symbols.c之外的12個(gè)c文件,復(fù)制到新工程/CANopen/src 里。

刪除dcf.c文件下第59、98行前面的“inline”關(guān)鍵字

3.2 建立自己的底層驅(qū)動(dòng)文件

? 在裸工程/CANopen/hardware下新建定時(shí)器、CAN的c/h文件。其中定時(shí)器用于時(shí)間獲取,CAN是通信基礎(chǔ)。

需要說(shuō)明的是,CANopen源代碼里含有timer.c 文件,為了命名不沖突,我這里起名加了后綴。比如使用定時(shí)器3,就建立timer3.c。

如圖,我們使用了can1,timer2, config.h為之前移植的文件,不用管。

文件說(shuō)明
can1中斷優(yōu)先級(jí)為1(無(wú)所謂);波特率設(shè)置為1M(1M或者500K都行,要與config.h一致)
timer2中斷優(yōu)先級(jí)1(無(wú)所謂);時(shí)鐘84M,分頻840,即基礎(chǔ)頻率為100K(要求與timerscfg.h里的配置即可),重裝載值為65535,即0.65s一次溢出中斷

can與timer的代碼移植自源代碼/drivers/cm4。cm4是基于stm32F3的,因此有些代碼需要修改

cm4.c里面包含can1與timer3的初始化代碼以及一些封裝好的代碼。我們將其各自復(fù)制到can1.c和timer3.c。并根據(jù)板子情況做修改。

大家可以到移植成功的工程里看看有啥修改。

can1.c

#include "can1.h"static CO_Data *co_data = NULL;//Initialize the CAN hardware unsigned char CAN1_Init(CO_Data * d, uint32_t bitrate) {GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;CAN_InitTypeDef CAN_InitStructure;CAN_FilterInitTypeDef CAN_FilterInitStructure;/* save the canfestival handle */ co_data = d;/* CAN GPIOs configuration **************************************************//* Enable GPIO clock */RCC_AHB1PeriphClockCmd(CAN_GPIO_CLK, ENABLE);/* Connect CAN pins to AF7 */GPIO_PinAFConfig(CAN_GPIO_PORT, CAN_RX_SOURCE, GPIO_AF_CANx);GPIO_PinAFConfig(CAN_GPIO_PORT, CAN_TX_SOURCE, GPIO_AF_CANx); /* Configure CAN RX and TX pins */GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN | CAN_TX_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_Init(CAN_GPIO_PORT, &GPIO_InitStructure);/* NVIC configuration *******************************************************/NVIC_InitStructure.NVIC_IRQChannel = CANx_RX0_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);/* CAN configuration ********************************************************/ /* Enable CAN clock */RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);/* CAN register init */CAN_DeInit(CANx);CAN_StructInit(&CAN_InitStructure);/* CAN cell init */CAN_InitStructure.CAN_TTCM = DISABLE;CAN_InitStructure.CAN_ABOM = DISABLE;CAN_InitStructure.CAN_AWUM = DISABLE;CAN_InitStructure.CAN_NART = DISABLE;CAN_InitStructure.CAN_RFLM = DISABLE;CAN_InitStructure.CAN_TXFP = DISABLE;CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;/* CAN Baudrate (CAN clocked at 42 MHz) 42e6 / ( 6 * (1+BS1+BS2)) */CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;if(bitrate == 1000000){CAN_InitStructure.CAN_BS1 = CAN_BS1_3tq;CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;}else { //除去1M頻率。剩下都配置為500KCAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq;}CAN_InitStructure.CAN_Prescaler = 6;CAN_Init(CANx, &CAN_InitStructure);/* CAN filter init */CAN_FilterInitStructure.CAN_FilterNumber = 0;CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;CAN_FilterInit(&CAN_FilterInitStructure);/* Enable FIFO 0 message pending Interrupt */CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);return 1; }// The driver send a CAN message passed from the CANopen stack unsigned char canSend(CAN_PORT notused, Message *m) {int i, res;CanTxMsg TxMessage = {0};TxMessage.StdId = m->cob_id;TxMessage.IDE = CAN_ID_STD;if(m->rtr)TxMessage.RTR = CAN_RTR_REMOTE;elseTxMessage.RTR = CAN_RTR_DATA;TxMessage.DLC = m->len;for(i=0 ; i<m->len ; i++)TxMessage.Data[i] = m->data[i]; res = CAN_Transmit(CANx, &TxMessage);if(res == CAN_TxStatus_NoMailBox)return 0; // errorreturn 1; // succesful }//The driver pass a received CAN message to the stack /* unsigned char canReceive(Message *m) { } */ unsigned char canChangeBaudRate_driver( CAN_HANDLE fd, char* baud) {return 0; }/*** @brief This function handles CAN1 RX0 interrupt request.* @param None* @retval None*/void CAN1_RX0_IRQHandler(void){int i;CanRxMsg RxMessage = {0};Message rxm = {0};CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);// Drop extended framesif(RxMessage.IDE == CAN_ID_EXT) //不處理擴(kuò)展幀return;rxm.cob_id = RxMessage.StdId;if(RxMessage.RTR == CAN_RTR_REMOTE)//遠(yuǎn)程幀rxm.rtr = 1;rxm.len = RxMessage.DLC;for(i=0 ; i<rxm.len ; i++)rxm.data[i] = RxMessage.Data[i];canDispatch(co_data, &rxm);//CANopen自身的處理函數(shù),因?yàn)榭焖賁DO不需要反饋,所以在上邊處理后就不需要調(diào)用這步了}

can1.h

#ifndef __CAN1_H #define __CAN1_H#include "sys.h" #include "main.h" #include "data.h" // CAN bus defines for cortex-M4 STM32F407#define CANx CAN1 #define CAN_CLK RCC_APB1Periph_CAN1 #define CAN_RX_PIN GPIO_Pin_11 #define CAN_TX_PIN GPIO_Pin_12 #define CAN_GPIO_PORT GPIOA #define CAN_GPIO_CLK RCC_AHB1Periph_GPIOA #define CANx_RX0_IRQn CAN1_RX0_IRQn#define GPIO_AF_CANx GPIO_AF_CAN1 #define CAN_RX_SOURCE GPIO_PinSource11 #define CAN_TX_SOURCE GPIO_PinSource12unsigned char CAN1_Init(CO_Data * d, uint32_t bitrate);#endif

timer3.c

#include "timer3.h"TIMEVAL last_counter_val = 0; TIMEVAL elapsed_time = 0;// Initializes the timer, turn on the interrupt and put the interrupt time to zero void TIM3_Init(void) {NVIC_InitTypeDef NVIC_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;/* TIM3 clock enable */RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);/* Enable the TIM3 gloabal Interrupt */NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);/* Compute the prescaler value */uint16_t PrescalerValue =840-1; //84M頻率/840為100k(與timerscfg.h配置一致即可),即10us間隔/* Time base configuration */TIM_TimeBaseStructure.TIM_Period = 65535;TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;TIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);TIM_ClearITPendingBit(TIM3, TIM_SR_UIF);/* TIM3 enable counter */ //這里需要啟動(dòng)定時(shí)器TIM_Cmd(TIM3, ENABLE);/* Preset counter for a safe start */TIM_SetCounter(TIM3, 1);/* TIM Interrupts enable */TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); }//Set the timer for the next alarm. void setTimer(TIMEVAL value) {uint32_t timer = TIM_GetCounter(TIM3); // Copy the value of the running timerelapsed_time += timer - last_counter_val;last_counter_val = 65535-value;TIM_SetCounter(TIM3, 65535-value);TIM_Cmd(TIM3, ENABLE);//printf("setTimer %lu, elapsed %lu\r\n", value, elapsed_time); }//Return the elapsed time to tell the Stack how much time is spent since last call. TIMEVAL getElapsedTime(void) {uint32_t timer = TIM_GetCounter(TIM3); // Copy the value of the running timerif(timer < last_counter_val)timer += 65535;TIMEVAL elapsed = timer - last_counter_val + elapsed_time;//printf("elapsed %lu - %lu %lu %lu\r\n", elapsed, timer, last_counter_val, elapsed_time);return elapsed; }// This function handles Timer 3 interrupt request. void TIM3_IRQHandler(void) {//printf("--\r\n");if(TIM_GetFlagStatus(TIM3, TIM_SR_UIF) == RESET)return;last_counter_val = 0;elapsed_time = 0;TIM_ClearITPendingBit(TIM3, TIM_SR_UIF);TimeDispatch(); }

timer3.h

#ifndef __TIMER3_H #define __TIMER3_H#include "sys.h" #include "main.h"void TIM3_Init(void);#endif

2022年3月18日記:
定時(shí)器實(shí)現(xiàn)函數(shù)存在缺陷,當(dāng)超過(guò)一個(gè)功能需要調(diào)用時(shí)間時(shí),會(huì)存在干涉。各位如果除了心跳報(bào)文發(fā)送之外,沒(méi)用到其他需要時(shí)間的功能(節(jié)點(diǎn)掉線檢測(cè)/pdo之類),那么可以忽略。不然可以看一下這個(gè)CANopen補(bǔ)充–時(shí)間計(jì)算出錯(cuò)。

3.3 建立詞典


我們起名字為Master,使用心跳管理,這樣我們待會(huì)便可以通過(guò)心跳報(bào)文來(lái)判斷移植成功與否。

在字典里設(shè)置心跳報(bào)文間隔為1000ms(0x3E8)。這樣,它每隔1000ms就會(huì)發(fā)送一個(gè)心跳報(bào)文。

點(diǎn)擊保存,將生成的.od文件放入CANopen/dictionnary文件夾。

再點(diǎn)擊建立詞典,同樣將生成的.c文件放入CANopen/dictionnary文件夾。

效果如下:

文件說(shuō)明
.od文件詞典工程文件,用于配置,不會(huì)被工程調(diào)用
.c .h詞典文件對(duì)應(yīng)的c和h文件。需要被工程調(diào)用

3.4工程配置

文件都弄好了,我們打開(kāi)keil軟件,將這些文件都加入到工程。

3.41 c文件添加

在Groups里新建兩個(gè)文件夾。需要說(shuō)明的時(shí)候,為了美觀,這里把詞典文件和外設(shè)驅(qū)動(dòng)文件放在一起了。

文件夾說(shuō)明
CANopen含CANopen/src
CANopen_Driver含CANopen/hardware 和CANopen/dictionary。

3.42 頭文件路徑添加


3.43 c99標(biāo)準(zhǔn)選擇

由于源碼很多地方,把定義語(yǔ)句放在賦值語(yǔ)句之后,這只在C99標(biāo)準(zhǔn)之后允許,因此勾選C99模式。

3.44 調(diào)試串口設(shè)置

? 使用工程自帶的USART1。

? 警告,在項(xiàng)目中正常運(yùn)行后,一定要關(guān)閉調(diào)試功能,不然串口發(fā)送數(shù)據(jù)會(huì)嚴(yán)重降低相應(yīng)速度!!!!!

我們打開(kāi)applicfg.h ,如果找不到,直接全局搜索:MSG(…) 便可定位到啦。

第一,添加debug的定義 再次警告,在項(xiàng)目中正常運(yùn)行后,記得關(guān)閉(把定義注釋掉);第二,把打印函數(shù)里的\n 改成\r\n。

如圖是串口反饋的效果,還是挺直觀的。沒(méi)有USB-CAN的同學(xué)可以通過(guò)串口調(diào)試助手來(lái)觀察。

3.45 程序啟動(dòng)

首次,在main.h里添加相關(guān)頭文件

main函數(shù)添加canopen初始化。包含定時(shí)器3、串口1、can1的初始化

#include "sys.h" #include "main.h" int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設(shè)置系統(tǒng)中斷優(yōu)先級(jí)分組4delay_init(168); //初始化延時(shí)函數(shù)TIM3_Init();USART1_Init(115200);CAN1_Init(&Master_Data,1000000);unsigned char nodeID = 0x00; //主站IDsetNodeId(&Master_Data, nodeID);setState(&Master_Data, Initialisation); //節(jié)點(diǎn)初始化setState(&Master_Data, Operational); while(1){delay_ms(1000);} }

下載,啟動(dòng)!

使用軟件觀察。

心跳沒(méi)有問(wèn)題,nice
如果大家有需要讓主站檢測(cè)節(jié)點(diǎn)是否掉線的需要,可以看CANopen補(bǔ)充–主站檢測(cè)節(jié)點(diǎn)是否在線。

4 末尾

? 到這里便移植成功啦。下一篇教程基于STM32F4的CANopen快速SDO通信(超級(jí)詳細(xì))

?

總結(jié)

以上是生活随笔為你收集整理的基于STM32F4的CANOpen移植教程(超级详细)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。