STM32F103_LL库+寄存器学习笔记13 - 梳理外设CAN与如何发送CAN报文(串行发送)
导言
CAN总线因其高速稳定的数据传输与卓越抗干扰性能,在汽车、机器人及工业自动化中被广泛应用。它采用分布式网络结构,实现多节点间实时通信,确保各控制模块精准协同。在汽车领域,CAN总线连接发动机、制动、车身系统,保障车辆安全;在机器人和工业控制中,传感器与执行器间信息传递迅速,使其成为智能制造与自动化控制不可或缺的重要技术。
遗憾的是CubeMX不支持生成CAN总线的LL库代码。所以,梳理完HAL库的实现方式后,继续梳理寄存器方式的实现。
以下是本章节的效果,开发板每隔100ms往CAN总线发送一个报文。CANID是0x0123,数据帧,标准帧,长度0x08,内容是0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08。

项目地址:
- (HAL库):https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_hal_library13_Can_Send
- (寄存器方式):https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library13_Can_Send
一、CubeMX
1.1、Clock Configuration

1.2、Parameter Settings

采样点的设置是CAN稳定通讯的前提!
采样点的设置是CAN稳定通讯的前提!
采样点的设置是CAN稳定通讯的前提!
如上所示,配置的重点是4分频、Tq1 = 14Times、Tq2 = 3Times。此时采样点的计算如下:
采样点位置,采样点在同步段后的 Tq1 处,即 1 + 14 = 15 Tq,采样点比例 = 15 / 18 ≈ 83.3%。所以,Prescaler = 4, Tq1 = 14, Tq2 = 3 这种配置可以精确得到 500 kbps,并且采样点位于约 83.3% 的位置,这是一个较为合理的采样位置,既能保证波特率精确,又能提供足够的采样稳定性。参考我的另外一篇博文《CAN总线技术 | 物理层03 - 采样点》

二、代码(HAL库)
2.1、main.c


如上所示,通过HAL库来实现CAN发送的代码实在简单。
2.2、编译、下载代码

如上所示,编译通过。下载代码到开发板后,效果如下:

三、梳理CAN发送
3.1、发送邮箱
CAN发送比CAN接收要简单许多,所以先从简单的CAN发送开始梳理。


如上所示,CAN一共有三个发送邮箱,所以为什么函数Test_CAN_Send_Msg()里需要用HAL_CAN_GetTxMailboxesFreeLevel()去判断有没有空的发送邮箱,才能将需要发送的CAN报文丢进去发送邮箱。CAN的发送邮箱相当于串口的发送数据寄存器USART_TDR。只是CAN的发送邮箱一共有3个,而串口的发送数据寄存器USART_TDR只有一个。显然,CAN的发送缓存比串口的发送缓存要大得多。
请记住,软件只能将需要发送的CAN消息放到发送邮箱,并不是将CAN消息发送到总线。
3.2、发送优先级

如上所示,从《STM32F1参考手册》的章节22.7.1-发送处理看到,CAN消息的发送优先级有两种设置,通过寄存器CAN_MCR的位TXFP置1,开启FIFO模式。值得注意的是,寄存器CAN_MCR的位TXFP默认是置0,即标识符决定发送优先级。另外,标识符越小,优先级越高。

3.3、标识符是什么?
如下图所示,标识符就是CANID。

3.4、终止发送

3.5、禁止自动重传模式

默认是禁止自动重传模式的,禁止CAN自动重传功能的影响主要体现在以下几点:
- 消息丢失风险增大
当一帧CAN报文发送失败(例如由于总线干扰或仲裁错误)时,如果自动重传被禁用,硬件不会重新尝试发送这帧报文,导致该报文直接丢失。此时,如果没有额外的软件层重传机制,就可能影响通信的可靠性。 - 降低传输延时
在一些实时性要求高的应用中,自动重传可能会引入额外延时。禁用自动重传可以使得一旦检测到发送失败就立即放弃,从而避免因连续重传而延迟后续消息的发送。 - 简化错误处理逻辑
禁用自动重传后,发送操作完成后,邮箱状态总会变为空置(无论是成功发送或发送失败),这对某些系统来说可以简化对邮箱状态的监控和管理。不过,这也意味着应用层需要增加判断和处理发送失败的逻辑,以决定是否进行重发或者采取其他补救措施。 - 总线负载和资源利用
自动重传在错误发生时可能导致总线负载增加,尤其在总线故障或者干扰情况下频繁重传会占用大量资源。禁用自动重传可以防止这种情况,但同时需要确保上层有机制去检测和响应通信错误。总之,禁用自动重传功能是一种在追求低延时和精细控制CAN通信时的取舍,但需要权衡消息可靠性和实时性,通常需要在应用层增加适当的错误检测和补救机制。

