《STM32 HAL库》CAN通信系列函数详尽解析——HAL_CAN_Init()

2024-06-22 11:36

本文主要是介绍《STM32 HAL库》CAN通信系列函数详尽解析——HAL_CAN_Init(),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

食用指南:本文主要内容为梳理CAN初始化函数主要运行逻辑及重点功能实现代码的详尽解析。函数源码在文末,建议在阅读源码之后观看。
CAN相关寄存器图:

主要逻辑分析:

下面分点梳理函数的主要逻辑(注意逻辑序号,后文依次为点进行分析):

  1. 首先,函数会检查传入的参数hcan是否为NULL,如果是NULL,则返回错误代码HAL_ERROR

  2. 接下来,函数会使用assert_param宏对传入的hcan结构体的各个参数进行检查,确保它们的取值范围符合要求。

  3. 如果宏USE_HAL_CAN_REGISTER_CALLBACKS的值为1并且hcan的状态为HAL_CAN_STATE_RESET,则表示使用了回调函数,并且需要将回调函数重置为默认的“legacy”函数。如果hcan->MspInitCallback为空,则将其设置为默认的初始化函数HAL_CAN_MspInit。然后调用hcanMspInitCallback函数,用于初始化底层硬件。

  4. 如果USE_HAL_CAN_REGISTER_CALLBACKS的值不为1或者hcan的状态不为HAL_CAN_STATE_RESET,则直接调用HAL_CAN_MspInit函数初始化底层硬件。

  5. 在初始化之前,首先需要将CAN控制器从睡眠模式唤醒。通过清除CAN_MCR_SLEEP位实现。

  1. 接着,获取当前的系统滴答计数器值tickstart,用于超时判断。

  2. 检查CAN控制器是否成功离开了睡眠模式。通过检查CAN_MSR_SLAK位,如果该位为0,则表示成功离开了睡眠模式。如果超时时间超过了预设的超时值CAN_TIMEOUT_VALUE,则更新错误代码并返回错误。

  3. 发送初始化请求,通过设置CAN_MCR_INRQ位实现。

  4. 获取当前的系统滴答计数器值tickstart,用于超时判断。

  5. 等待CAN控制器接受初始化请求。通过检查CAN_MSR_INAK位,如果该位为1,则表示CAN控制器已经接受了初始化请求。如果超时时间超过了预设的超时值CAN_TIMEOUT_VALUE,则更新错误代码并返回错误。

  6. 根据初始化参数设置CAN控制器的各种工作模式和配置,包括时间触发通信模式、自动总线断开管理、自动唤醒模式、自动重传、接收FIFO锁定模式和传输FIFO优先级等。

  1. 设置位时序寄存器BTR,将各个参数值写入寄存器中。

  2. 初始化错误代码和CAN状态。

  3. 返回函数执行状态HAL_OK

重点部分分析

逻辑 3

#if USE_HAL_CAN_REGISTER_CALLBACKS == 1if (hcan->State == HAL_CAN_STATE_RESET){/* Reset callbacks to legacy functions */hcan->RxFifo0MsgPendingCallback  =  HAL_CAN_RxFifo0MsgPendingCallback;  /* Legacy weak RxFifo0MsgPendingCallback */hcan->RxFifo0FullCallback        =  HAL_CAN_RxFifo0FullCallback;        /* Legacy weak RxFifo0FullCallback */hcan->RxFifo1MsgPendingCallback  =  HAL_CAN_RxFifo1MsgPendingCallback;  /* Legacy weak RxFifo1MsgPendingCallback */hcan->RxFifo1FullCallback        =  HAL_CAN_RxFifo1FullCallback;        /* Legacy weak RxFifo1FullCallback */hcan->TxMailbox0CompleteCallback =  HAL_CAN_TxMailbox0CompleteCallback; /* Legacy weak TxMailbox0CompleteCallback */hcan->TxMailbox1CompleteCallback =  HAL_CAN_TxMailbox1CompleteCallback; /* Legacy weak TxMailbox1CompleteCallback */hcan->TxMailbox2CompleteCallback =  HAL_CAN_TxMailbox2CompleteCallback; /* Legacy weak TxMailbox2CompleteCallback */hcan->TxMailbox0AbortCallback    =  HAL_CAN_TxMailbox0AbortCallback;    /* Legacy weak TxMailbox0AbortCallback */hcan->TxMailbox1AbortCallback    =  HAL_CAN_TxMailbox1AbortCallback;    /* Legacy weak TxMailbox1AbortCallback */hcan->TxMailbox2AbortCallback    =  HAL_CAN_TxMailbox2AbortCallback;    /* Legacy weak TxMailbox2AbortCallback */hcan->SleepCallback              =  HAL_CAN_SleepCallback;              /* Legacy weak SleepCallback */hcan->WakeUpFromRxMsgCallback    =  HAL_CAN_WakeUpFromRxMsgCallback;    /* Legacy weak WakeUpFromRxMsgCallback */hcan->ErrorCallback              =  HAL_CAN_ErrorCallback;              /* Legacy weak ErrorCallback */if (hcan->MspInitCallback == NULL){hcan->MspInitCallback = HAL_CAN_MspInit; /* Legacy weak MspInit */}/* Init the low level hardware: CLOCK, NVIC */hcan->MspInitCallback(hcan);}

