其他分享
首页 > 其他分享> > Ardupilot UAVCAN 学习《4》

Ardupilot UAVCAN 学习《4》

作者:互联网

目录

文章目录

摘要


本节主要记录如何通过ardupilot的uavcan来实现一个Service帧信息,进而配置UAVCAN电调。有问题可以一起交流@18129927205


1.初始化一个Service 请求数据帧


还记得一个数据帧有Message帧(广播)和Service帧(非广播),这里我们重点学习如何创建Service帧。在创建Service帧时,需要创建客户端和服务器,这里的客户端指的是请求方,服务器指的是应答方。


在这里插入图片描述
这里我们通过飞控配置电调的指示灯,首先看下电调指示灯配置文档,如下图。

帧格式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这些就是我们要参考的文档知识。

1.定义UAVCAN 驱动文件

在目录ardupilot/modules/uavcan/dsdl/uavcan/protocol中定义如下文件,注意文件名的含义(212是我们的Node ID不是CAN ID )。
在这里插入图片描述

在目录ardupilot/modules/uavcan/libuavcan/dsdl_compiler/pyuavcan/dsdl/uavcan/protocol中定义如下文件,注意文件名的含义。
在这里插入图片描述
注意两个文件完全一样,内容如下:
在这里插入图片描述

2.初始化驱动文件

  1. void Copter::setup()—》 //API函数初始化接口
  2. init_ardupilot()—》 //设备层初始化接口
  3. BoardConfig_CAN.init(); //ardupilot板层can初始化函数接口
void AP_BoardConfig_CAN::init()
{

    for (uint8_t i = 0; i < MAX_NUMBER_OF_CAN_INTERFACES; i++) //最多支持两个can总线
    {
    	//_driver_number驱动号
        _st_driver_number[i] = (int8_t) _var_info_can[i]._driver_number;
        //_can_debug调试号
        _st_can_debug[i] = (int8_t) _var_info_can[i]._can_debug;
    }
    //初始化can总线
    setup_canbus();

}

4. setup_canbus();

5. _var_info_can_protocol[i]._uavcan->try_init() == true


到这里进入核心初始化代码,在文件目录ardupilot/libraries文件下找到AP_UAVCAN文件夹会看到AP_UAVCAN.cpp和AP_UAVCAN.h文件。


1》增加头文件

#include <uavcan/protocol/Led.hpp>

备注:这个文件是在编译时自动生成的Led.hpp,文件来源于我们定义的212.Led.uavcan那个文件


2》增加客户端函数和控制函数

//定义LED控制函数
static void led_ctrl_Info(const uavcan::ServiceCallResult<uavcan::protocol::Led>& resp);
//定义客户端函数
static uavcan::ServiceClient<uavcan::protocol::Led>* Led_Ctrl_client[MAX_NUMBER_OF_CAN_DRIVERS];

3》初始化客户端和回调函数

