U-Boot启动流程详解

参考:U-Boot顶层目录链接脚本文件(u-boot.lds)介绍
作者:一只青木呀
发布时间: 2020-10-23 13:52:23
网址:https://blog.csdn.net/weixin_45309916/article/details/109240625

目录

  • 链接脚本 u-boot.lds 详解
    • 1、u-boot.lds文件
    • 2、arch/arm/lib/vectors.S 文件
    • 3、u-boot.map(地址映射文件)
    • 4、链接文件分析
    • 总结
  • U-Boot 启动流程详解
    • reset 函数源码详解
    • lowlevel_init 函数详解
    • s_init 函数详解
    • _main 函数详解
    • board_init_f 函数详解(初始化外设、uboot重定位给Linux腾空间)
    • relocate_code 函数详解
    • relocate_vectors 函数详解
    • board_init_r 函数详解
    • run_main_loop 函数详解
    • cli_loop 函数详解
    • cmd_process 函数详解
  • bootz 启动 Linux 内核过程
    • images 全局变量
    • do_bootz 函数(bootz启动内核命令的执行函数)
    • bootz_start 函数
    • do_bootm_states 函数
    • bootm_os_get_boot_func 函数
    • do_bootm_linux 函数

上一章我们详细的分析了 uboot 的顶层 Makefile,理清了 uboot 的编译流程。本章我们来详细的分析一下 uboot 的启动流程,理清 uboot 是如何启动的。通过对 uboot 启动流程的梳理,我们就可以掌握 ①外设是在哪里被初始化的,这样当我们需要修改这些外设驱动的时候就会心里有数。另外,通过分析 uboot 的启动流程可以 ②了解 Linux 内核是如何被启动的

链接脚本 u-boot.lds 详解

要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本(链接脚本是编译生成的)来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过 uboot 的话链接脚本为 arch/arm/cpu/u-boot.lds。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds
文件,如下图所示:
在这里插入图片描述
只有编译 u-boot 以后才会在根目录下出现 u-boot.lds 文件!

1、u-boot.lds文件

打开 u-boot.lds,内容如下:
在这里插入图片描述
在这里插入图片描述
第 3 行为代码当前入口点: _start, _start 在文件 arch/arm/lib/vectors.S 中有定义,如图下所示:

2、arch/arm/lib/vectors.S 文件

在这里插入图片描述
从上图可以看出, _start 后面就是中断向量表,从图中的“.section “.vectors”, "ax”可以得到,此代码存放在.vectors 段里面。

3、u-boot.map(地址映射文件)

使用如下命令在uboot 中查找“__image_copy_start”:(上图第一节第十行代码处)

grep -nR "__image_copy_start"

搜索结果如图32.1.3 所示:

在这里插入图片描述

打开 u-boot.map(地址映射文件):
在这里插入图片描述
u-boot.map 是 uboot 的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址,从上图932 行可以看到 __image_copy_start 为 0X87800000,而.text 的起始地址也是0X87800000

4、链接文件分析

.text(代码段)描述
*(.__image_copy_start)uboot 拷贝的首地址

在链接文件中第 10 行*(._image_copystart) 在映射文件中可以看到地址为 0X87800000,而.text 的起始地址也是0X87800000。

在链接文件中第 11 行是 vectors 段, vectors 段保存中断向量表(裸机部分讲过),从u-boot.lds文件我们知道了 vectors.S 的代码是存在 vectors 段中的。从地址映射文件中, vectors 段的起始地址也是 0X87800000,说明整个 uboot 的起始地址就是 0X87800000,这也是为什么我们裸机例程的链接起始地址选择0X87800000 了,目的就是为了和uboot 一致。

在链接文件中第 12 行将 arch/arm/cpu/armv7/start.s 编译出来的.o代码放到中断向量表后面(参照上图u-boot.map的代码)。

在链接文件中第 13 行为 text 段,其他的代码段就放到这里

在链接文件中第 16 行 .rodata只读数据段(一般存放常量)

在链接文件中第 18 行,数据段 (一般存放已初始化的全局和静态变量)