3.6、500K波特率,发送8个字节的CAN标准帧消息,需要多长时间?
理论上,我们可以计算标准CAN数据帧的位数,再乘以每位的传输时间。对于标准CAN数据帧(CAN 2.0A),各部分位数通常为:
- 起始位 (SOF):1位
- 仲裁段:11位标识符 + 1位远程传输请求(RTR) = 12位
- 控制段:6位(包括IDE、保留位以及4位数据长度代码 DLC)
- 数据段:8字节 × 8 = 64位
- CRC段:15位CRC + 1位CRC分隔符 = 16位
- 确认段 (ACK):2位
- 结束位 (EOF):7位
总计:1 + 12 + 6 + 64 + 16 + 2 + 7 = 108位
在500k波特率下,每位传输时间为:1 / 500,000 = 2微秒。因此,理论上传输108位的CAN报文所需时间为:108 × 2μs = 216微秒。注意:这只是理论传输时间,实际情况还可能受到位填充、总线仲裁和间隔等因素影响,但理论上就是216微秒。
四、寄存器梳理
4.1、配置时钟
4.1.1、开启CAN时钟


RCC->APB1ENR |= (1UL << 25UL); // 开启CAN时钟
4.1.2、开启GPIOA时钟


RCC->APB2ENR |= (1UL << 2UL); // 开启GPIOA时钟,因为CAN使用PA11与PA12端口
4.2、配置GPIO
4.2.1、CAN_REMAP决定使用哪个GPIO口


如上所示,CAN_REMAP默认状态就是00,所以CAN1使用PA11与PA12。如需复用其他GPIO口,按照表格去修改GPIO_REMAP即可。
4.2.2、GPIO配置

如上图所示,CAN_TX与CAN_RX有对应的GPIO口模式
/* 2. 配置PA11(CAN_RX)为上拉输入、PA12(CAN_TX)为复用推挽输出 */// PA11: CRH[15:12], MODE=00, CNF=10(上拉输入)GPIOA->CRH &= ~(0xF << 12);GPIOA->CRH |= (0x8 << 12);GPIOA->ODR |= (1UL << 11); // 上拉// PA12: CRH[19:16], MODE=11(50MHz), CNF=10(复用推挽)GPIOA->CRH &= ~(0xF << 16);GPIOA->CRH |= (0xB << 16);
4.3、配置CAN
4.3.1、退出睡眠模式

如上所示,进入初始化之前必须先退出睡眠模式,避免进入初始化模式失败。
if (CAN1->MSR & (1UL << 1)) { // 检查 MSR.SLAK 是否为 1CAN1->MCR &= ~(1UL << 1); // 清除 SLEEP 位while (CAN1->MSR & (1UL << 1)); // 等待 MSR.SLAK 变 0
}
4.3.2、进入初始化模式


/* 3. 进入初始化模式 */
CAN1->MCR |= (1UL << 0); // 请求进入INIT (MCR.INRQ=1)
while (!(CAN1->MSR & (1UL << 0))); // 等待INAK=1 (MSR.INAK=1)
4.3.3、关闭时间触发模式

CAN1->MCR &= ~(1UL << 7); // 清除TTCM位(时间触发模式)
4.3.4、自动离线管理模式

CAN1->MCR &= ~(1UL << 6); // 清除ABOM位(自动离线管理模式)
4.3.5、自动唤醒

CAN1->MCR &= ~(1UL << 5); // 清除AWUM位(软件自动唤醒)
4.3.6、接收FIFO锁定模式

CAN1->MCR &= ~(1UL << 3); // 清除RFLM位(接收FIFO设置新报文覆盖旧报文)
4.3.7、发送FIFO优先级


CAN1->MCR &= ~(1UL << 2); // 清除TXFP位(发送FIFO优先级由标识符来决定)
4.3.8、禁止报文自动重传


CAN1->MCR |= (1UL << 4); // CAN报文只发送一次
4.3.9、位时序(重点!!!!)
位时序设置规则参考《CAN总线技术 | 物理层03 - 采样点》