让我们一句一句地详细分析这段代码:

#if USE_HAL_CAN_REGISTER_CALLBACKS == 1

这是一个条件编译的预处理指令,它检查宏定义USE_HAL_CAN_REGISTER_CALLBACKS是否等于1。如果等于1,则表示要使用回调函数注册功能。

if (hcan->State == HAL_CAN_STATE_RESET)
{

这是一个条件语句,它检查CAN的状态hcan->State是否等于HAL_CAN_STATE_RESET。只有当CAN处于复位状态时,才会执行接下来的代码块。

/* Reset callbacks to legacy functions */
hcan->RxFifo0MsgPendingCallback  =  HAL_CAN_RxFifo0MsgPendingCallback;  /* Legacy weak RxFifo0MsgPendingCallback */
……

这一系列的语句将CAN处理器结构体hcan中的回调函数成员设置为默认的回调函数。这些默认的回调函数被称为”Legacy weak”,表示它们是在历史版本中使用的弱定义回调函数。以第一句为例,作用是将HAL_CAN_RxFifo0MsgPendingCallback的回调函数的地址赋值给hcan->RxFifo0MsgPendingCallback,即将CAN模块的RxFIFO0消息待处理回调函数指针指向一个特定的函数。在CAN模块中,当RxFIFO0内有消息待处理时,可以通过注册一个回调函数来通知应用程序进行相应的处理。

点击《STM32 HAL库》CAN通信系列函数详尽解析——HAL_CAN_Init()——古月居可查看全文

这篇关于《STM32 HAL库》CAN通信系列函数详尽解析——HAL_CAN_Init()的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084179

相关文章

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Spring MVC使用视图解析的问题解读

《SpringMVC使用视图解析的问题解读》:本文主要介绍SpringMVC使用视图解析的问题解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC使用视图解析1. 会使用视图解析的情况2. 不会使用视图解析的情况总结Spring MVC使用视图

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

kotlin的函数forEach示例详解

《kotlin的函数forEach示例详解》在Kotlin中,forEach是一个高阶函数,用于遍历集合中的每个元素并对其执行指定的操作,它的核心特点是简洁、函数式,适用于需要遍历集合且无需返回值的场... 目录一、基本用法1️⃣ 遍历集合2️⃣ 遍历数组3️⃣ 遍历 Map二、与 for 循环的区别三、高

利用Python和C++解析gltf文件的示例详解

《利用Python和C++解析gltf文件的示例详解》gltf,全称是GLTransmissionFormat,是一种开放的3D文件格式,Python和C++是两个非常强大的工具,下面我们就来看看如何... 目录什么是gltf文件选择语言的原因安装必要的库解析gltf文件的步骤1. 读取gltf文件2. 提

Java中的runnable 和 callable 区别解析

《Java中的runnable和callable区别解析》Runnable接口用于定义不需要返回结果的任务,而Callable接口可以返回结果并抛出异常,通常与Future结合使用,Runnab... 目录1. Runnable接口1.1 Runnable的定义1.2 Runnable的特点1.3 使用Ru