在链接文件中第 24 行 ,.u_boot_list段

在链接文件中第 28 行, .image_copy_end:uboot 拷贝的结束地址

在链接文件中第 32 行,.rel_dyn_start:.rel.dyn 段起始地址

在链接文件中第 39 行,.rel_dyn_end:.rel_dyn段结束地址

在链接文件中第 52 行,.bss_start:.bss 段起始地址(静态数据区,一般存放未初始化的全局和静态变量)

在链接文件中第 61 行,.bss_end:.bss段结束

总结

在u-boot.lds 中有一些跟地址有关的“变量”需要我们注意一下,后面分析u-boot 源码的时候会用到,这些变量要最终编译完成才能确定的!!!比如我编译完成以后这些“变量”的值如表所示:

变量数值描述
*(.vectors)0x87800000中断向量表
arch/arm/cpu/armv7/start.o0x87800300strrt.c
__image_copy_start0x87800000uboot 拷贝的首地址
__image_copy_end0x8785dd54uboot 拷贝的结束地址
__rel_dyn_start0x8785dd54.rel.dyn 段起始地址
__rel_dyn_end0x878668f4.rel.dyn 段结束地址
_image_binary_end0x878668f4镜像结束地址
__bss_start0x8785dd54.bss 段起始地址
__bss_end0x878a8e74.bss 段结束地址

上表中的“变量”值可以在 u-boot.map 文件中查找,上表中除了__image_copy_start以外,其他的变量值每次编译的时候可能会变化,如果修改了 uboot 代码、修改了 uboot 配置、选用不同的优化等级等等都会影响到这些值。所以,一切以实际值为准。

U-Boot 启动流程详解

reset 函数源码详解

从u-boot.lds 中我们已经知道了入口点是arch/arm/lib/vectors.S 文件中的_start,代码如下:

在这里插入图片描述
第48 行_start 开始的是中断向量表,其中54~61 行就是中断向量表,和我们裸机例程里面一样。54 行跳转到reset 函数里面,reset 函数在arch/arm/cpu/armv7/start.S 里面,代码如下:
在这里插入图片描述
第35 行就是reset 函数。
第37 行从reset 函数跳转到了save_boot_params 函数,而save_boot_params 函数同样定义在start.S 里面,定义如下:

在这里插入图片描述
save_boot_params 函数也是只有一句跳转语句,跳转到save_boot_params_ret 函数(为啥不直接跳转到呢,搞得这么麻烦),
save_boot_params_ret 函数代码如下:

在这里插入图片描述

第43 行,读取寄存器cpsr(程序状态寄存器,前面架构部分讲过) 中的值,并保存到r0 寄存器中。

第44 行,将寄存器r0 中的值与0X1F 进行与运算,结果保存到r1 寄存器中,目的就是提取cpsr 的bit0~bit4 这5 位,这5 位为M4 M3 M2 M1 M0,M[4:0]这五位用来设置处理器的工作模式,如表32.2.1.1 所示:

在这里插入图片描述

第45 行,判断r1 寄存器的值是否等于0X1A(0b11010),也就是判断当前处理器模式是否处于Hyp 模式。

第46 行,如果r1 和0X1A 不相等,也就是CPU 不处于Hyp 模式的话就将r0 寄存器的bit0~5 进行清零,其实就是清除模式位。

第47 行,如果处理器不处于Hyp 模式的话就将r0 的寄存器的值与0x13 进行或运算,0x13=0b10011,也就是设置处理器进入SVC 模式。

第48 行,r0 寄存器的值再与0xC0 进行或运算,那么r0 寄存器此时的值就是0xD3,cpsr的I 为和F 位分别控制IRQ 和FIQ 这两个中断的开关,设置为1 就关闭了FIQ 和IRQ!

第49 行,将r0 寄存器写回到cpsr 寄存器中。完成设置CPU 处于SVC32 模式,并且关闭FIQ 和IRQ 这两个中断。

继续执行执行下面的代码:

在这里插入图片描述
第56 行,如果没有定义CONFIG_OMAP44XX 和CONFIG_SPL_BUILD 的话条件成立,此处条件成立。

第58 行读取CP15 中c1 寄存器的值到r0 寄存器中,根据17.1.4 小节可知,这里是读取SCTLR 寄存器的值。

第59 行,CR_V 在arch/arm/include/asm/system.h 中有如下所示定义:

#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */

因此这一行的目的就是清除SCTLR 寄存器中的bit13,SCTLR 寄存器结构如图32.2.1.1 所示:

在这里插入图片描述

从图32.2.1.1 可以看出,bit13 为V 位,此位是向量表控制位,当为0 的时候向量表基地址为0X00000000,软件可以重定位向量表。为1 的时候向量表基地址为0XFFFF0000,软件不能重定位向量表。这里将V 清零目的就是为了接下来的向量表重定位,这个我们在第十七章有过详细的介绍了。

第60 行将r0 寄存器的值重写写入到寄存器SCTLR 中。

第63 行设置r0 寄存器的值为_start,_start 就是整个uboot 的入口地址,其值为0X87800000,相当于uboot 的起始地址,因此0x87800000 也是向量表的起始地址。

第64 行将r0 寄存器的值(向量表值)写入到CP15 的c12 寄存器中,也就是VBAR 寄存器。因此第58~64 行就是设置向量表重定位的。

代码继续往下执行:

在这里插入图片描述
第68 行如果没有定义CONFIG_SKIP_LOWLEVEL_INIT 的话条件成立。我们没有定义CONFIG_SKIP_LOWLEVEL_INIT,因此条件成立,执行下面的语句。

示例代码32.2.1.6 中的内容比较简单,就是分别调用函数cpu_init_cp15、cpu_init_crit 和_main。

函数cpu_init_cp15 用来设置CP15 相关的内容,比如关闭MMU 啥的,此函数同样在start.S文件中定义的,代码如下:

在这里插入图片描述
函数cpu_init_cp15 都是一些和CP15 有关的内容,我们不用关心,有兴趣的可以详细的看一下。

函数cpu_init_crit 也在是定义在start.S 文件中,函数内容如下:

在这里插入图片描述
可以看出函数cpu_init_crit 内部仅仅是调用了函数lowlevel_init,接下来就是详细的分析一下lowlevel_init 和_main 这两个函数。

lowlevel_init 函数详解

函数lowlevel_init 在文件arch/arm/cpu/armv7/lowlevel_init.S 中定义,内容如下:
在这里插入图片描述
在这里插入图片描述

第22 行设置sp 指向CONFIG_SYS_INIT_SP_ADDR,CONFIG_SYS_INIT_SP_ADDR 在include/configs/mx6ullevk.h 文件中,在mx6ullevk.h 中有如下所示定义:

在这里插入图片描述
示例代码32.2.2.2 中的IRAM_BASE_ADDR 和IRAM_SIZE 在文件
arch/arm/include/asm/arch-mx6/imx-regs.h 中有定义,如下所示,其实就是IMX6UL/IM6ULL 内部ocram 的首地址和大小

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

s_init 函数详解

在上一小节中,我们知道lowlevel_init 函数后面会调用s_init 函数,s_init 函数定义在文件arch/arm/cpu/armv7/mx6/soc.c 中,如下所示:
在这里插入图片描述
在第816 行会判断当前CPU 类型,如果CPU 为MX6SX、MX6UL、MX6ULL 或MX6SLL中的任意一种,那么就会直接返回,相当于s_init 函数什么都没做。所以对于I.MX6UL/I.MX6ULL 来说,s_init 就是个空函数。从s_init 函数退出以后进入函数lowlevel_init,但是lowlevel_init 函数也执行完成了,返回到了函数cpu_init_crit,函数cpu_init_crit 也执行完成了,最终返回到save_boot_params_ret,函数调用路径如图32.2.3.1 所示:

在这里插入图片描述

从图32.2.3.1 可知,接下来要执行的是save_boot_params_ret 中的_main 函数,接下来分析_main 函数。