如上所示,相当于CubeMX里的Bit Timings Parameters。


4. 设置BTR=0x002D0003- SJW=0 => 1Tq- TS2=0x02 => 2 => 3Tq- TS1=0x0D => 13 => 14Tq- BRP=0x03 => 3 => 分频=4*/CAN1->BTR = (0x00 << 24) | // SILM(31) | LBKM(30) = 0(0x00 << 22) | // SJW(23:22) = 0 (SJW = 1Tq)(0x02 << 20) | // TS2(22:20) = 2 (TS2 = 3Tq)(0x0D << 16) | // TS1(19:16) = 13 (TS1 = 14Tq)(0x0003); // BRP(9:0) = 3 (Prescaler = 4)
4.3.10、退出初始化模式、进入正常模式

如上所示,软件对寄存器CAN_MCR的位INRQ清0时,会退出初始化模式,进入工作模式。
CAN1->MCR &= ~(1UL << 0); // 清除 INRQ (进入正常模式)
while (CAN1->MSR & (1UL << 0)); // 等待 MSR.INAK 变 0
4.3.11、设置过滤器0

如上所示,STM32F103ZET6一共有14个过滤器,但CAN要能正常收发,必须至少要设置一个过滤器。
所有过滤器进入初始化模式

如上所示,通过寄存器CAN_FMR的位0-FINIT置1,让所有过滤器组进入初始化模式。
CAN1->FMR |= (1UL << 0); // 进入过滤器初始化模式
设置过滤器组0通过所有标识符(CANID),即不过滤
CAN1->sFilterRegister[0].FR1 = 0x00000000;
CAN1->sFilterRegister[0].FR2 = 0x00000000;
如上所示,数组0代表过滤器组0。当FR1与FR2都设置0x00000000时,代表不过滤任何CANID,即所有CANID都会被接收。此时,如果CAN总线上有很多高频的CAN消息的话,CAN中断会非常频繁地进入,极大地浪费MCU的资源。
后续,会弄一篇笔记,讲讲怎样设置过滤器组,让开发板只接收感兴趣的CANID,而不是所有的CANID。
过滤器组0匹配FIFO0

CAN1->FFA1R &= ~(1UL << 0); // 过滤器组0 分配到 FIFO0
激活过滤器组0

CAN1->FA1R |= (1UL << 0); // 激活过滤器 0
所有过滤器组退出初始化模式

CAN1->FMR &= ~(1UL << 0); // 退出过滤器初始化模式
4.4、发送CAN报文
4.4.1、确认邮箱是不是空闲

如上所示,通过判断寄存器CAN_TIxR的bit0-TXRQ是不是等于0,来确认发送邮箱是不是空闲的。
/* 寻找空闲邮箱 */
for(mailbox = 0; mailbox < 3; mailbox++) {if((CAN1->sTxMailBox[mailbox].TIR & (1UL << 0)) == 0)break;
}
if(mailbox >= 3)return 1; // 无空闲邮箱
4.4.2、清空某个发送邮箱

将发送邮箱的4个寄存器都清0即可,包括TIR、TDTR、TDLR、TDHR。
/* 清空该邮箱 */
CAN1->sTxMailBox[mailbox].TIR = 0;
CAN1->sTxMailBox[mailbox].TDTR = 0;
CAN1->sTxMailBox[mailbox].TDLR = 0;
CAN1->sTxMailBox[mailbox].TDHR = 0;
4.4.3、设置将要发送的CANID、CAN帧类型

CAN1->sTxMailBox[mailbox].TIR |= (stdId << 21); // 标准ID写入TIR的[31:21]、IDE=0相当于标准帧、RTR=0相当于数据帧
4.4.4、设置CAN报文长度

CAN1->sTxMailBox[mailbox].TDTR = (DLC & 0x0F); // 设置CAN报文的长度。使用&运算的目的是保证只有变量DLC的低四位写入TDTR寄存器,不会干涉到其他位。
4.4.5、将要发送的数据放入发送邮箱