bool AP_UAVCAN::try_init(void)
{
    //父类对象
    if (_parent_can_mgr == nullptr)
    {
        return false;
    }
    //初始化OK吗
    if (_initialized)
    {
        return true;
    }
    //对象初始化OK吗
    if (!_parent_can_mgr->is_initialized())
    {
        return false;
    }
    _uavcan_i = UINT8_MAX;
    for (uint8_t i = 0; i < MAX_NUMBER_OF_CAN_DRIVERS; i++)
    {
        if (_parent_can_mgr == hal.can_mgr[i])
        {
            _uavcan_i = i;
            break;
        }
    }
    if(_uavcan_i == UINT8_MAX)
    {
        return false;
    }
    //获取结点
    auto *node = get_node();
    if (node == nullptr)
    {
        return false;
    }
    //结点初始化OK
    if (node->isStarted())
    {
        return false;
    }
    uint8_t driver_index = _uavcan_i;
    uavcan::NodeID self_node_id(_uavcan_node); //设置节点ID=10
    node->setNodeID(self_node_id);

    char ndname[20];
    snprintf(ndname, sizeof(ndname), "org.ardupilot:%u", _uavcan_i);

    uavcan::NodeStatusProvider::NodeName name(ndname);
    node->setName(name);
    // Standard type uavcan.protocol.SoftwareVersion
    //标准型uavcan.protocol.SoftwareVersion协议
    uavcan::protocol::SoftwareVersion sw_version;
    sw_version.major = AP_UAVCAN_SW_VERS_MAJOR;  //1
    sw_version.minor = AP_UAVCAN_SW_VERS_MINOR;  //1
    node->setSoftwareVersion(sw_version);  //设置软件版本

    uavcan::protocol::HardwareVersion hw_version; // Standard type uavcan.protocol.HardwareVersion
    //设置硬件版本1.0
    hw_version.major = AP_UAVCAN_HW_VERS_MAJOR;
    hw_version.minor = AP_UAVCAN_HW_VERS_MINOR;
    node->setHardwareVersion(hw_version);
    //初始化节点
    const int node_start_res = node->start();

    if (node_start_res < 0)
    {
        debug_uavcan(1, "UAVCAN: node start problem\n\r");
    }
	   //初始化LED控制客户端
	    Led_Ctrl_client[driver_index] = new uavcan::ServiceClient<uavcan::protocol::Led>(*_node);
	    if (Led_Ctrl_client[driver_index] == nullptr)
	    {
	        return false;
	    }
       //初始化LED控制函数
	    int res = Led_Ctrl_client[driver_index]->init();
	    //调用Led
		 Led_Ctrl_client[driver_index]->setCallback(led_ctrl_Info);
	    //设置优先级
	    Led_Ctrl_client[driver_index]->setPriority(16);
	     //设置服务节点是1
	     uavcan::NodeID service_res_node_id(1);
	     uavcan::protocol::Led::Request request;
	     request.option=1;
	     request.color=1;
	     request.blink=0;
	     //直接调用
	     Led_Ctrl_client[0]->call(service_res_node_id,request);
	     //直接调用
	    if (res < 0)
	    {
	        return false;
	    }
	   ......
	   **后续省略**
	   .....
}

在这里插入图片描述


到这里初始化函数基本完成,对于控制函数可以暂时不写,如果你想接收服务器端的数据过来,可以在控制函数中进行解析收到的服务器数据。


3.分析初始化函数

重点两个函数需要分析:init()和call()函数
找到ardupilot/modules/uavcan/libuavcan/include/uavcan/node/service_client.hpp
可以找到这两个定义
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里我们只先分析init函数,对应call我们在loop中进行分析

    int init()
    {
      //PublisherType publisher_;
        return publisher_.init();
    }

可以看出publisher属于PublisherType publisher_;的一个对象,PublisherType属于ServiceClient的一个私有模板数据成员
在这里插入图片描述
在这里插入图片描述

而typedef GenericPublisher<DataType, RequestType> PublisherType;中的第二个参数RequestType,属于typedef typename DataType::Request RequestType;
可以看出都是属于Request类型的

我们分析下面这个函数

    int init(TransferPriority priority)
    {
        return publisher_.init(priority);
    }

执行
这个下面函数Init方法可以在第一次发布之前调用,但不必 因为发布服务器可以自动初始化。

    int init()
    {
        return checkInit();
    }

template <typename DataSpec, typename DataStruct>
int GenericPublisher<DataSpec, DataStruct>::checkInit()
{
    if (isInited())
    {
        return 0;
    }
    return doInit(DataTypeKind(DataSpec::DataTypeKind), DataSpec::getDataTypeFullName(), CanTxQueue::Qos(Qos));
}

注意这里面又两个函数

bool GenericPublisherBase::isInited() const
{
    return sender_.isInitialized();
}
int GenericPublisherBase::doInit(DataTypeKind dtkind, const char* dtname, CanTxQueue::Qos qos)
{
    if (isInited())
    {
        return 0;
    }

    GlobalDataTypeRegistry::instance().freeze();

    const DataTypeDescriptor* const descr = GlobalDataTypeRegistry::instance().find(dtkind, dtname);
    if (descr == UAVCAN_NULLPTR)
    {
        UAVCAN_TRACE("GenericPublisher", "Type [%s] is not registered", dtname);
        return -ErrUnknownDataType;
    }
    //初始化
    sender_.init(*descr, qos);

    return 0;
}