_main 函数详解

第93 行,调用board_init_f 函数,此函数定义在文件common/board_f.c 中!主要用来初始化DDR,定时器,完成代码拷贝等等,此函数我们后面在详细的分析。

第103 行,重新设置环境(sp 和gd)、获取gd->start_addr_sp 的值赋给sp,在函数board_init_f中会初始化gd 的所有成员变量,其中gd->start_addr_sp=0X9EF44E90,所以这里相当于设置
sp=gd->start_addr_sp=0X9EF44E90。0X9EF44E90 是DDR 中的地址,说明新的sp 和gd 将会存放到DDR 中,而不是内部的RAM 了。GD_START_ADDR_SP=64,参考示例代码32.2.2.4。

这个就是_main 函数的运行流程,在_main 函数里面调用了board_init_f、relocate_code、relocate_vectors 和board_init_r 这4 个函数,接下来依次看一下这4 个函数都是干啥的。

board_init_f 函数详解(初始化外设、uboot重定位给Linux腾空间)

_main 中会调用board_init_f 函数,board_init_f 函数主要有两个工作:

  • ①、初始化一系列外设,比如串口、定时器,或者打印一些消息等。
  • ②、初始化gd 的各个成员变量,uboot 会将自己重定位到DRAM 最后面的地址区域,也就是将自己拷贝到DRAM 最后面的内存区域中。这么做的目的是给Linux 腾出空间,防止Linux kernel 覆盖掉uboot,将DRAM 前面的区域完整的空出来。在拷贝之前肯定要给uboot 各部分分配好内存位置和大小,比如gd 应该存放到哪个位置,malloc 内存池应该存放到哪个位置等等。这些信息都保存在gd 的成员变量中,因此要对gd 的这些成员变量做初始化。最终形成一个完整的内存“分配图”,在后面重定位uboot 的时候就会用到这个内存“分配图”。

。。代码未贴出:

第9 行,board_early_init_f 函数,板子相关的早期的一些初始化设置,I.MX6ULL 用来初始化串口的IO 配置

第10 行,timer_init,初始化定时器,Cortex-A7 内核有一个定时器,这里初始化的就是Cortex-A 内核的那个定时器。通过这个定时器来为uboot 提供时间。就跟Cortex-M 内核Systick 定时器一样。关于Cortex-A 内部定时器的详细内容,请参考文档《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》的“Chapter B8 The Generic Timer”章节。

第11 行,board_postclk_init,对于I.MX6ULL 来说是设置VDDSOC 电压。

第12 行,get_clocks 函数用于获取一些时钟值,I.MX6ULL 获取的是sdhc_clk 时钟,也就是SD 卡外设的时钟。

第13 行,env_init 函数是和环境变量有关的,设置gd 的成员变量env_addr,也就是环境变量的保存地址。

第14 行,init_baud_rate 函数用于初始化波特率,根据环境变量baudrate 来初始化gd->baudrate。

第15 行,serial_init,初始化串口。

第16 行,console_init_f,设置gd->have_console 为1,表示有个控制台,此函数也将前面暂存在缓冲区中的数据通过控制台打印出来。

第17 行、display_options,通过串口输出一些信息,如图32.2.5.1 所示:

在这里插入图片描述

第19 行,print_cpuinfo 函数用于打印CPU 信息,结果如图32.2.5.3 所示:

在这里插入图片描述
第20 行,show_board_info 函数用于打印板子信息,会调用checkboard 函数,结果如图32.2.5.4 所示:
在这里插入图片描述
第23 行,init_func_i2c 函数用于初始化I2C,初始化完成以后会输出如图32.2.5.5 所示信息:
在这里插入图片描述


第44 行,setup_dest_addr 函数,设置目的地址,设置gd->ram_size,gd->ram_top,gd->relocaddr这三个的值。接下来我们会遇到很多跟数值有关的设置,如果直接看代码分析的话就太费时间了,我可以修改uboot 代码,直接将这些值通过串口打印出来,比如这里我们修改文件common/board_f.c,因为setup_dest_addr 函数定义在文件common/board_f.c 中,在setup_dest_addr函数输入如图32.2.5.6 所示内容:

在这里插入图片描述
设置好以后重新编译uboot,然后烧写到SD 卡中,选择SD 卡启动,重启开发板,打开SecureCRT,uboot 会输出如图32.2.5.7 所示信息:

在这里插入图片描述
从图32.2.5.7 可以看出:

gd->ram_size = 0X20000000 //ram 大小为0X20000000=512MB
gd->ram_top = 0XA0000000 //ram 最高地址为0X80000000+0X20000000=0XA0000000
gd->relocaddr = 0XA0000000 //重定位后最高地址为0XA0000000

第45 行,reserve_round_4k 函数用于对gd->relocaddr 做4KB 对齐,因为
gd->relocaddr=0XA0000000,已经是4K 对齐了,所以调整后不变。

第46 行,reserve_mmu,留出MMU 的TLB 表的位置,分配MMU 的TLB 表内存以后会对gd->relocaddr 做64K 字节对齐。完成以后gd->arch.tlb_size、gd->arch.tlb_addr 和gd->relocaddr如图32.2.5.8 所示:

在这里插入图片描述
从图32.2.5.8 可以看出:

gd->arch.tlb_size= 0X4000 //MMU 的TLB 表大小
gd->arch.tlb_addr=0X9FFF0000 //MMU 的TLB 表起始地址,64KB 对齐以后
gd->relocaddr=0X9FFF0000 //relocaddr 地址

第47 行,reserve_trace 函数,留出跟踪调试的内存,I.MX6ULL 没有用到!

第48 行,reserve_uboot,留出重定位后的uboot 所占用的内存区域,uboot 所占用大小由gd->mon_len 所指定,留出uboot 的空间以后还要对gd->relocaddr 做4K 字节对齐,并且重新设置gd->start_addr_sp,结果如图32.2.5.9 所示:

在这里插入图片描述

。。。

在这里插入图片描述
从图32.2.5.16 可以看出,uboot 重定位后的偏移为0X18747000,重定位后的新地址为0X9FF4700,新的gd 首地址为0X9EF44EB8,最终的sp 为0X9EF44E90。

至此,board_init_f 函数就执行完成了,最终的内存分配如图32.2.5.16 所示:
在这里插入图片描述

relocate_code 函数详解

relocate_vectors 函数详解

board_init_r 函数详解

run_main_loop 函数详解

cli_loop 函数详解

cmd_process 函数详解

bootz 启动 Linux 内核过程

uboot启动Linux使用bootz命令,①bootz命令是如何启动Linux内核?②uboot的生命是怎么终止的?③Linux又是怎么启动的呢?

images 全局变量

不管是bootz命令 还是bootm 命令,在启动Linux 内核的时候都会用到一个重要的全局变量:images,images 在文件cmd/bootm.c 中有如下定义:

在这里插入图片描述
images 是bootm_headers_t 类型的全局变量,bootm_headers_t 是个boot 头结构体,在文件include/image.h 中的定义如下(删除了一些条件编译代码):
在这里插入图片描述
在这里插入图片描述

第335 行的os 成员变量是image_info_t 类型的,为系统镜像信息。
第352~362 行这11 个宏定义表示BOOT 的不同阶段。

接下来看一下结构体image_info_t,也就是系统镜像信息结构体,此结构体在文件include/image.h 中的定义如下:
在这里插入图片描述
全局变量images 会在bootz 命令的执行中频繁使用到,相当于Linux 内核启动的“灵魂”

在这里插入图片描述

在这里插入图片描述
下面详细介绍各个函数:

do_bootz 函数(bootz启动内核命令的执行函数)

bootz 命令的执行函数为do_bootz,在文件cmd/bootm.c 中有如下定义:

bootz_start 函数

do_bootm_states 函数

bootm_os_get_boot_func 函数

do_bootm_linux 函数

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux下使用nginx搭建集群,CentOS(linux) 下Nginx的安装(Nginx+Tomcat集群第一步)

