连续折腾了多个晚上,又趁周末又花了一天时间,终于把powerlink协议移植成功到单片机上啦。本想放弃,但想了下不管我能不能用上,结个尾吧,分享给有需要的人。放弃并不难,但坚持一定很酷。为了移植测试这个协议花了不少血本。stm32开发板就买了两套,其中第一套板子在移植过程中发现内存不够用,型号买小啦,最后买了stm32F407ZGT6的开发板。

前言

STM32F407ZGT6芯片资源1M的falsh,192k的内存够用了。买的第一套Alientek的miniSTM32开发板芯片型号stm32F103RCT6,芯片资源256k的flash,48k的ram不够用(主要是ram不够用)。因为移植过程中发现这powerlink协议栈挺占内存的,rom倒是占的不大。

汇总下资源占用情况,分享给有需要的人参考。(资源包含嵌入式系统RTX内核源码和从站demo功能源码在内)

Program Size: Code=94008 RO-data=15352 RW-data=4204 ZI-data=62212

RW-data+ZI-data 内存占用 4204+62212  = 66416,超过60K了,主要是字典文件占用内存大。Rom占用:94k。

项目开源,欢迎测试评测。开源地址:powerlink-stm32: powerlink-stm32

GitHub - yangyongzhen/powerlink-stm32: openPOWERLINK stack on stm32 mcu transplant

我使用的开发板如下图所示长这样:

移植工程结构:

从工程结构上看,我把涉及改动的文件都单独放到port文件夹了。里面涉及的文件挺多的,不过好在代码量不太大。使用Keil自带的RTX嵌入式内核系统的相关特性,移植不算难。

移植过程

协议栈移植

移植过程参考之前分享的一篇文章《POWERLINK协议源码(最新)在stm32单片机上的移植指南》POWERLINK协议源码(最新)在stm32单片机上的移植指南,把涉及的相关文件摘出来。

屏蔽掉跟系统或驱动相关的接口和编译错误,建立工程目录结构。

netif-stm32.c和target-stm32.c代码量不大,很好移植。

target-stm32.c中主要涉及target_msleep,target_enableGlobalInterrupt,target_getTickCount等的实现。使用RTX系统的相关api实现即可。target_setIpAdrs接口不需要,留空即可。

target-mutex.c文件移植:

/**
\brief  Create MutexThe function creates a mutex.\param[in]      mutexName_p         The name of the mutex to create.
\param[out]     pMutex_p            Pointer to store the created mutex.\return The function returns a tOplkError error code.
\retval kErrorOk                    Mutex was successfully created.
\retval kErrorNoFreeInstance        An error occurred while creating the mutex.\ingroup module_target
*/
//------------------------------------------------------------------------------
tOplkError target_createMutex(const char* mutexName_p,OPLK_MUTEX_T* pMutex_p)
{UNUSED_PARAMETER(mutexName_p);pMutex_p =  osMutexNew(NULL);return kErrorOk;
}//------------------------------------------------------------------------------
/**
\brief  Destroy MutexThe function destroys a mutex.\param[in]      mutexId_p           The ID of the mutex to destroy.\ingroup module_target
*/
//------------------------------------------------------------------------------
void target_destroyMutex(OPLK_MUTEX_T mutexId_p)
{
//CloseHandle(mutexId_p);if(mutexId_p != NULL){osMutexDelete(mutexId_p);}
}//------------------------------------------------------------------------------
/**
\brief  Lock MutexThe function locks a mutex.\param[in]      mutexId_p           The ID of the mutex to lock.\return The function returns a tOplkError error code.
\retval kErrorOk                    Mutex was successfully locked.
\retval kErrorNoFreeInstance        An error occurred while locking the mutex.\ingroup module_target
*/
//------------------------------------------------------------------------------
tOplkError target_lockMutex(OPLK_MUTEX_T mutexId_p)
{tOplkError  ret;osStatus_t status;ret = kErrorOk;if (mutexId_p != NULL) {status = osMutexAcquire(mutexId_p, osWaitForever);if (status != osOK)  {// handle failure code}}    return ret;
}//------------------------------------------------------------------------------
/**
\brief  Unlock MutexThe function unlocks a mutex.\param[in]      mutexId_p           The ID of the mutex to unlock.\ingroup module_target
*/
//------------------------------------------------------------------------------
void target_unlockMutex(OPLK_MUTEX_T mutexId_p)
{//ReleaseMutex(mutexId_p);osStatus_t status;if (mutexId_p != NULL)  {status = osMutexRelease(mutexId_p);if (status != osOK)  {// handle failure code}}
}int target_lock(void)
{target_enableGlobalInterrupt(FALSE);return 0;
}
int target_unlock(void)
{target_enableGlobalInterrupt(TRUE);return 0;
}

使用RTX的Mutex互斥量api接口,这部分很容易移植。

circbuf-stm32.c文件中,主要涉及加锁和解锁,也好移植。

//------------------------------------------------------------------------------
/**
\brief  Lock circular bufferThe function enters a locked section of the circular buffer.\param[in]      pInstance_p         Pointer to circular buffer instance.\ingroup module_lib_circbuf
*/
//------------------------------------------------------------------------------
void circbuf_lock(tCircBufInstance* pInstance_p)
{osStatus_t              waitResult;tCircBufArchInstance*   pArchInstance;// Check parameter validityASSERT(pInstance_p != NULL);pArchInstance = (tCircBufArchInstance*)pInstance_p->pCircBufArchInstance;waitResult = osMutexAcquire(pArchInstance->lockMutex, osWaitForever);switch (waitResult) {case osOK:break;default:DEBUG_LVL_ERROR_TRACE("%s() Mutex wait unknown error! Error:%ld\n",__func__);break;}
}//------------------------------------------------------------------------------
/**
\brief  Unlock circular bufferThe function leaves a locked section of the circular buffer.\param[in]      pInstance_p         Pointer to circular buffer instance.\ingroup module_lib_circbuf
*/
//------------------------------------------------------------------------------
void circbuf_unlock(tCircBufInstance* pInstance_p)
{tCircBufArchInstance* pArchInstance;// Check parameter validityASSERT(pInstance_p != NULL);pArchInstance = (tCircBufArchInstance*)pInstance_p->pCircBufArchInstance;osMutexRelease(pArchInstance->lockMutex);
}

eventkcal-stm32.c文件移植:

这个参考了eventkcal-win32.c的实现,比 eventkcal-linux.c的简单些。使用RTX的信号量机制,实现替代也不难。

//------------------------------------------------------------------------------
/**
\brief  Ethernet driver initializationThis function initializes the Ethernet driver.\param[in]      pEdrvInitParam_p    Edrv initialization parameters\return The function returns a tOplkError error code.\ingroup module_edrv
*/
//------------------------------------------------------------------------------
tOplkError edrv_init(const tEdrvInitParam* pEdrvInitParam_p)
{// Check parameter validityASSERT(pEdrvInitParam_p != NULL);// Clear instance structureOPLK_MEMSET(&edrvInstance_l, 0, sizeof(edrvInstance_l));if (pEdrvInitParam_p->pDevName == NULL)return kErrorEdrvInit;// Save the init dataedrvInstance_l.initParam = *pEdrvInitParam_p;edrvInstance_l.fStartCommunication = TRUE;edrvInstance_l.fThreadIsExited = FALSE;// If no MAC address was specified read MAC address of used// Ethernet interfaceif ((edrvInstance_l.initParam.aMacAddr[0] == 0) &&(edrvInstance_l.initParam.aMacAddr[1] == 0) &&(edrvInstance_l.initParam.aMacAddr[2] == 0) &&(edrvInstance_l.initParam.aMacAddr[3] == 0) &&(edrvInstance_l.initParam.aMacAddr[4] == 0) &&(edrvInstance_l.initParam.aMacAddr[5] == 0)){   // read MAC address from controllergetMacAdrs(edrvInstance_l.initParam.pDevName,edrvInstance_l.initParam.aMacAddr);}edrvInstance_l.sock = socket(0, Sn_MR_MACRAW, 0,0);if (edrvInstance_l.sock < 0){DEBUG_LVL_ERROR_TRACE("%s() cannot open socket\n", __func__);return kErrorEdrvInit;}edrvInstance_l.hThread = osThreadNew(workerThread,&edrvInstance_l,NULL);
//    // wait until thread is started
//    sem_wait(&edrvInstance_l.syncSem);return kErrorOk;
}
//------------------------------------------------------------------------------
/**
\brief  Event handler thread functionThis function contains the main function for the event handler thread.\param[in]      arg                 Thread parameter. Used to get the instance structure.\return The function returns the thread exit code.
*/
//------------------------------------------------------------------------------
static void eventThread(void* arg)
{const tEventkCalInstance*   pInstance = (const tEventkCalInstance*)arg;osStatus_t waitResult;DEBUG_LVL_EVENTK_TRACE("Kernel event thread %d waiting for events...\n", GetCurrentThreadId());while (!pInstance->fStopThread){waitResult = osSemaphoreAcquire(pInstance->semKernelData, 100UL);       // wait for max. 10 ticks for semaphore token to get availableswitch (waitResult) {case osOK:if (eventkcal_getEventCountCircbuf(kEventQueueKInt) > 0){eventkcal_processEventCircbuf(kEventQueueKInt);}else{if (eventkcal_getEventCountCircbuf(kEventQueueU2K) > 0){eventkcal_processEventCircbuf(kEventQueueU2K);}}break;case osErrorResource:DEBUG_LVL_ERROR_TRACE("kernel event osErrorResource!\n");break;case osErrorParameter:DEBUG_LVL_ERROR_TRACE("kernel event osErrorParameter!\n");break;case osErrorTimeout:DEBUG_LVL_ERROR_TRACE("kernel event timeout!\n");break;default:DEBUG_LVL_ERROR_TRACE("%s() Semaphore wait unknown error! \n",__func__);break;}}DEBUG_LVL_EVENTK_TRACE("Kernel event thread is exiting!\n");}

edrv-rawsock_stm32.c文件移植:

这个很重要,网格底层通信相关的都在这个文件里。使用w5500模块提供的api,操作原始MAC报文帧的方式实现。pthread_mutex_lock和sem_post这些linux系统的互斥量和信号量等,都用RTX提供的相关接口替换。

//------------------------------------------------------------------------------
/**
\brief  Send Tx bufferThis function sends the Tx buffer.\param[in,out]  pBuffer_p           Tx buffer descriptor\return The function returns a tOplkError error code.\ingroup module_edrv
*/
//------------------------------------------------------------------------------
tOplkError edrv_sendTxBuffer(tEdrvTxBuffer* pBuffer_p)
{int    sockRet;// Check parameter validityASSERT(pBuffer_p != NULL);FTRACE_MARKER("%s", __func__);if (pBuffer_p->txBufferNumber.pArg != NULL)return kErrorInvalidOperation;if (getLinkStatus(edrvInstance_l.initParam.pDevName) == FALSE){/* If there is no link, we pretend that the packet is sent and immediately call* tx handler. Otherwise the stack would hang! */if (pBuffer_p->pfnTxHandler != NULL){pBuffer_p->pfnTxHandler(pBuffer_p);}}else{//pthread_mutex_lock(&edrvInstance_l.mutex);osMutexAcquire(edrvInstance_l.mutex,osWaitForever);if (edrvInstance_l.pTransmittedTxBufferLastEntry == NULL){edrvInstance_l.pTransmittedTxBufferLastEntry = pBuffer_p;edrvInstance_l.pTransmittedTxBufferFirstEntry = pBuffer_p;}else{edrvInstance_l.pTransmittedTxBufferLastEntry->txBufferNumber.pArg = pBuffer_p;edrvInstance_l.pTransmittedTxBufferLastEntry = pBuffer_p;}//pthread_mutex_unlock(&edrvInstance_l.mutex);osMutexRelease(edrvInstance_l.mutex);sockRet = send(edrvInstance_l.sock, (u_char*)pBuffer_p->pBuffer, (int)pBuffer_p->txFrameSize);if (sockRet < 0){DEBUG_LVL_EDRV_TRACE("%s() send() returned %d\n", __func__, sockRet);return kErrorInvalidOperation;}else{packetHandler((u_char*)&edrvInstance_l, sockRet, pBuffer_p->pBuffer);}}return kErrorOk;
}
//------------------------------------------------------------------------------
/**
\brief  Edrv worker threadThis function implements the edrv worker thread. It is responsible to receive frames\param[in,out]  pArgument_p         User specific pointer pointing to the instance structure\return The function returns a thread error code.
*/
//------------------------------------------------------------------------------
static void workerThread(void* pArgument_p)
{tEdrvInstance*  pInstance = (tEdrvInstance*)pArgument_p;int             rawSockRet;u_char          aBuffer[EDRV_MAX_FRAME_SIZE];DEBUG_LVL_EDRV_TRACE("%s(): ThreadId:%ld\n", __func__, syscall(SYS_gettid));// signal that thread is successfully started//sem_post(&pInstance->syncSem);osSemaphoreRelease(pInstance->syncSem);while (edrvInstance_l.fStartCommunication){rawSockRet = recvfrom(edrvInstance_l.sock, aBuffer, EDRV_MAX_FRAME_SIZE, 0, 0);if (rawSockRet > 0){packetHandler(pInstance, rawSockRet, aBuffer);}}edrvInstance_l.fThreadIsExited = TRUE;}

从站demo移植

移植从站的demo, demo_cn_console文件夹里的从站demo,在上述协议栈成功移植的基础上,这部分从站demo移植很简单。

/*
** main function
**
**  Arguments:
**      none
**
*/
int main (int argc, char* argv[])
{tOplkError  ret = kErrorOk;tOptions    opts;// System InitializationSystemCoreClockUpdate();if (getOptions(argc, argv, &opts) < 0)return 0;LED_Initialize();uart_init();//stdout_init();printf("hello test\r\n");LED_On(2);spi_init();reset_w5500();set_w5500_mac();set_w5500_ip();eventlog_init(opts.logFormat,opts.logLevel,opts.logCategory,(tEventlogOutputCb)console_printlogadd);initEvents(&fGsOff_l);printf("----------------------------------------------------\n");printf("openPOWERLINK console CN DEMO application\n");printf("Using openPOWERLINK stack: %s\n", oplk_getVersionString());printf("----------------------------------------------------\n");eventlog_printMessage(kEventlogLevelInfo,kEventlogCategoryGeneric,"demo_cn_console: Stack version:%s Stack configuration:0x%08X",oplk_getVersionString(),oplk_getStackConfiguration());ret = initPowerlink(CYCLE_LEN,opts.devName,aMacAddr_l,opts.nodeId);if (ret != kErrorOk)goto Exit;ret = initApp();if (ret != kErrorOk)goto Exit;osKernelInitialize();                       // Initialize CMSIS-RTOSosThreadNew(Main_Loop_Thread, NULL, NULL);   // Create application main threadosThreadNew(LED_Blink_PortE, NULL, NULL);   // Create application test threadosKernelStart();                            // Start thread executionfor (;;) {//Dummy infinite for loop.}
Exit:   printf("openPOWERLINK console Exit\n");shutdownApp();shutdownPowerlink();return 0;
}

如何使用

完成上述移植过程后,需要下载到板子上运行。需要配置好串口管脚,方便串口输出日志调试看。spi的管脚也需要根据板子上的实际资源配置好。然后接上网线,先运行起来主站,然后运行从站,结合串口打印日志调试。

POWERLINK协议在stm32单片机+w5500移植成功经验分享相关推荐

  1. linux vlc流媒体服务器,vlc media server rtsp 流媒体服务器搭建成功经验分享

    vlc 由videolan.org 出品,开源免费的一款本地和网络播放器,支持个音视频格式非常多,还可以用来搭建 rtsp 流媒体服务器,非常好用,支持的平台很多:windows.mac.linux都 ...

  2. STM32的CAN总线调试经验分享

    相关文章 CAN总线简易入门教程 CAN总线显性电平和隐性电平详解 STM32的CAN总线调试经验分享 文章目录 相关文章 背景 CAN总线 CAN控制器 CAN收发器 调试过程 硬件排查 CAN分析 ...

  3. 复试复旦大学计算机博士,【华慧推荐】2018年复旦大学博士面试成功经验分享...

    原标题:[华慧推荐]2018年复旦大学博士面试成功经验分享 2018年复旦大学博士面试成功经验分享 导语:笔者是参加2018年复旦大学博士面试,跨专业考博,几经波折,最后2分钟扭转局面,体验了地狱天堂 ...

  4. 最新 UltraEdit 24.20 注册成功经验分享

    注册完成效果图 最新 UltraEdit 24.20 注册成功经验分享 UE24.20 官方中文版+注册机下载地址 http://download.csdn.net/download/special0 ...

  5. 中南大学计算机学硕很难考吗,2019中南大学计算机专业考研成功经验分享

    原标题:2019中南大学计算机专业考研成功经验分享 1.我的基本情况 先说下我的基本情况,计算机专硕, 初试386分,排名24, 复试排名第一 ,总分第二(今年上400分的并不多,但是因为报考得人数太 ...

  6. 清华计算机系行政面试,清华大学复试面试过程及成功经验分享

    原标题:清华大学复试面试过程及成功经验分享 [勤思考研分享]复试中的面试是非常重要的内容.下面为大家带来我的面试过程和一些经验总结,希望对备战复试的考生们有所帮助. 个人面试 每个人要单独面对三名考官 ...

  7. MacBook2016在SSD上安装Win To Go(成功经验分享)

    1.WTG辅助工具下载 https://bbs.luobotou.org/thread-761-1-1.html2.下载Win10镜像 https://msdn.itellyou.cn/3.默认&qu ...

  8. 中国人民大学计算机专硕好考吗,干货:中国人民大学考研复试成功经验分享,值得收藏!...

    考研之路或许不会一帆风顺,回望过去,有备考途中的失意,有初试通过的欣喜,有家人朋友的陪伴--不到最后时刻,永远不轻言放弃,不到最后胜利,永远不掉以轻心. 今天,小编为你们准备了干货满满的考研复试经验, ...

  9. 2010年5月27日俱乐部晚场活动,“iPhone应用成功经验分享”主题研讨活动

    5月27日晚上,CTO俱乐部第22期主题沙龙在北京成功举办,本次沙龙邀请到五位iPhone应用开发领域的创富英雄与到场的五十多位会员朋友分享了他们对移动开发领域前景的看法. 主题:如何在iPhone和 ...

最新文章

  1. 小程序开发需要注意什么
  2. Python字典(dict )的几种遍历方式
  3. 第十八课 色彩样式与滤镜
  4. AngularJS集合数据遍历显示
  5. myeclipse搭建php,MyEclipse配置JDK类库的简易流程
  6. 【C++学习笔记三】C++多态、抽象(接口)
  7. 又一所“国字头”大学要来?屠呦呦也在
  8. binwalk 提取bootimg_boot.img格式文件结构解析
  9. oracle递归树查询
  10. Eclipse 中 program arguments 与 VM arguments 的区别
  11. 正确的Kado ED「永遠のこたえ」
  12. html滑动验证,html5移动端按住滑块拖动验证代码
  13. 旷视研究院参会PRCV2019 推进模式识别与CV技术交流
  14. cs5460a c语言程序,cs5460a应用电路(含源程序)
  15. 哇塞,原来自己写 Google Chrome 浏览器扩展(插件)这么容易!
  16. Android仿自如客APP裸眼3D效果
  17. 天池-小布助手对话短文本语义匹配 复赛rank3、决赛rank4代码及解决方案
  18. 趣图 | EDG牛逼!!!
  19. python计算复数的辐角,(Python 3)1051复数乘法(15分),python31051
  20. 工作流(Workflow)简介

热门文章

  1. 合创视觉科技UI设计全套设计推荐指南
  2. 蛋花花浅谈人工智能主要应用于哪些方面
  3. HTTPS 防范中间人攻击原理
  4. 织梦实现软件下载排行调用 dedecms软件下载排行调用
  5. 方正证券 车联网行业专题研究报告(2019年)
  6. 9-3修复画笔/修补/污点修复画笔/颜色替换/红眼移除工具
  7. 花钱学python值得么_关于花的作文,花的作文,描写花的作文
  8. Python字典在循环中的使用:用 for 遍历字典
  9. 读《怦然心动的人生整理魔法》
  10. WebService最详细教程