继续执行

/**
 * 传送ID初始化
 */
void TransferSender::init(const DataTypeDescriptor& dtid, CanTxQueue::Qos qos)
{
    UAVCAN_ASSERT(!isInitialized());

    qos_          = qos;
    data_type_id_ = dtid.getID();
    crc_base_     = dtid.getSignature().toTransferCRC();
}

建议这部分代码先不要深入研究,等用起来后再仔细分析。


2.周期执行一个Service 请求


在这里插入图片描述

void Scheduler::_uavcan_thread(void *arg)
{
    Scheduler *sched = (Scheduler *)arg;
    chRegSetThreadName("apm_uavcan");
    while (!sched->_hal_initialized)
    {
        sched->delay_microseconds(20000);
    }
    while (true)
    {

        sched->delay_microseconds(300);
        for (int i = 0; i < MAX_NUMBER_OF_CAN_INTERFACES; i++)
        {
            if (AP_UAVCAN::get_uavcan(i) != nullptr)
            {
            	 //调用定时器函数
                CANManager::from(hal.can_mgr[i])->_timer_tick();
            }
        }
    }
}
void CANManager::_timer_tick()
{
    if (!initialized_) return;

    if (p_uavcan != nullptr)
    {
        p_uavcan->do_cyclic(); //循环处理
    }
}
void AP_UAVCAN::do_cyclic(void)
{
	static uint8_t count=0;
    if (!_initialized)
    {
        hal.scheduler->delay_microseconds(1000);
        return;
    }

    //获取结点
    auto *node = get_node();
    count++;

    if(count>=20)
    {
        //设置服务节点是1
        uavcan::NodeID service_res_node_id(1);
        uavcan::protocol::Led::Request request;
        request.option=1;
        request.color=1;
        request.blink=0;
        //直接调用
        Led_Ctrl_client[0]->call(service_res_node_id,request);
        count=0;
    }

     //开始执行
    const int error = node->spin(uavcan::MonotonicDuration::fromMSec(1));  //获取解析数据
    if (error < 0)
    {
        hal.scheduler->delay_microseconds(100);
        return;
    }
......
此处省略
......

}

在这里插入图片描述

重点函数:

Led_Ctrl_client[0]->call(service_res_node_id,request);

在这里插入图片描述
这个函数传入了两个参数,第一个是服务节点ID,第二个是客户端请求的带的参数对象
在这里插入图片描述

template <typename DataType_, typename Callback_>
int ServiceClient<DataType_, Callback_>::call(NodeID server_node_id, const RequestType& request,
                                              ServiceCallID& out_call_id)
{

    if (!coerceOrFallback<bool>(callback_, true))
    {
    	printf("Invalid callback\r\n");
        UAVCAN_TRACE("ServiceClient", "Invalid callback");
        return -ErrInvalidConfiguration;
    }
    /*
     * 不依赖于结构数据类型的公共过程
     * Common procedures that don't depend on the struct data type
     */
    const int prep_res =prepareToCall(SubscriberType::getNode(), DataType::getDataTypeFullName(), server_node_id, out_call_id);
    if (prep_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to prepare the call, error: %i", prep_res);
        return prep_res;
    }

    /*
     * Initializing the call state - this will start the subscriber ad-hoc
     * 初始化呼叫状态-这将临时启动订户
     */
    const int call_state_res = addCallState(out_call_id);
    if (call_state_res < 0)
    {
        UAVCAN_TRACE("ServiceClient", "Failed to add call state, error: %i", call_state_res);
        return call_state_res;
    }
    /*
     * 配置侦听器以便它只接受匹配的响应
     * TODO移动到init(),但这需要对GenericSubscriber进行一些重构<>(删除TransferForwarder)
     * Configuring the listener so it will accept only the matching responses
     * TODO move to init(), but this requires to somewhat refactor GenericSubscriber<> (remove TransferForwarder)
     */
    TransferListenerWithFilter* const tl = SubscriberType::getTransferListener();
    if (tl == UAVCAN_NULLPTR)
    {
        UAVCAN_ASSERT(0);  // Must have been created
        cancelCall(out_call_id);
        return -ErrLogic;
    }
    tl->installAcceptanceFilter(this);

    /*
     *发布请求------Publishing the request
     */
    const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id,
                                                 out_call_id.transfer_id);
    if (publisher_res < 0)
    {
        cancelCall(out_call_id);
        return publisher_res;
    }

    UAVCAN_ASSERT(server_node_id == out_call_id.server_node_id);
    return publisher_res;
}

