PX4FMU和PX4IO最底层启动过程分析(下)

PX4FMU和PX4IO最底层启动过程分析(下)

PX4FMU的系统启动函数为nash_main(int argc,char *argv[])
PX4IO的系统启动函数为nash_start(int argc,char *argv[])

PX4FMU启动函数nash_main(int argc,char *argv[])

首先分析一下nash_main(int argc,char *argv[])
PX4FMU中有#define CONFIG_USER_ENTRYPOINT nsh_main

int nsh_main(int argc, char *argv[])
{int exitval = 0;int ret;/* Call all C++ static constructors */#if defined(CONFIG_HAVE_CXX) && defined(CONFIG_HAVE_CXXINITIALIZE)up_cxxinitialize();
#endif/* Make sure that we are using our symbol take */#if defined(CONFIG_LIBC_EXECFUNCS) && defined(CONFIG_EXECFUNCS_SYMTAB)exec_setsymtab(CONFIG_EXECFUNCS_SYMTAB, 0);
#endif/* Register the BINFS file system */#if defined(CONFIG_FS_BINFS) && (CONFIG_BUILTIN)ret = builtin_initialize();if (ret < 0){fprintf(stderr, "ERROR: builtin_initialize failed: %d\n", ret);exitval = 1;}
#endif/* Initialize the NSH library */nsh_initialize();/* If the Telnet console is selected as a front-end, then start the* Telnet daemon.*/#ifdef CONFIG_NSH_TELNETret = nsh_telnetstart();if (ret < 0){/* The daemon is NOT running.  Report the the error then fail...* either with the serial console up or just exiting.*/fprintf(stderr, "ERROR: Failed to start TELNET daemon: %d\n", ret);exitval = 1;}
#endif/* If the serial console front end is selected, then run it on this thread */#ifdef CONFIG_NSH_CONSOLEret = nsh_consolemain(0, NULL);/* nsh_consolemain() should not return.  So if we get here, something* is wrong.*/fprintf(stderr, "ERROR: nsh_consolemain() returned: %d\n", ret);exitval = 1;
#endifreturn exitval;
}

其中包含

#ifdef CONFIG_NSH_CONSOLEret = nsh_consolemain(0, NULL);

进入nsh_consolemain(int argc, char *argv[])函数

int nsh_consolemain(int argc, char *argv[])
{FAR struct console_stdio_s *pstate = nsh_newconsole();int ret;DEBUGASSERT(pstate);/* Execute the start-up script */#ifdef CONFIG_NSH_ROMFSETC(void)nsh_initscript(&pstate->cn_vtbl);
#endif/* Initialize any USB tracing options that were requested */#ifdef CONFIG_NSH_USBDEV_TRACEusbtrace_enable(TRACE_BITSET);
#endif/* Execute the session */ret = nsh_session(pstate);/* Exit upon return */nsh_exit(&pstate->cn_vtbl, ret);return ret;
}

其中包含

/* Execute the start-up script */#ifdef CONFIG_NSH_ROMFSETC(void)nsh_initscript(&pstate->cn_vtbl);
#endif

执行启动脚本也就是rcS,接下来根据本身版本分别看ardupilot和PX4原生码

/* Execute the session */ret = nsh_session(pstate);

执行用户程序
跟踪pstate

FAR struct console_stdio_s *pstate = nsh_newconsole();

进入console_stdio_s *nsh_newconsole(void)