CentOS(linux) 下Nginx的安装(NginxTomcat集群)CentOS 7.4(腾讯云)pcre库zlib库opensslNginx服务器安装gcc g开发类库yum -y install gcc automake autoconf libtool makeyum install gcc gcc-cwget https://ftp.pcre.org/pub/pcre/pcre-8.42.tar.gz解压pcre库tar -zxvf pcre-8…

在线生成透明ICO图标神器

此神器的链接为&#xff1a;http://ico.duduxuexi.com/ 大家可以将这个网址收藏好&#xff0c;本人亲测十分好用&#xff01;对我们的ios,安卓以及windows开发都有极大的好处。转载于:https://www.cnblogs.com/geeksongs/p/10040935.html

Vim的使用和快捷键介绍

参考&#xff1a;Linux–Vim的使用以及快捷键大全 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-07-12 12:43:19 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/107297756 目录安装vim三种常用的模式三种常用模式的切换vim 快捷键1.一般模…

手机当电脑麦克风 linux,WO Mic让手机成为电脑的无线麦克风

WO Mic 是一款可以让 Android 手机成为电脑无线麦克风的应用&#xff0c;支持 windows 和 Linux&#xff0c;除了 WiFi&#xff0c;还能使用蓝牙以及 USB 数据线(只有安卓支持数据线连接)。官方下载页面如下&#xff1a;一、windows下安装连接windows主机上需要安装客户端和驱动…

如何编写 Cloud9 JavaScript IDE 的功能扩展

上周末我们在JSConf.eu发布了 Cloud9 IDE &#xff0c;同时发布了对应的GitHub项目。在4天时间里该项目得到340个人的关注和将近50个fork。Cloud9的口号是由"由Javascripters 为Javascripters创建的IED"&#xff0c;这口号有点递归&#xff0c;它意味着你可以hack这个…

make工具和Makefile基础语法(含有交叉编译、pthread_create()的处理)

目录含有交叉编译、pthread_create()处理初次使用1、编写Makefile文件2、make3、make cleanMakefile基本语法1、Makefile规则格式2、变量3、变量赋值符&#xff08;、:、?、&#xff09;4、模式规则&#xff08;通配符&#xff09;5、自动化变量6、伪目标7、条件判断8、函数使…

树莓派超声波模块测距

参考&#xff1a;树莓派超声波模块测距及C语言demo 作者&#xff1a;一只青木呀 发布时间&#xff1a; 2020-07-22 16:54:16 网址&#xff1a;https://blog.csdn.net/weixin_45309916/article/details/107516949 前面这篇博文已经讲过了&#xff1a;树莓派外设开发基础&#xf…

spring cloud (一、服务注册demo_eureka)

首先我的博客记理论知识很少&#xff0c;大家对spring boot、spring cloud 、分布式 、微服务什么的一点概念都没有的还请先去百度看看理论&#xff0c;知道了是做什么用的&#xff0c;然后再写下demo ,这样学起来才没有那么迷糊&#xff01; 我一般写下的demo都是我踩了很多坑…

JavaFX 的 UI 控件集 ControlsFX

出处&#xff1a;http://www.oschina.net/p/controlsfx JavaFX 的 UI 控件集 ControlsFX ControlsFX 开源项目旨在为 JavaFX 开发提供更多的 UI 控件和其他工具。 该项目主要针对JavaFX 8.0&#xff08;捆绑在JDK 8中&#xff09;&#xff0c;考虑到JavaFX中已经包含了一些控件…

shell 脚本编写使用

目录一、什么是shell 脚本二、shell 脚本写法三、shell 脚本语法1、第一个 shell 脚本2、read命令3、数值计算4、test命令5、中括号判断符6、默认变量7、条件判断8、函数9、循环一、什么是shell 脚本 终端中输入一系列命令完成一些操作&#xff0c;但是我们一条一条输入命令&a…

javaweb开发之处理表单上传文件和文件下载