重点分析一下几个函数


1.callback_函数


这个函数指的是哪个函数呢?还得的初始化时我们设置的回调函数吗?

	 Led_Ctrl_client[driver_index]->setCallback(led_ctrl_Info);

在这里插入图片描述
从这里可以看出callback_指的就是led_ctrl_Info()函数。


2.prepareToCall()函数


const int prep_res =prepareToCall(SubscriberType::getNode(), DataType::getDataTypeFullName(), server_node_id, out_call_id);

这里面又四个参数:要弄明白这四个参数的含义

int ServiceClientBase::prepareToCall(INode& node,
                                     const char* dtname,
                                     NodeID server_node_id,
                                     ServiceCallID& out_call_id)
int ServiceClientBase::prepareToCall(INode& node,
                                     const char* dtname,
                                     NodeID server_node_id,
                                     ServiceCallID& out_call_id)
{
    /*
     * 确保不会因为输入数据无效而导致传输错误
     * Making sure we're not going to get transport error because of invalid input data
     */
    //判断服务节点是不是非广播,服务节点等于NODE ID吗
    if (!server_node_id.isUnicast() || (server_node_id == node.getNodeID()))
    {
        UAVCAN_TRACE("ServiceClient", "Invalid Server Node ID");
        return -ErrInvalidParam;
    }
    //参数传递
    out_call_id.server_node_id = server_node_id;
    /*
     * 确定数据类型ID
     * Determining the Data Type ID
     */
    if (data_type_descriptor_ == UAVCAN_NULLPTR)
    {
        GlobalDataTypeRegistry::instance().freeze();
        //查找到我们定义的.uavcan名字
        data_type_descriptor_ = GlobalDataTypeRegistry::instance().find(DataTypeKindService, dtname);
        if (data_type_descriptor_ == UAVCAN_NULLPTR)
        {
            UAVCAN_TRACE("ServiceClient", "Type [%s] is not registered", dtname);
            return -ErrUnknownDataType;
        }
        UAVCAN_TRACE("ServiceClient", "Data type descriptor inited: %s", data_type_descriptor_->toString().c_str());
    }
    UAVCAN_ASSERT(data_type_descriptor_ != UAVCAN_NULLPTR);
    /*
     * 确定传输ID
     * Determining the Transfer ID
     */
    const OutgoingTransferRegistryKey otr_key(data_type_descriptor_->getID(),
                                              TransferTypeServiceRequest, server_node_id);
    const MonotonicTime otr_deadline = node.getMonotonicTime() + TransferSender::getDefaultMaxTransferInterval();
    TransferID* const otr_tid =
        node.getDispatcher().getOutgoingTransferRegistry().accessOrCreate(otr_key, otr_deadline);
    if (!otr_tid)
    {
        UAVCAN_TRACE("ServiceClient", "OTR access failure, dtd=%s", data_type_descriptor_->toString().c_str());
        return -ErrMemory;
    }
    out_call_id.transfer_id = *otr_tid;
    otr_tid->increment();

    return 0;
}

这个函数主要实现:1.判断服务节点是不是非广播,服务节点等于NODE ID吗,2.确定数据类型ID 3.确定传输ID



3.发布请求函数


    const int publisher_res = publisher_.publish(request, TransferTypeServiceRequest, server_node_id,
                                                 out_call_id.transfer_id);

在这里插入图片描述