FAR struct console_stdio_s *nsh_newconsole(void)
{struct console_stdio_s *pstate = (struct console_stdio_s *)zalloc(sizeof(struct console_stdio_s));if (pstate){/* Initialize the call table */#ifndef CONFIG_NSH_DISABLEBGpstate->cn_vtbl.clone      = nsh_consoleclone;pstate->cn_vtbl.release    = nsh_consolerelease;
#endifpstate->cn_vtbl.write      = nsh_consolewrite;pstate->cn_vtbl.output     = nsh_consoleoutput;pstate->cn_vtbl.linebuffer = nsh_consolelinebuffer;pstate->cn_vtbl.redirect   = nsh_consoleredirect;pstate->cn_vtbl.undirect   = nsh_consoleundirect;pstate->cn_vtbl.exit       = nsh_consoleexit;/* (Re-) open the console input device */#ifdef CONFIG_NSH_CONDEVpstate->cn_confd           = open(CONFIG_NSH_CONDEV, O_RDWR);if (pstate->cn_confd < 0){free(pstate);return NULL;}/* Create a standard C stream on the console device */pstate->cn_constream = fdopen(pstate->cn_confd, "r+");if (!pstate->cn_constream){close(pstate->cn_confd);free(pstate);return NULL;}
#endif/* Initialize the output stream */pstate->cn_outfd           = OUTFD(pstate);pstate->cn_outstream       = OUTSTREAM(pstate);}return pstate;
}

应该是用户在console输入新的nsh命令吧


PX4IO启动函数nash_start(int argc,char *argv[])

接着分析一下nash_start(int argc,char *argv[])
PX4IO中有#define CONFIG_USER_ENTRYPOINT user_start

int user_start(int argc, char *argv[])
{/* configure the first 8 PWM outputs (i.e. all of them) */up_pwm_servo_init(0xff);/* run C++ ctors before we go any further */up_cxxinitialize();/* reset all to zero */memset(&system_state, 0, sizeof(system_state));/* configure the high-resolution time/callout interface */hrt_init();/* calculate our fw CRC so FMU can decide if we need to update */calculate_fw_crc();/** Poll at 1ms intervals for received bytes that have not triggered* a DMA event.*/
#ifdef CONFIG_ARCH_DMAhrt_call_every(&serial_dma_call, 1000, 1000, (hrt_callout)stm32_serial_dma_poll, NULL);
#endif/* print some startup info */lowsyslog("\nPX4IO: starting\n");/* default all the LEDs to off while we start */LED_AMBER(false);LED_BLUE(false);LED_SAFETY(false);
#ifdef GPIO_LED4LED_RING(false);
#endif/* turn on servo power (if supported) */
#ifdef POWER_SERVOPOWER_SERVO(true);
#endif/* turn off S.Bus out (if supported) */
#ifdef ENABLE_SBUS_OUTENABLE_SBUS_OUT(false);
#endif/* start the safety switch handler */safety_init();/* initialise the control inputs */controls_init();/* set up the ADC */adc_init();/* start the FMU interface */interface_init();/* add a performance counter for mixing */perf_counter_t mixer_perf = perf_alloc(PC_ELAPSED, "mix");/* add a performance counter for controls */perf_counter_t controls_perf = perf_alloc(PC_ELAPSED, "controls");/* and one for measuring the loop rate */perf_counter_t loop_perf = perf_alloc(PC_INTERVAL, "loop");struct mallinfo minfo = mallinfo();lowsyslog("MEM: free %u, largest %u\n", minfo.mxordblk, minfo.fordblks);/* initialize PWM limit lib */pwm_limit_init(&pwm_limit);/**    P O L I C E    L I G H T S** Not enough memory, lock down.** We might need to allocate mixers later, and this will* ensure that a developer doing a change will notice* that he just burned the remaining RAM with static* allocations. We don't want him to be able to* get past that point. This needs to be clearly* documented in the dev guide.**/if (minfo.mxordblk < 600) {lowsyslog("ERR: not enough MEM");bool phase = false;while (true) {if (phase) {LED_AMBER(true);LED_BLUE(false);} else {LED_AMBER(false);LED_BLUE(true);}up_udelay(250000);phase = !phase;}}/* Start the failsafe led init */failsafe_led_init();/** Run everything in a tight loop.*/uint64_t last_debug_time = 0;uint64_t last_heartbeat_time = 0;for (;;) {/* track the rate at which the loop is running */perf_count(loop_perf);/* kick the mixer */perf_begin(mixer_perf);mixer_tick();perf_end(mixer_perf);/* kick the control inputs */perf_begin(controls_perf);controls_tick();perf_end(controls_perf);if ((hrt_absolute_time() - last_heartbeat_time) > 250 * 1000) {last_heartbeat_time = hrt_absolute_time();heartbeat_blink();}ring_blink();check_reboot();/* check for debug activity (default: none) */show_debug_messages();/* post debug state at ~1Hz - this is via an auxiliary serial port* DEFAULTS TO OFF!*/if (hrt_absolute_time() - last_debug_time > (1000 * 1000)) {isr_debug(1, "d:%u s=0x%x a=0x%x f=0x%x m=%u",(unsigned)r_page_setup[PX4IO_P_SETUP_SET_DEBUG],(unsigned)r_status_flags,(unsigned)r_setup_arming,(unsigned)r_setup_features,(unsigned)mallinfo().mxordblk);last_debug_time = hrt_absolute_time();}}
}

user_start 负责px4io 基础环境的初始化,包括PWM,串口,ADC 等资源的初始化,最后运行一个死循环,用于处理遥控器输入,与PX4FMU 通讯的内容
controls_tick 负责处理遥控器的输入内容,包括SBUS 的处理sbus_input、 SPKT/DSM 的处理dsm_port_input、 PPM 的处理ppm_input

PX4IO 底层中断处理的内容以下图
在这里插入图片描述

(1)紫色为PX4IO 的底层串口IO 操做,流程为当PX4IO 收到PX4FMU 的串口数据后会运行serial_interrupt, serial_interrupt 负责收发DMA 的操做,若是收到一个完整的包,则调用rx_dma_callback 进行处理, rx_dma_callback 首先调用rx_handle_packet 解析包中的内容,判断为写寄存器仍是读寄存器,处理完成后由rx_dma_callback 发送回包给PX4FMU

static int
serial_interrupt(int irq, void *context)
{static bool abort_on_idle = false;uint32_t sr = rSR;	/* get UART status register */(void)rDR;		/* required to clear any of the interrupt status that brought us here */if (sr & (USART_SR_ORE |	/* overrun error - packet was too big for DMA or DMA was too slow */USART_SR_NE |		/* noise error - we have lost a byte due to noise */USART_SR_FE)) {		/* framing error - start/stop bit lost or line break */perf_count(pc_errors);if (sr & USART_SR_ORE) {perf_count(pc_ore);}if (sr & USART_SR_NE) {perf_count(pc_ne);}if (sr & USART_SR_FE) {perf_count(pc_fe);}/* send a line break - this will abort transmission/reception on the other end */rCR1 |= USART_CR1_SBK;/* when the line goes idle, abort rather than look at the packet */abort_on_idle = true;}if (sr & USART_SR_IDLE) {/** If we saw an error, don't bother looking at the packet - it should have* been aborted by the sender and will definitely be bad. Get the DMA reconfigured* ready for their retry.*/if (abort_on_idle) {abort_on_idle = false;dma_reset();return 0;}/** The sender has stopped sending - this is probably the end of a packet.* Check the received length against the length in the header to see if* we have something that looks like a packet.*/unsigned length = sizeof(dma_packet) - stm32_dmaresidual(rx_dma);if ((length < 1) || (length < PKT_SIZE(dma_packet))) {/* it was too short - possibly truncated */perf_count(pc_badidle);dma_reset();return 0;}/** Looks like we received a packet. Stop the DMA and go process the* packet.*/perf_count(pc_idle);stm32_dmastop(rx_dma);rx_dma_callback(rx_dma, DMA_STATUS_TCIF, NULL);}return 0;
}
static void
rx_dma_callback(DMA_HANDLE handle, uint8_t status, void *arg)
{/** We are here because DMA completed, or UART reception stopped and* we think we have a packet in the buffer.*/perf_begin(pc_txns);/* disable UART DMA */rCR3 &= ~(USART_CR3_DMAT | USART_CR3_DMAR);/* handle the received packet */rx_handle_packet();/* re-set DMA for reception first, so we are ready to receive before we start sending */dma_reset();/* send the reply to the just-processed request */dma_packet.crc = 0;dma_packet.crc = crc_packet(&dma_packet);stm32_dmasetup(tx_dma,(uint32_t)&rDR,(uint32_t)&dma_packet,PKT_SIZE(dma_packet),DMA_CCR_DIR		|DMA_CCR_MINC		|DMA_CCR_PSIZE_8BITS	|DMA_CCR_MSIZE_8BITS);stm32_dmastart(tx_dma, NULL, NULL, false);rCR3 |= USART_CR3_DMAT;perf_end(pc_txns);
}
static void
rx_handle_packet(void)
{/* check packet CRC */uint8_t crc = dma_packet.crc;dma_packet.crc = 0;if (crc != crc_packet(&dma_packet)) {perf_count(pc_crcerr);/* send a CRC error reply */dma_packet.count_code = PKT_CODE_CORRUPT;dma_packet.page = 0xff;dma_packet.offset = 0xff;return;}if (PKT_CODE(dma_packet) == PKT_CODE_WRITE) {/* it's a blind write - pass it on */if (registers_set(dma_packet.page, dma_packet.offset, &dma_packet.regs[0], PKT_COUNT(dma_packet))) {perf_count(pc_regerr);dma_packet.count_code = PKT_CODE_ERROR;} else {dma_packet.count_code = PKT_CODE_SUCCESS;}return;}if (PKT_CODE(dma_packet) == PKT_CODE_READ) {/* it's a read - get register pointer for reply */unsigned count;uint16_t *registers;if (registers_get(dma_packet.page, dma_packet.offset, &registers, &count) < 0) {perf_count(pc_regerr);dma_packet.count_code = PKT_CODE_ERROR;} else {/* constrain reply to requested size */if (count > PKT_MAX_REGS) {count = PKT_MAX_REGS;}if (count > PKT_COUNT(dma_packet)) {count = PKT_COUNT(dma_packet);}/* copy reply registers into DMA buffer */memcpy((void *)&dma_packet.regs[0], registers, count * 2);dma_packet.count_code = count | PKT_CODE_SUCCESS;}return;}/* send a bad-packet error reply */dma_packet.count_code = PKT_CODE_CORRUPT;dma_packet.page = 0xff;dma_packet.offset = 0xfe;
}

(2) 蓝色为包操做,只提供registers_set 写操做和registers_get 读操做
(3)IOPacket 为协议包,包括如下几部分

定义描述
count_code标记包的读写,错误,长度等信息
crc为包的效验码
page为数据页
offset为数据偏移量
regs为数据内容

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2808231.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

五大方法教你如何分分钟构造百万测试数据!

在测试的工作过程中&#xff0c;很多场景是需要构造一些数据在项目里的&#xff0c;方便测试工作的进行&#xff0c;构造的方法有很多&#xff0c;难度和技术深度也不一样。本文提供方法供你选择。 在测试的工作过程中&#xff0c;很多场景是需要构造一些数据在项目里的&#…

【《高性能 MySQL》摘录】第 2 章 MySQL 基准测试

文章目录 2.1 为什么需要基准测试2.2 基准测试的策略2.2.1 测试何种指标 2.3 基准测试方法2.3.1 设计和规划基准测试2.3.2 基准测试应该运行多长时间2.3.3 获取系统性能和状态2.3.4 获得准确的测试结果2.3.5 运行基准测试并分析结果2.3.6 绘图的重要性 2.4 基准测试工具…

vue3 实现 el-pagination页面分页组件的封装以及调用

示例图 一、组件代码 <template><el-config-provider :locale"zhCn"><el-pagination background class"lj-paging" layout"prev, pager, next, jumper" :pager-count"5" :total"total":current-page"p…

深入浅出JVM(十)之字节码指令(下篇)

上篇文章深入浅出JVM&#xff08;九&#xff09;之字节码指令&#xff08;上篇&#xff09;已经深入浅出说明加载存储、算术、类型转换的字节码指令&#xff0c;本篇文章作为字节码的指令的下篇&#xff0c;深入浅出的解析各种类型字节码指令&#xff0c;如&#xff1a;方法调用…

计算机网络:思科实验【2-MAC地址、IP地址、ARP协议及总线型以太网的特性】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;Cisco Packet Tracer实验 本文对应的实验报告源文件请关注微信公众号程序员刘同学&#xff0c;回复思科获取下载链接。 实验目的实验环境实验内容MAC地址、IP地址、ARP协议总线型以太网的…

2024-02-25 Unity 编辑器开发之编辑器拓展7 —— Inspector 窗口拓展

文章目录 1 SerializedObject 和 SerializedProperty2 自定义显示步骤3 数组、List 自定义显示3.1 基础方式3.2 自定义方式 4 自定义属性自定义显示4.1 基础方式4.2 自定义方式 5 字典自定义显示5.1 SerizlizeField5.2 ISerializationCallbackReceiver5.3 代码示例 1 Serialize…

音频smmu问题之smmu学习

一、音频smmu 内存访问问题 在工作中&#xff0c;遇到一个smmu问题&#xff0c;主要log信息如下&#xff1a; arm-smmu 15000000.apps-smmu: Unhandled arm-smmu context fault from soc:spf_core_platform:qcom,msm-audio-ion! arm-smmu 15000000.apps-smmu: FAR 0x0000000…

【考研数学】基础阶段习题1800和660怎么选❓

我建议以1800题为主 1800题包含基础和强化两部分&#xff0c;基础部分题量很大&#xff0c;类型也很全面&#xff0c;并且难度一点也不高&#xff0c;适合基础不好的学生来做。 660题难度比较大&#xff0c;不适合基础阶段做。 660题虽然名字叫基础训练&#xff0c;但是不适…

超详细!彻底说明白Redis持久化

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 文章目录 Redis持久化方式RDBfork 函数与写时复制RDB 相关配置 AOFAOF 文件解读AOF 的写入与同步AOF 重写AOF重写的实现AOF 重写面临的问题AOF重写缓存区 AOF相关配置AOF …

车载电子电器架构 —— OEM基础技术概念开发流程

车载电子电器架构 —— 基础技术概念开发 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗…

Spring Cloud Gateway官方文档学习

文章目录 推荐写在前面一、熟悉Gateway基本概念与原理1、三大概念2、工作流程 二、基本使用路由断言的两种写法 三、路由断言工厂1、After路由断言工厂2、Before路由断言工厂3、Between路由断言工厂4、Cookie路由断言工厂5、Header路由断言工厂6、Host路由断言工厂7、Method路由…

《插入排序》与《选择排序》

目录 前言&#xff1a; 排序的概念&#xff1a; 插入排序&#xff1a; 1.直接插入排序&#xff1a; 2.希尔排序( 缩小增量排序 )&#xff1a; 选择排序&#xff1a; 1.直接选择排序: 2.快速排序&#xff1a; hore思想&#xff1a; 挖坑法&#xff1a; 双指针法&#…

【风格迁移】CAST:对比学习,从图像特征而非其二阶统计量(Gram矩阵)中学习风格

CAST&#xff1a;对比学习&#xff0c;从图像特征而非其二阶统计量&#xff08;Gram矩阵&#xff09;中学习风格 提出背景5 why 分析5 so分析 CAST 框架多层风格投影器领域增强模块生成网络 效果对比 StyleGAN 提出背景 论文&#xff1a;https://arxiv.org/pdf/2205.09542.pdf…

如何使用移动端设备在公网环境远程访问本地黑群晖

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…

linux常用的网络命令实战分享

文章目录 ifup/down命令ifconfig命令观察网络接口信息修改接口参数增加虚拟网络接口 route命令查看路由表增加路由表规则删除路由表规则 IP 命令ip linkip addr设定路由 ip route arp 命令 在实际研发运维工作中常常会涉及到网关相关的操作和知识&#xff0c;这里对linux下常用…

电脑msvcp100.dll丢失了怎么办?msvcp100.dll丢失的5种解决方法

当计算机系统中无法找到msvcp100.dll文件&#xff0c;或者遭遇msvcp100.dll丢失的情况时&#xff0c;可能会引发一系列运行问题和功能障碍。msvcp100.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;这是一个至关重要的动态链接库文件&#xff0c;对于许…

LeetCode第二题: 两数相加

文章目录 题目描述示例 解题思路 - 迭代法Go语言实现 - 迭代法算法分析 解题思路 - 模拟法Go语言实现 - 模拟法算法分析 解题思路 - 优化模拟法主要方法其他方法的考虑 ‍ 题目描述 给出两个非空的链表用来表示两个非负的整数。其中&#xff0c;它们各自的位数是按照逆序的方…

Spring Boot利用Kaptcha生成验证码

生成验证码 我们在登录或注册某个网站的时候&#xff0c;会需要我们输入验证码&#xff0c;才能登录注册&#xff0c;那么如何生成验证码呢&#xff1f;其实&#xff0c;生成验证码我们可以用Java Swing在后台内存里的区域画一个出来&#xff0c;但是非常麻烦&#xff0c;所以…

【JavaEE】_HttpServlet类

目录 1. init方法 2. destory方法 3. service方法 4. servlet生命周期 前文已经提及到&#xff1a;servlet是tomcat提供的&#xff0c;用于操作HTTP协议的一组API&#xff0c;可以将这组API理解为HTTP服务器的框架&#xff1b; 编写一个servlet程序&#xff0c;往往都要继…

基于Java SSM框架实现音乐播放器管理系统项目【项目源码+论文说明】计算机毕业设计

ssm音乐播放器管理系统演示录像2020 摘要 随着社会的发展&#xff0c;计算机的优势和普及使得音乐播放器管理系统的开发成为必需。音乐播放器管理系统主要是借助计算机&#xff0c;通过对首页、音乐推荐、付费音乐、论坛信息、个人中心、后台管理等信息进行管理。减少管理员的…