/* 填充数据 */
if(DLC <= 4) {for(uint8_t i = 0; i < DLC; i++) {CAN1->sTxMailBox[mailbox].TDLR |= ((uint32_t)data[i]) << (8 * i);}
} else {for(uint8_t i = 0; i < 4; i++) {CAN1->sTxMailBox[mailbox].TDLR |= ((uint32_t)data[i]) << (8 * i);}for(uint8_t i = 4; i < DLC; i++) {CAN1->sTxMailBox[mailbox].TDHR |= ((uint32_t)data[i]) << (8 * (i-4));}
}
4.4.6、请求发送数据

CAN1->sTxMailBox[mailbox].TIR |= CAN_TI0R_TXRQ;
4.4.7、等待邮箱的CAN消息被成功发送(可选)

/* 轮询等待TXRQ清零或超时 */
while((CAN1->sTxMailBox[mailbox].TIR & CAN_TI0R_TXRQ) && --timeout);
if(timeout == 0) {// 发送失败(无ACK或位错误), 返回2return 2;
}
为什么要等待TXRQ清零?
TXRQ 位的作用是在发送请求时置 1,并在以下情况下自动清零:
- 报文成功发送(总线空闲时成功仲裁,并收到 ACK)。
- 发送失败(无 ACK 或仲裁失败):
- 若 NART=0(自动重传开启),CAN 硬件会自动重试,直到发送成功。
- 若 NART=1(自动重传关闭),发送失败时 TXRQ 也会清零,并可能产生错误标志(TERR、ALST、REC/TEC 递增等)。
- 软件手动中止发送(通过设置 ABRQ 置 1 来取消发送)。
如果不等待 TXRQ 清零,可能发生:
- 报文未成功发送(因无ACK、错误等原因),但代码并不知道,导致误以为报文已经发送成功;
- CAN 总线忙碌,报文未立即发送,但代码已经继续执行其他任务,可能影响数据完整性。
什么时候可以不等 TXRQ 清零?
- 如果程序不关心发送是否成功(只管发,不管 ACK),可以不等 TXRQ 清零。
- 如果使用中断模式(而非轮询),可以不在此等待 TXRQ,而是注册 CAN 发送完成中断(最常用!!!!!!)。
- 如果应用层通过 TSR(Transmit Status Register)等方式定期检查发送状态,而不依赖 TXRQ 位轮询。
什么时候必须等 TXRQ 清零?
- 需要确认报文已发送完毕(尤其是 NART=1 时,若无 ACK 会导致发送失败)。
- 要确保 FIFO 发送顺序正确(如果多个报文依次发送,等待 TXRQ 清零可确保当前报文已经结束)。
- 应用层需要可靠的反馈(如果 TXRQ 持续置位,说明发送失败,应触发错误处理机制)。
五、代码(寄存器方式)
5.1、main.c


如上所示,函数CAN_Config()将CAN设置好,并进入正常工作模式。波特率500K,其他小功能全部关闭,且不过滤任何CANID。



5.2、编译、调试

编译OK,下载程序到开发板,效果如下所示:

相关文章:
STM32F103_LL库+寄存器学习笔记13 - 梳理外设CAN与如何发送CAN报文(串行发送)
导言 CAN总线因其高速稳定的数据传输与卓越抗干扰性能,在汽车、机器人及工业自动化中被广泛应用。它采用分布式网络结构,实现多节点间实时通信,确保各控制模块精准协同。在汽车领域,CAN总线连接发动机、制动、车身系统,…...
JavaScript学习19-事件类型之鼠标事件
1. 2. 3....
Linux系统调用编程
文章目录 一、进程和线程二、Linux的虚拟内存管理和stm32的真实物理内存**Linux虚拟内存管理**STM32物理内存映射2. 主要区别 三、Linux系统调用函数 fork()、wait()、exec()1. fork():创建子进程2. wait():等待子进程状态改变3. exec():替换…...
游戏引擎学习第203天
回顾当前情况 在这里我将直播完成整个游戏的制作。我们现在面临一些技术上的困难,确实如此。我的笔记本电脑的电源接口坏了,所以我不得不准备了这台备用笔记本,希望它能够正常工作。我所以希望一切都还好,尽管我不完全确定是否一…...
408 计算机网络 知识点记忆(4)
前言 本文基于王道考研课程与湖科大计算机网络课程教学内容,系统梳理核心知识记忆点和框架,既为个人复习沉淀思考,亦希望能与同行者互助共进。(PS:后续将持续迭代优化细节) 往期内容 408 计算机网络 知识…...
线性代数:分块矩阵,秩,齐次线性,非齐次线性的解相关经典例题
所以C错误,选D 排除A,B选项...
Nginx功能及应用全解:从负载均衡到反向代理的全面剖析
Nginx作为一款开源的高性能HTTP服务器和反向代理服务器,凭借其高效的资源利用率和灵活的配置方式,已成为互联网领域中最受欢迎的Web服务器之一。无论是作为HTTP服务器、负载均衡器,还是作为反向代理和缓存服务器,Nginx的多种功能广…...
深度学习数据集划分比例多少合适
在机器学习和深度学习中,测试集的划分比例需要根据数据量、任务类型和领域需求灵活调整。 1. 常规划分比例 通用场景 训练集 : 验证集 : 测试集 60% : 20% : 20% 适用于大多数中等规模数据集(如数万到数十万样本),平衡了训练数…...
CExercise_1_5 水仙花数
题目: 经典循环案例:请求出所有的水仙花数,并统计总共有几个。 所谓的水仙花数是指一个三位数,其各位数字的立方和等于该数本身。 举例:153就是一个水仙花数,153 1 * 1 * 1 5 * 5 * 5 3 * 3 * 3 1 125…...
用C实现一个最简单的正则表达式引擎
用C实现一个简单的正则表达式引擎 下面我将实现一个极简的正则表达式引擎,仅支持以下基本功能: . 匹配任意单个字符* 匹配零个或多个前导字符^ 匹配字符串开头$ 匹配字符串结尾 完整代码实现 #include <stdio.h> #include <stdbool.h>bo…...
哈密尔顿路径(Hamiltonian Path)及相关算法题目
哈密尔顿路径要求访问图中每个顶点恰好一次,通常用于解决旅行商问题(TSP)或状态压缩DP问题。 哈密尔顿路径(Hamiltonian Path)是指在一个图中经过每个顶点恰好一次的路径。如果这条路径的起点和终点相同(即…...
MINIQMT学习课程Day10
开始获取股票数据课程的学习: 获取qmt账号的持仓情况后,我们进入下一步,如何获得当前账号的委托状况 还是之前的步骤,打开qmt,选择独立交易, 之后使用pycharm,编写py文件 导入包:…...
JAVA实战开源项目:智慧图书管理系统(Vue+SpringBoot) 附源码
本文项目编号 T 152 ,文末自助获取源码 \color{red}{T152,文末自助获取源码} T152,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...
Linux 系统管理综合实训 —— 基于 NAT 模式的多 IP 配置、Nginx 服务部署及存储管理
1. 虚拟机网络配置:NAT模式与多IP地址设置 将你的虚拟机的网卡模式设置为nat模式,给虚拟机网卡配置三个主机位分别为100、200、168的ip地址 设置静态IP [rootlocalhost ~]# nmcli c modify ens160 ipv4.method manual ipv4.addresses 192.168.2.100/2…...
如何在windows 环境、且没有显卡的情况下用python跑通从ModelScope下载的大模型的调用
文章目录 背景介绍源代码:安装调试过程1.设置第三方镜像源2.预先安装:3.在python中创建代码:4.最终修改程序,将device_map从“cuda”改成“auto”,大模型调用1.5B(1___5B)的5.最终跑出结果解释:示例&#x…...
黑马点评redis改 part 1
本篇将主要阐述短信登录的相关知识,感谢黑马程序员开源,感谢提供初始源文件(给到的是实战第7集开始的代码)【Redis实战篇】黑马点评学习笔记(16万字超详细、Redis实战项目学习必看、欢迎点赞⭐收藏)-CSDN博…...
Apache Struts2 漏洞(CVE-2017-5638)技术分析
一、漏洞简介 CVE-2017-5638 是 Apache Struts2 中的一个远程命令执行漏洞,攻击者可以通过构造特定的 HTTP 请求头,利用Struts的 OGNL 表达式解析机制,在服务器端执行任意代码。 二、漏洞触发场景 漏洞存在于 Struts2 的 Jakarta Multipar…...
A2DP(Advanced Audio Distribution Profile)是蓝牙协议栈中用于音频传输的一个标准化协议
A2DP(Advanced Audio Distribution Profile)是蓝牙协议栈中用于音频传输的一个标准化协议,主要用于高质量音频流的无线传输。以下是A2DP协议的详细信息: 定义 A2DP协议允许音源设备(Source,简称SRC&#…...
【Ragflow】11. 文件解析流程分析/批量解析实现
概述 本文继续对ragflow文档解析部分进行分析,并通过脚本的方式实现对文件的批量上传解析。 文件解析流程 文件解析的请求处理流程大致如下: 1.前端上传文件,通过v1/document/run接口,发起文件解析请求 2.后端api\apps\docum…...
第三期:深入理解 Spring Web MVC [特殊字符](数据传参+ 特殊字符处理 + 编码问题解析)
✨前言:传参和状态管理,看似简单其实门道不少 在 Web 开发中,前端和后端最核心的交流方式就是“传参”,而“传参”除了涉及如何写代码获取参数,还藏着很多开发者容易忽略的细节: 为什么 URL 带了中文&…...
嵌入式学习笔记——ARM-中断与异常
文章目录 中断与异常的区别中断与 DMA 的区别中断能否睡眠?下半部能否睡眠?1. 中断处理程序不能睡眠2. 下半部(SoftIRQ、Tasklet、Workqueue) 中断处理注意点1. 快进快出2. 避免阻塞3. 正确返回值4. 如何处理大量任务5. 避免竞态问…...
Everything 安装教程与使用教程(附安装包)
文章目录 前言一、Everything 介绍二、Everything 安装教程1.Everything 安装包下载2.选择安装文件3.选择安装语言4.接受许可协议5.选择安装位置6.配置安装选项7.完成安装 三、Everything 使用教程1.启动软件2.简单关键词搜索3.按类型搜索 前言 在日常使用电脑时,随…...
嵌入式开发中栈溢出的处理方法
嵌入式开发中栈溢出的处理方法 目录 引言栈溢出的原理栈溢出的危害栈溢出检测方法 哨兵变量法栈着色法硬件监测机制编译器栈保护 裸机系统中的栈溢出处理操作系统中的栈溢出处理预防栈溢出的最佳实践结论 引言 在嵌入式系统开发中,栈溢出是一个常见且危险的问题…...
SQL语句(三)—— DQL
目录 基本语法 一、基础查询 1、查询多个字段 2、字段设置别名 3、去除重复记录 4、示例代码 二、条件查询 1、语法 2、条件列表常用的运算符 3、示例代码 三、分组查询 (一)聚合函数 1、介绍 2、常见的聚合函数 3、语法 4、示例代码 &…...
#python项目生成exe相关了解
在 Windows 上将 Python 项目 生成 EXE 可执行文件,主要使用 pyinstaller。以下是完整步骤: 📌 1. 安装 PyInstaller pip install pyinstaller如果已安装,可执行以下命令检查版本: pyinstaller --versionὌ…...
Opencv计算机视觉编程攻略-第九节 描述和匹配兴趣点
一般而言,如果一个物体在一幅图像中被检测到关键点,那么同一个物体在其他图像中也会检测到同一个关键点。图像匹配是关键点的常用功能之一,它的作用包括关联同一场景的两幅图像、检测图像中事物的发生地点等等。 1.局部模板匹配 凭单个像素就…...
JSON-lib考古现场:在2025年打开赛博古董店的奇妙冒险
各位在代码海洋里捡贝壳的探险家们!今天我们要打开一个尘封的Java古董箱——JSON-lib!这货可是2003年的老宝贝,比在座很多程序员的工龄还大!准备好穿越回Web 1.0时代,感受XML统治时期的余晖了吗? …...
Android: Handler 的用法详解
Android 中 Handler 的用法详解 Handler 是 Android 中用于线程间通信的重要机制,主要用于在不同线程之间发送和处理消息。以下是 Handler 的全面用法指南: 一、Handler 的基本原理 Handler 基于消息队列(MessageQueue)和循环器(Looper)工作,…...
汇编学习之《push , pop指令》
学习本章前线了解ESP, EBP 指令 汇编学习之《指针寄存器&大小端学习》-CSDN博客 栈的特点: 好比一个垂直容器,可以陆续放入物体,但是先放的物体通常会被后面放的物体压着,只有等上面后放的物品拿出来后,才能…...
Python循环控制语句
1. 循环类型概述 Python提供两种主要的循环结构: while循环 - 在条件为真时重复执行for循环 - 遍历序列中的元素 2. while循环 基本语法 while 条件表达式:循环体代码示例 count 0 while count < 5:print(f"这是第{count1}次循环")count 13. f…...