template <typename DataSpec, typename DataStruct>
int GenericPublisher<DataSpec, DataStruct>::genericPublish(const DataStruct& message, TransferType transfer_type,
                                                           NodeID dst_node_id, TransferID* tid,
                                                           MonotonicTime blocking_deadline)
{
    const int res = checkInit();
    if (res < 0)
    {
        return res;
    }

    Buffer buffer;

    const int encode_res = doEncode(message, buffer);
    if (encode_res < 0)
    {
        return encode_res;
    }

    return GenericPublisherBase::genericPublish(buffer, transfer_type, dst_node_id, tid, blocking_deadline);
}
int GenericPublisherBase::genericPublish(const StaticTransferBufferImpl& buffer, TransferType transfer_type,
                                         NodeID dst_node_id, TransferID* tid, MonotonicTime blocking_deadline)
{
    /*
     * tid必须不等于0,我们执行if
     */
    if (tid)
    {
        return sender_.send(buffer.getRawPtr(), buffer.getMaxWritePos(), getTxDeadline(),
                            blocking_deadline, transfer_type, dst_node_id, *tid);
    }
    else
    {
        return sender_.send(buffer.getRawPtr(), buffer.getMaxWritePos(), getTxDeadline(),
                            blocking_deadline, transfer_type, dst_node_id);
    }
}

在这里插入图片描述

int TransferSender::send(const uint8_t* payload, unsigned payload_len, MonotonicTime tx_deadline,
                         MonotonicTime blocking_deadline, TransferType transfer_type, NodeID dst_node_id,
                         TransferID tid) const
{
    Frame frame(data_type_id_, transfer_type, dispatcher_.getNodeID(), dst_node_id, tid);

    frame.setPriority(priority_);
    frame.setStartOfTransfer(true);

    UAVCAN_TRACE("TransferSender", "%s", frame.toString().c_str());

    /*
     * Checking if we're allowed to send.
     * In passive mode we can send only anonymous transfers, if they are enabled.
     */
    if (dispatcher_.isPassiveMode())
    {
        const bool allow = allow_anonymous_transfers_ &&
                           (transfer_type == TransferTypeMessageBroadcast) &&
                           (int(payload_len) <= frame.getPayloadCapacity());
        if (!allow)
        {
            return -ErrPassiveMode;
        }
    }

    dispatcher_.getTransferPerfCounter().addTxTransfer();

    /*
     *发送结构---- Sending frames
     */
    if (frame.getPayloadCapacity() >= payload_len)           // Single Frame Transfer
    {
        const int res = frame.setPayload(payload, payload_len);
        if (res != int(payload_len))
        {
            UAVCAN_ASSERT(0);
            UAVCAN_TRACE("TransferSender", "Frame payload write failure, %i", res);
            registerError();
            return (res < 0) ? res : -ErrLogic;
        }

        frame.setEndOfTransfer(true);
        UAVCAN_ASSERT(frame.isStartOfTransfer() && frame.isEndOfTransfer() && !frame.getToggle());

        const CanIOFlags flags = frame.getSrcNodeID().isUnicast() ? flags_ : (flags_ | CanIOFlagAbortOnError);
        //发送出去
        return dispatcher_.send(frame, tx_deadline, blocking_deadline, qos_, flags, iface_mask_);
    }
    else                                                   // Multi Frame Transfer
    {
        UAVCAN_ASSERT(!dispatcher_.isPassiveMode());
        UAVCAN_ASSERT(frame.getSrcNodeID().isUnicast());

        int offset = 0;
        {
            TransferCRC crc = crc_base_;
            crc.add(payload, payload_len);

            static const int BUFLEN = sizeof(static_cast<CanFrame*>(0)->data);
            uint8_t buf[BUFLEN];

            buf[0] = uint8_t(crc.get() & 0xFFU);       // Transfer CRC, little endian
            buf[1] = uint8_t((crc.get() >> 8) & 0xFF);
            (void)copy(payload, payload + BUFLEN - 2, buf + 2);

            const int write_res = frame.setPayload(buf, BUFLEN);
            if (write_res < 2)
            {
                UAVCAN_TRACE("TransferSender", "Frame payload write failure, %i", write_res);
                registerError();
                return write_res;
            }
            offset = write_res - 2;
            UAVCAN_ASSERT(int(payload_len) > offset);
        }

        int num_sent = 0;

        while (true)
        {
            const int send_res = dispatcher_.send(frame, tx_deadline, blocking_deadline, qos_, flags_, iface_mask_);
            if (send_res < 0)
            {
                registerError();
                return send_res;
            }

            num_sent++;
            if (frame.isEndOfTransfer())
            {
                return num_sent;  // Number of frames transmitted
            }

            frame.setStartOfTransfer(false);
            frame.flipToggle();

            UAVCAN_ASSERT(offset >= 0);
            const int write_res = frame.setPayload(payload + offset, payload_len - unsigned(offset));
            if (write_res < 0)
            {
                UAVCAN_TRACE("TransferSender", "Frame payload write failure, %i", write_res);
                registerError();
                return write_res;
            }

            offset += write_res;
            UAVCAN_ASSERT(offset <= int(payload_len));
            if (offset >= int(payload_len))
            {
                frame.setEndOfTransfer(true);
            }
        }
    }

    UAVCAN_ASSERT(0);
    return -ErrLogic; // Return path analysis is apparently broken. There should be no warning, this 'return' is unreachable.
}