2019独角兽企业重金招聘Python工程师标准>>> 一、基于表单的上传文件 1. enctype属性 当表单需要上传文件时&#xff0c;需指定表单 enctype 的值为 multipart/form-data。 在 form 元素的语法中&#xff0c;enctype 属性指定将数据发送到服务器时浏览器使用的编码…

单片机预备知识(电平、进制转换、字节、数据类型)

参考&#xff1a;郭天祥十天带你精通51单片机 网址&#xff1a;https://www.bilibili.com/video/BV1DW411a7mz/?spm_id_from333.788.videocard.0 目录电平特性二进制进制转换1K字节等于多少字节基本数据类型电平特性 二进制 进制转换 也可参考这篇&#xff1a;进制问题和C语言…

[summary] 单调队列

2019独角兽企业重金招聘Python工程师标准>>> 很久没做单调队列了╮(╯_╰)╭ 已经不太会了.... 单调队列究其本质就是队列,加上队尾可以删除. 队列都是从队尾插入,队首输出.单调队列也一样. 以队尾到队首递增的单调队列为例,我们需要保证队列的单调性,所以当插入一…

段错误的调试方法(printf输出、GDB)

参考&#xff1a;段错误产生原因及简单的调试方法 参考&#xff1a;如何解决段错误 参考&#xff1a;C语言gdb调试之精髓&#xff08;常用命令、多进程、多线程、程序日志&#xff09; 网址&#xff1a;https://www.bilibili.com/video/BV1ei4y1V758?fromsearch&seid40373…

STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、单总线获取DHT11模块温湿度并通过IIC显示到屏幕(软件IIC)

参考&#xff1a;基于stm32软件IIC的oled显示温湿度 作者&#xff1a;ZPZ DayUp 发布时间&#xff1a; 2021-07-25 20:52:43 网址&#xff1a;https://blog.csdn.net/m0_56197680/article/details/119077076?spm1001.2014.3001.5501 目录软件模拟IIC时序(起始、停止、应答、发…

w ndows7端口在哪里,win7电脑遇到端口被占用的情况该如何查看并将其关闭

Windows7操作系统的酷炫和强大已经深受用户们的喜欢了&#xff0c;这里根大家分享的是教你查看win7电脑端口是否被占用的技巧&#xff0c;端口是我们在进行远程或者打印机等都会遇到的&#xff0c;但是有很多用户会遇到端口被占用的情况&#xff0c;遇到这样的问题首先就要找出…

STM32F103五分钟入门系列(十三)独立看门狗IWDG

参考&#xff1a;STM32F103五分钟入门系列&#xff08;十三&#xff09;独立看门狗IWDG 作者&#xff1a;自信且爱笑‘ 发布时间&#xff1a;2021-07-31 19:50:28 网址&#xff1a;https://blog.csdn.net/Curnane0_0/article/details/119269391?utm_sourceapp&app_version…

android自动软键盘,Android自定义软键盘

MyKeyboardAndroid自定义键盘的使用实现步骤第一步&#xff1a;1、新建一个xml文件夹放在res目录下面&#xff0c;然后新建xml文件:money_keyboard.xml2、然后在XML文件中添加按钮布局&#xff0c;这个布局就是键盘的样子了android:horizontalGap"1dp"android:keyWid…

Bootstrap使用-1

目录 结构&#xff1a;1. 视图函数2. 模板3. 登陆4. 怎样发生的添加模板基础模板提供的block定制基础模板结构&#xff1a; $ tree -I "__pycache*|*.pyc|*.xlsx" -FCL 3 . |-- templates/ | -- h1.html -- test-boostracp.py 1. 视图函数 test-boostracp.py from …

段错误、内存溢出、内存泄漏(区别)、堆溢出、栈溢出

参考&#xff1a;内存泄漏、内存溢出、段错误、堆溢出、栈溢出 作者&#xff1a;焦木白 发布时间&#xff1a;2019-10-22 网址&#xff1a;https://blog.csdn.net/jiaomubai/article/details/102680705?spm1001.2014.3001.5501 目录段错误内存溢出内存泄漏栈溢出堆溢出段错误 …