注意这个函数:dispatcher_.send(frame, tx_deadline, blocking_deadline, qos_, flags, iface_mask_);

int Dispatcher::send(const Frame& frame, MonotonicTime tx_deadline, MonotonicTime blocking_deadline,
                     CanTxQueue::Qos qos, CanIOFlags flags, uint8_t iface_mask)
{
    if (frame.getSrcNodeID() != getNodeID())
    {
        UAVCAN_ASSERT(0);
        return -ErrLogic;
    }

    CanFrame can_frame;
    //形成CAN协议数据格式类型
    if (!frame.compile(can_frame))
    {
        UAVCAN_TRACE("Dispatcher", "Unable to send: frame is malformed: %s", frame.toString().c_str());
        UAVCAN_ASSERT(0);
        return -ErrLogic;
    }
    //准备发送出去
    return canio_.send(can_frame, tx_deadline, blocking_deadline, iface_mask, qos, flags);
}

这里面有两个函数需要注意。

frame.compile(can_frame)

bool Frame::compile(CanFrame& out_can_frame) const
{
    if (!isValid())
    {
        UAVCAN_ASSERT(0);        // This is an application error, so we need to maximize it.
        return false;
    }

    /*
     *CAN ID字段------ CAN ID field
     */
     //获取CAN ID 
    out_can_frame.id = CanFrame::FlagEFF |
        bitpack<0, 7>(src_node_id_.get()) |
        bitpack<24, 5>(transfer_priority_.get());
       //判断数据帧的类型
    if (transfer_type_ == TransferTypeMessageBroadcast)
    {
        out_can_frame.id |=
            bitpack<7, 1>(0U) |
            bitpack<8, 16>(data_type_id_.get());
    }
    else
    {
        const bool request_not_response = transfer_type_ == TransferTypeServiceRequest;
        out_can_frame.id |=
            bitpack<7, 1>(1U) |
            bitpack<8, 7>(dst_node_id_.get()) |
            bitpack<15, 1>(request_not_response ? 1U : 0U) |
            bitpack<16, 8>(data_type_id_.get());
    }

    /*
     *有效帧----- Payload
     */
    uint8_t tail = transfer_id_.get(); //还记得刚开始说的这个tail吗
    if (start_of_transfer_)
    {
        tail |= (1U << 7);
    }
    if (end_of_transfer_)
    {
        tail |= (1U << 6);
    }
    if (toggle_)
    {
        tail |= (1U << 5);
    }

    UAVCAN_ASSERT(payload_len_ < sizeof(static_cast<CanFrame*>(UAVCAN_NULLPTR)->data));

    out_can_frame.dlc = static_cast<uint8_t>(payload_len_);
    (void)copy(payload_, payload_ + payload_len_, out_can_frame.data);

    out_can_frame.data[out_can_frame.dlc] = tail;
    out_can_frame.dlc++;

    /*
     * Discriminator鉴别器
     */
    if (src_node_id_.isBroadcast())
    {
        TransferCRC crc;
        crc.add(out_can_frame.data, out_can_frame.dlc); //value_ = uint16_t(uint16_t((value_ << 8) ^ Table[uint16_t((value_ >> 8) ^ byte) & 0xFFU]) & 0xFFFFU);
        out_can_frame.id |= bitpack<10, 14>(crc.get() & ((1U << 14) - 1U));
    }

    return true;
}

这个函数很重要,是形成CAN ID 字段的关键,如果前面没有设置对,在这里打印检测也可以定位问题。

canio_.send(can_frame, tx_deadline, blocking_deadline, iface_mask, qos, flags)
int CanIOManager::send(const CanFrame& frame, MonotonicTime tx_deadline, MonotonicTime blocking_deadline,
                       uint8_t iface_mask, CanTxQueue::Qos qos, CanIOFlags flags)
{
    const uint8_t num_ifaces = getNumIfaces();
    const uint8_t all_ifaces_mask = uint8_t((1U << num_ifaces) - 1);
    iface_mask &= all_ifaces_mask;

    if (blocking_deadline > tx_deadline)
    {
        blocking_deadline = tx_deadline;
    }

    int retval = 0;
    while (true)        // Somebody please refactor this.
    {
        if (iface_mask == 0)
        {
            break;
        }

        CanSelectMasks masks;
        masks.write = iface_mask | makePendingTxMask();
        {
            // Building the list of next pending frames per iface.
            // The driver will give them a scrutinizing look before deciding whether he wants to accept them.
            const CanFrame* pending_tx[MaxCanIfaces] = {};
            for (int i = 0; i < num_ifaces; i++)
            {
                CanTxQueue& q = *tx_queues_[i];
                if (iface_mask & (1 << i))      // I hate myself so much right now.
                {
                    pending_tx[i] = q.topPriorityHigherOrEqual(frame) ? q.getTopPriorityPendingFrame() : &frame;
                }
                else
                {
                    pending_tx[i] = q.getTopPriorityPendingFrame();
                }
            }
             //号召选择
            const int select_res = callSelect(masks, pending_tx, blocking_deadline);
            if (select_res < 0)
            {
                return -ErrDriver;
            }
            UAVCAN_ASSERT(masks.read == 0);
        }
        // Transmission
        for (uint8_t i = 0; i < num_ifaces; i++)
        {
            if (masks.write & (1 << i))
            {
                int res = 0;
                if (iface_mask & (1 << i))
                {
                    if (tx_queues_[i]->topPriorityHigherOrEqual(frame))
                    {
                        res = sendFromTxQueue(i);                 // May return 0 if nothing to transmit (e.g. expired)
                    }
                    if (res <= 0)
                    {
                    //重要函数
                        res = sendToIface(i, frame, tx_deadline, flags);
                        if (res > 0)
                        {
                            iface_mask &= uint8_t(~(1 << i));     // Mark transmitted
                        }
                    }
                }
                else
                {
                    res = sendFromTxQueue(i);
                }
                if (res > 0)
                {
                    retval++;
                }
            }
        }
        // Timeout. Enqueue the frame if wasn't transmitted and leave.
        const bool timed_out = sysclock_.getMonotonic() >= blocking_deadline;
        if (masks.write == 0 || timed_out)
        {
            if (!timed_out)
            {
                UAVCAN_TRACE("CanIOManager", "Send: Premature timeout in select(), will try again");
                continue;
            }
            for (uint8_t i = 0; i < num_ifaces; i++)
            {
                if (iface_mask & (1 << i))
                {
                    tx_queues_[i]->push(frame, tx_deadline, qos, flags);
                }
            }
            break;
        }
    }
    return retval;
}

核心底层接口函数sendToIface(i, frame, tx_deadline, flags);

int CanIOManager::sendToIface(uint8_t iface_index, const CanFrame& frame, MonotonicTime tx_deadline, CanIOFlags flags)
{
    UAVCAN_ASSERT(iface_index < MaxCanIfaces);
    ICanIface* const iface = driver_.getIface(iface_index);
    if (iface == UAVCAN_NULLPTR)
    {
        UAVCAN_ASSERT(0);   // Nonexistent interface
        return -ErrLogic;
    }
    const int res = iface->send(frame, tx_deadline, flags);
    if (res != 1)
    {
        UAVCAN_TRACE("CanIOManager", "Send failed: code %i, iface %i, frame %s",
                     res, iface_index, frame.toString().c_str());
    }
    if (res > 0)
    {
        counters_[iface_index].frames_tx += unsigned(res);
    }
    return res;
}
uavcan::int16_t CanIface::send(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline,
                               uavcan::CanIOFlags flags)
{
    if (frame.isErrorFrame() || frame.dlc > 8)
    {
        return -ErrUnsupportedFrame;
    }

    /*
     * Normally we should perform the same check as in @ref canAcceptNewTxFrame(), because
     * it is possible that the highest-priority frame between select() and send() could have been
     * replaced with a lower priority one due to TX timeout. But we don't do this check because:
     *
     *  - It is a highly unlikely scenario.
     *
     *  - Frames do not timeout on a properly functioning bus. Since frames do not timeout, the new
     *    frame can only have higher priority, which doesn't break the logic.
     *
     *  - If high-priority frames are timing out in the TX queue, there's probably a lot of other
     *    issues to take care of before this one becomes relevant.
     *
     *  - It takes CPU time. Not just CPU time, but critical section time, which is expensive.
     */
    CriticalSectionLocker lock;

    /*
     * Seeking for an empty slot
     */
    uavcan::uint8_t txmailbox = 0xFF;
    if ((can_->TSR & bxcan::TSR_TME0) == bxcan::TSR_TME0)
    {
        txmailbox = 0;
    }
    else if ((can_->TSR & bxcan::TSR_TME1) == bxcan::TSR_TME1)
    {
        txmailbox = 1;
    }
    else if ((can_->TSR & bxcan::TSR_TME2) == bxcan::TSR_TME2)
    {
        txmailbox = 2;
    }
    else
    {
        return 0;       // No transmission for you.
    }

    peak_tx_mailbox_index_ = uavcan::max(peak_tx_mailbox_index_, txmailbox);    // Statistics

    /*
     * Setting up the mailbox
     */
    bxcan::TxMailboxType& mb = can_->TxMailbox[txmailbox];
    //最终把数据合并,传递到发送邮箱寄存器
    if (frame.isExtended())
    {
       //注意这个寄存器地址要王右移3位
        mb.TIR = ((frame.id & uavcan::CanFrame::MaskExtID) << 3) | bxcan::TIR_IDE;

    }
    else
    {
        mb.TIR = ((frame.id & uavcan::CanFrame::MaskStdID) << 21);
    }

    if (frame.isRemoteTransmissionRequest())
    {
        mb.TIR |= bxcan::TIR_RTR;
    }
    mb.TDTR = frame.dlc;

    mb.TDHR = (uavcan::uint32_t(frame.data[7]) << 24) |
              (uavcan::uint32_t(frame.data[6]) << 16) |
              (uavcan::uint32_t(frame.data[5]) << 8)  |
              (uavcan::uint32_t(frame.data[4]) << 0);
    mb.TDLR = (uavcan::uint32_t(frame.data[3]) << 24) |
              (uavcan::uint32_t(frame.data[2]) << 16) |
              (uavcan::uint32_t(frame.data[1]) << 8)  |
              (uavcan::uint32_t(frame.data[0]) << 0);

    mb.TIR |= bxcan::TIR_TXRQ;  // Go.

    /*
     * Registering the pending transmission so we can track its deadline and loopback it as needed
     */
    TxItem& txi = pending_tx_[txmailbox];
    txi.deadline       = tx_deadline;
    txi.frame          = frame;
    txi.loopback       = (flags & uavcan::CanIOFlagLoopback) != 0;
    txi.abort_on_error = (flags & uavcan::CanIOFlagAbortOnError) != 0;
    txi.pending        = true;
    return 1;
}

在这里插入图片描述

到这里基本实现了把服务帧数据发送出去。


3.CAN调试方法


所谓工欲善其事必先利其器,我们最好买个USB-CAN 模块方便实时监控我们发的和收到的数据是否正常,接线方式可以见下面的方式。

在这里插入图片描述

在这里插入图片描述

标签:node,Ardupilot,return,res,frame,学习,UAVCAN,id
来源: https://blog.csdn.net/lixiaoweimashixiao/article/details/106575152