Linux学习第21天:Linux内核定时器驱动开发: 流淌的时间长河
Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长
在人类的发展进化中,时间是一个非常重要神秘的物质量。任何事物都是在时间的长河中流淌发生、发展、变化。我们进行驱动开发中对时间的定义和使用也是必须要掌握的重要知识点。
本节笔记主要学习Linux内核定时器的驱动开发,主要包括内核时间管理和定时器、硬件原理图分析【LED】、驱动开发和测试。最重要的内容为定时器驱动开发部分。
本笔记的脑图如下:
一、Linux时间管理和内核定时器
1.内核时间管理
作为一个应用者,不需要研究太深的具体实现。会用相应的API函数即可。
图形化配置界面可以设置系统节拍率。具体路径为:
Kernel Features
Timer frequency(<choice>[=y])
使用CONFIG_HZ来设置自己的系统时钟。
HZ表示一秒的节拍数,也就是频率。
使用jiffies来记录系统从启动以来的系统节拍数。
jiffies/HZ就是系统运行时间,单位为秒。
处理32位jiffies的绕回,用到的几个函数。如下:
函数 | 描述 |
time_after(unknown,known) | unkonwn超过known时为真 |
time_before(unknown,known) | unkonwn没有超过known时为真 |
time_after_eq(unknown,known) | unkonwn超过或等于known时为真 |
time_before_eq(unknown,known) | unkonwn没有超过或等于known时为真 |
2.内核定时器
需要周期性处理的工作都要用到定时器。Linux定时器采用系统时钟来实现,只需要提供超时时间和定时处理函数即可。
内核定时器超时会自动关闭。
使用timer_list结构体表示内核定时器。该结构体的定义如下:
struct timer_list {
struct list_head entry;
unsigned long expires; /* 定时器超时时间,单位是节拍数 */
struct tvec_base *base;
void (*function)(unsigned long); /* 定时处理函数 */
unsigned long data; /* 要传递给 function 函数的参数 */
int slack;
};
定义好定时器以后,需要以下函数对其进行初始化:
函数 | 说明 |
init_timer | 初始化 |
add_timer | 注册 |
del_timer | 删除 |
del_timer_sync | 等待其他处理器使用完定时器后删除 |
mod_timer | 修改定时值 |
内核定时器的使用流程如下:
1 struct timer_list timer; /* 定义定时器 */
2
3 /* 定时器回调函数 */
4 void function(unsigned long arg)
{
6 /*
7 * 定时器处理代码
8 */
9
10 /* 如果需要定时器周期性运行的话就使用 mod_timer
11 * 函数重新设置超时值并且启动定时器。
12 */
13 mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
14 }
15
16 /* 初始化函数 */
17 void init(void)
18 {
19 init_timer(&timer); /* 初始化定时器 */
20
21 timer.function = function; /* 设置定时处理函数 */
22 timer.expires=jffies + msecs_to_jiffies(2000);/* 超时时间 2 秒 */
23 timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
24
25 add_timer(&timer); /* 启动定时器 */
26 }
27
28 /* 退出函数 */
29 void exit(void)
30 {
31 del_timer(&timer); /* 删除定时器 */
32 /* 或者使用 */
33 del_timer_sync(&timer);
34 }
3.Linux内核短延时函数
函数 描述
void ndelay(unsigned long nsecs) 纳秒
void udelay(unsigned long usecs) 微秒
void mdelay(unsigned long mseces) 毫秒
二、硬件原理图分析
LED0 接到了 GPIO_3 上, GPIO_3 就是 GPIO1_IO03,当 GPIO1_IO03输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当 GPIO1_IO03 输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。所以 LED0 的亮灭取决于 GPIO1_IO03的输出电平,输出 0 就亮,输出 1 就灭。
三、驱动程序开发
1.修改设备树文件
使用已有的即可。
2.定时器驱动程序开发
38 struct timer_dev{
39 dev_t devid; /* 设备号 */
40 struct cdev cdev; /* cdev */
41 struct class *class; /* 类 */
42 struct device *device; /* 设备 */
43 int major; /* 主设备号 */
44 int minor; /* 次设备号 */
45 struct device_node *nd; /* 设备节点 */
46 int led_gpio; /* key 所使用的 GPIO 编号 */
47 int timeperiod; /* 定时周期,单位为 ms */
48 struct timer_list timer; /* 定义一个定时器 */
49 spinlock_t lock; /* 定义自旋锁 */
50 };
定时器设备结构体,在 48 行定义了一个定时器成员变量 timer。
60 static int led_init(void)
61 {
62 int ret = 0;
63
64 timerdev.nd = of_find_node_by_path("/gpioled");
65 if (timerdev.nd== NULL) {
66 return -EINVAL;
67 }
68
69 timerdev.led_gpio = of_get_named_gpio(timerdev.nd ,"led-gpio",
0);
70 if (timerdev.led_gpio < 0) {
71 printk("can't get led\r\n");
72 return -EINVAL;
73 }
74
75 /* 初始化 led 所使用的 IO */
76 gpio_request(timerdev.led_gpio, "led"); /* 请求 IO */
77 ret = gpio_direction_output(timerdev.led_gpio, 1);
78 if(ret < 0) {
79 printk("can't set gpio!\r\n");
80 }
81 return 0;
82 }
LED 灯初始化函数,从设备树中获取 LED 灯信息,然后初始化相应的 IO。
91 static int timer_open(struct inode *inode, struct file *filp)
92 {
93 int ret = 0;
94 filp->private_data = &timerdev; /* 设置私有数据 */
95
96 timerdev.timeperiod = 1000; /* 默认周期为 1s */
97 ret = led_init(); /* 初始化 LED IO */
98 if (ret < 0) {
99 return ret;
100 }
101 return 0;
102 }
函数 timer_open,对应应用程序的 open 函数,应用程序调用 open 函数打开/dev/timer 驱动文件的时候此函数就会执行。此函数设置文件私有数据为 timerdev,并且初始化定时周期默认为 1 秒,最后调用 led_init 函数初始化 LED 所使用的 IO。
111 static long timer_unlocked_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
112 {
原子哥在线教学:www.yuanzige.com 论坛:www.openedv.com
1228
I.MX6U 嵌入式 Linux 驱动开发指南
113 struct timer_dev *dev = (struct timer_dev *)filp->private_data;
114 int timerperiod;
115 unsigned long flags;
116
117 switch (cmd) {
118 case CLOSE_CMD: /* 关闭定时器 */
119 del_timer_sync(&dev->timer);
120 break;
121 case OPEN_CMD: /* 打开定时器 */
122 spin_lock_irqsave(&dev->lock, flags);
123 timerperiod = dev->timeperiod;
124 spin_unlock_irqrestore(&dev->lock, flags);
125 mod_timer(&dev->timer, jiffies +
msecs_to_jiffies(timerperiod));
126 break;
127 case SETPERIOD_CMD: /* 设置定时器周期 */
128 spin_lock_irqsave(&dev->lock, flags);
129 dev->timeperiod = arg;
130 spin_unlock_irqrestore(&dev->lock, flags);
131 mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
132 break;
133 default:
134 break;
135 }
136 return 0;
137 }
函数 timer_unlocked_ioctl,对应应用程序的 ioctl 函数,应用程序调用 ioctl函数向驱动发送控制信息,此函数响应并执行。此函数有三个参数: filp, cmd 和 arg,其中 filp是对应的设备文件, cmd 是应用程序发送过来的命令信息, arg 是应用程序发送过来的参数,在本章例程中 arg 参数表示定时周期。
CLOSE_CMD: 关闭定时器命令, 调用 del_timer_sync 函数关闭定时器。
OPEN_CMD:打开定时器命令,调用 mod_timer 函数打开定时器,定时周期为 timerdev 的
timeperiod 成员变量,定时周期默认是 1 秒。
SETPERIOD_CMD:设置定时器周期命令,参数 arg 就是新的定时周期,设置 timerdev 的
timeperiod 成员变量为 arg 所表示定时周期指。并且使用 mod_timer 重新打开定时器,使定时器
以新的周期运行。
139 /* 设备操作函数 */
140 static struct file_operations timer_fops = {
141 .owner = THIS_MODULE,
142 .open = timer_open,
143 .unlocked_ioctl = timer_unlocked_ioctl,
144 };
定时器驱动操作函数集 timer_fops。
146 /* 定时器回调函数 */
147 void timer_function(unsigned long arg)
148 {
149 struct timer_dev *dev = (struct timer_dev *)arg;
150 static int sta = 1;
151 int timerperiod;
152 unsigned long flags;
153
154 sta = !sta; /* 每次都取反,实现 LED 灯反转 */
155 gpio_set_value(dev->led_gpio, sta);
156
157 /* 重启定时器 */
158 spin_lock_irqsave(&dev->lock, flags);
159 timerperiod = dev->timeperiod;
160 spin_unlock_irqrestore(&dev->lock, flags);
161 mod_timer(&dev->timer, jiffies +
msecs_to_jiffies(dev->timeperiod));
162 }
函数 timer_function,定时器服务函数,此函有一个参数 arg,在本例程中arg 参数就是 timerdev 的地址,这样通过 arg 参数就可以访问到设备结构体。当定时周期到了以后此函数就会被调用。在此函数中将 LED 灯的状态取反,实现 LED 灯闪烁的效果。因为内核定时器不是循环的定时器,执行一次以后就结束了,因此在 161 行又调用了 mod_timer 函数重新开启定时器。
169 static int __init timer_init(void)
170 {
171 /* 初始化自旋锁 */
172 spin_lock_init(&timerdev.lock);
173
174 /* 注册字符设备驱动 */
175 /* 1、创建设备号 */
176 if (timerdev.major) { /* 定义了设备号 */
177 timerdev.devid = MKDEV(timerdev.major, 0);
178 register_chrdev_region(timerdev.devid, TIMER_CNT,
TIMER_NAME);
179 } else { /* 没有定义设备号 */
180 alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT,
TIMER_NAME);
181 timerdev.major = MAJOR(timerdev.devid); /* 获取主设备号 */
182 timerdev.minor = MINOR(timerdev.devid); /* 获取次设备号 */
183 }
184
185 /* 2、初始化 cdev */
186 timerdev.cdev.owner = THIS_MODULE;
187 cdev_init(&timerdev.cdev, &timer_fops);
188
189 /* 3、添加一个 cdev */
190 cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
191
192 /* 4、创建类 */
193 timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
194 if (IS_ERR(timerdev.class)) {
195 return PTR_ERR(timerdev.class);
196 }
197
198 /* 5、创建设备 */
199 timerdev.device = device_create(timerdev.class, NULL,
timerdev.devid, NULL, TIMER_NAME);
200 if (IS_ERR(timerdev.device)) {
201 return PTR_ERR(timerdev.device);
202 }
203
204 /* 6、初始化 timer,设置定时器处理函数,还未设置周期,所有不会激活定时器 */
205 init_timer(&timerdev.timer);
206 timerdev.timer.function = timer_function;
207 timerdev.timer.data = (unsigned long)&timerdev;
208 return 0;
209 }
第 169~209 行,函数 timer_init,驱动入口函数。在第 205~207 行初始化定时器,设置定时器的定时处理函数为 timer_function,另外设置要传递给 timer_function 函数的参数为 timerdev的地址。在此函数中并没有调用 timer_add 函数来开启定时器,因此定时器默认是关闭的,除非应用程序发送打开命令。
216 static void __exit timer_exit(void)
217 {
218
219 gpio_set_value(timerdev.led_gpio, 1); /* 卸载驱动的时候关闭 LED */
220 del_timer_sync(&timerdev.timer); /* 删除 timer */
221 #if 0
222 del_timer(&timerdev.tiemr);
223 #endif
224
225 /* 注销字符设备驱动 */
226 cdev_del(&timerdev.cdev); /* 删除 cdev */
227 unregister_chrdev_region(timerdev.devid, TIMER_CNT);
228
229 device_destroy(timerdev.class, timerdev.devid);
230 class_destroy(timerdev.class);
231 }
驱动出口函数,在 219 行关闭 LED,也就是卸载驱动以后 LED 处于熄灭状态。第 220 行调用 del_timer_sync 函数删除定时器,也可以使用 del_timer 函数。
3.编写测试APP
21 /* 命令值 */
22 #define CLOSE_CMD (_IO(0XEF, 0x1)) /* 关闭定时器 */
23 #define OPEN_CMD (_IO(0XEF, 0x2)) /* 打开定时器 */
24 #define SETPERIOD_CMD (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */
25
26 /*
27 * @description : main 主程序
28 * @param - argc : argv 数组元素个数
29 * @param - argv : 具体参数
30 * @return : 0 成功;其他 失败
31 */
32 int main(int argc, char *argv[])
33 {
34 int fd, ret;
35 char *filename;
36 unsigned int cmd;
37 unsigned int arg;
38 unsigned char str[100];
39
40 if (argc != 2) {
41 printf("Error Usage!\r\n");
42 return -1;
43 }
44
45 filename = argv[1];
46
47 fd = open(filename, O_RDWR);
48 if (fd < 0) {
49 printf("Can't open file %s\r\n", filename);
50 return -1;
51 }
52
53 while (1) {
54 printf("Input CMD:");
55 ret = scanf("%d", &cmd);
56 if (ret != 1) { /* 参数输入错误 */
57 gets(str); /* 防止卡死 */
58 }
59
60 if(cmd == 1) /* 关闭 LED 灯 */
61 cmd = CLOSE_CMD;
62 else if(cmd == 2) /* 打开 LED 灯 */
63 cmd = OPEN_CMD;
64 else if(cmd == 3) {
65 cmd = SETPERIOD_CMD; /* 设置周期值 */
66 printf("Input Timer Period:");
67 ret = scanf("%d", &arg);
68 if (ret != 1) { /* 参数输入错误 */
69 gets(str); /* 防止卡死 */
70 }
71 }
72 ioctl(fd, cmd, arg); /* 控制定时器的打开和关闭 */
73 }
74 close(fd);
75 }
①、运行 APP 以后提示我们输入要测试的命令,输入 1 表示关闭定时器、输入 2 表示打开定时器,输入 3 设置定时器周期。
②、如果要设置定时器周期的话,需要让用户输入要设置的周期值,单位为毫秒。
第 22~24 行,命令值。
第 53~73 行, while(1)循环,让用户输入要测试的命令,然后通过第 72 行的 ioctl 函数发送给驱动程序。如果是设置定时器周期命令 SETPERIOD_CMD,那么 ioctl 函数的 arg 参数就是用户输入的周期值。
四、运行测试
1.编译驱动程序和测试AP
编译成功以后就会生成一个名为“ timer.ko”的驱动模块文件。
编译成功以后就会生成 timerApp 这个应用程序。
2.运行测试
根据提示输入指令及周期,观察LED的交替亮变化即可。
五、总结
本节笔记主要学习Linux内核定时器的驱动开发,主要包括内核时间管理和定时器、硬件原理图分析【LED】、驱动开发和测试。最重要的内容为定时器驱动开发部分。
本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。
相关文章:

Linux学习第21天:Linux内核定时器驱动开发: 流淌的时间长河
Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 在人类的发展进化中,时间是一个非常重要神秘的物质量。任何事物都是在时间的长河中流淌发生、发展、变化。我们进行驱动开发中对时间的定义和使用也是…...

Centos服务在服务器重启后自启
以Dolphin为例 打开rc.local文件以编辑: sudo vi /etc/rc.d/rc.local在文件中添加您的启动命令。在您的情况下,要添加的命令如下: sh /opt/dolphinscheduler/zookeeper/bin/zkServer.sh start sh /opt/dolphinscheduler/dolphinscheduler/…...

慢性疼痛治疗服务公司Kindly MD申请700万美元纳斯达克IPO上市
来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,慢性疼痛治疗服务公司Kindly MD近期已向美国证券交易委员会(SEC)提交招股书,申请在纳斯达克IPO上市,股票代码为(KDLY),Kindly MD计划通过…...

代码随想录 Day6 哈希 LeetcodeT454 四数之和II T383赎金信 T15 三数之和 T18 四数之和
本文代码思路来源于: 代码随想录 前言 希望大家在刷这部分题的时候先熟悉熟悉哈希结构的基本常用api,比较方便理解. LeetCode T454 四数之和 题目链接:454. 四数相加 II - 力扣(LeetCode) 题目思路 暴力解法仍然是遍历四个数组解决此题, 哈希的思路有…...

干货速来|教你如何撰写毕业论文
撰写毕业论文对于正常大学毕业至关重要。毕业论文是对学生在大学期间所学知识的综合运用和深入研究的体现,也是对学术能力和独立思考能力的考验。 撰写毕业论文的过程需要学生投入大量的时间和精力,包括选题、文献综述、研究方法选择、数据收集和分析、…...

【ROS 2】-2 话题通信
飞书原文链接: Docs...

Unity之NetCode多人网络游戏联机对战教程(2)--简单实现联机
文章目录 1.添加基本组件2.创建NetworkManager组件3.创建Player4.创建地面5.创建GameManager6.编译运行7. 测试联机后话 1.添加基本组件 NetworkManagerPlayerScene 2.创建NetworkManager组件 创建一个空物体,命名为NetworkManager 选择刚刚创建的NetworkManager…...
makdown文法
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...

新手程序员怎么接单?
程序员如何在自己年富力强的时候,最大化发挥自己的能力?将超能力转化为“钞能力”? 有人还在苦哈哈当老黄牛,一身使不完的牛劲,有人已经另辟蹊径,开创了自己的一片致富小天地。 接单找兼职,就…...

接口测试——接口协议抓包分析与mock_L2
目录: 抓包工具charles抓包工具fiddler抓包工具证书配置app抓包实战练习接口测试实战练习 1.抓包工具charles 工具介绍 支持 SSL 代理支持流量控制支持重发网络请求,方便后端调试支持修改网络请求参数支持网络请求的截获并动态修改可以自动将 json 或…...

Seata入门系列【1】安装seata 1.7.1+nacos 2.1.1
1 介绍 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 Github: https://github.com/seata/seata 官方文档:h…...

2023年职业院校技能大赛中职组----大数据应用与服务赛项任务书试题
2023年职业院校技能大赛中职组----大数据应用与服务赛项任务书试题 模块一:数据库系统运维(25分)任务一:数据库系统搭建(10分)任务二:房源数据库系统运维(15分) 模块二&a…...

产品经理的职业前景怎么样?一文为你全面解答!
随着科技的迅速发展和市场竞争的日益激烈,产品经理这个职业变得越来越炙手可热。产品经理负责一款产品的全生命周期管理,从需求收集到设计、开发、测试、发布,再到市场推广和用户反馈,都需要产品经理参与决策。因此,这…...

【深度学习】图像去噪(2)——常见网络学习
【深度学习】图像去噪 是在 【深度学习】计算机视觉 系列文章的基础上,再次针对深度学习(尤其是图像去噪方面)的基础知识有更深入学习和巩固。 1 DnCNN 1.1 网络结构 1.1.1 残差学习 1.1.2 Batch Normalization (BN) 1.1.2.1 背景和目标…...

八大排序详解
目录 1.排序的概念及应用 1.1 排序的概念 1.2 排序的应用 1.3 常见的排序算法 2.常见排序算法的实现 2.1 直接插入排序 2.1.1 基本思想 2.1.2 动图解析 2.1.3 排序步骤(默认升序) 2.1.4 代码实现 2.1.5 特性总结 2.2 希尔排序 2.2.1 基本思…...

自定义热加载:如何不停机实现核心代码更新
文章目录 1. 常见的几种实现代码热更新的几种方式对于开发环境我们可以使用部署环境1. 使用 Arthas 的 redefine 命令来加载新的 class 文件2. 利用 URLClassLoader 动态加载3. 通过Java的Instrumentation API 也是可以实现的 2. 实现1. ClassScanner扫描目录和加载类2. 定时任…...

Spring Cloud Alibaba Nacos 2.2.3 (2) - 单机版启动 (winodows 和 linux )
Nacos 2.2.3 (1) - 下载与数据库配置 参考下载与数据库配置 启动服务器 执行 nacos-server-2.2.3\bin 下的startup.sh或者startup.cmd (根据不同系统) windows 下nacos 单机启动 方式一: 1,打开cmd 2,cd 到nacos-s…...

VB从资源文件中播放wav音乐文件
Private Const SND_SYNC &H0 Private Const SND_MEMORY &H4 API函数 Private Declare Function sndPlaySoundFromMemory Lib "winmm.dll" Alias "sndPlaySoundA" (lpszSoundName As Any, ByVal uFlags As Long) As Long 音乐效果请“单击” Pr…...

web:[HCTF 2018]WarmUp
题目 点进页面,页面只有一张滑稽脸,没有其他的提示信息 查看网页源代码,发现source.php,尝试访问一下 跳转至该页面,页面显示为一段php代码,需要进行代码审计 <?phphighlight_file(__FILE__);class emm…...

程序开发常用在线工具汇总
菜鸟工具# https://c.runoob.com/ 编码# ASCII码# https://www.habaijian.com/ 在线转换# https://www.107000.com/T-Ascii/http://www.ab126.com/goju/1711.html Base64# 在线转换# https://www.qqxiuzi.cn/bianma/base64.htmhttp://www.mxcz.net/tools/Unicode.aspx …...

crypto:丢失的MD5
题目 得到一个md5.py 运行一下,发现报错,修改一下 运行之后又报错 报错原因是算法之前编码 正确的代码为 import hashlib for i in range(32,127):for j in range(32,127):for k in range(32,127):mhashlib.md5()m.update((TASC chr(i) O3RJMV c…...

气传导和骨传导耳机哪个好?气传导耳机好用吗?气传导耳机推荐
气传导和骨传导耳机都是不入耳设计,骨传导是通过振动颅骨传达声音信号 骨传导耳机是一种能够通过振动颅骨来传达声音信号的耳机,其原理是利用骨传导技术,将声音信号通过颅骨传达到内耳,从而实现听觉效果,不过长时间佩…...

Spring 的代理开发设计
目录 编辑一、静态代理设计模式 1、为什么需要代理设计模式 2、代理设计模式 (1)概念 (2)名词解释 (3)代理开发的核心要素 (4)编码 (5)静态代理存在…...

实现注册手机号用户
1、使用Post异步发送请求(发送短信),离焦事件触发时判断 <script src"layer/layer.js"></script><!--离焦事件--><script type"text/javascript" th:inline"javascript">$("#use…...

【2023年11月第四版教材】第15章《风险管理》(第三部分)
第15章《风险管理》(第三部分) 5 过程1-规划风险管理6 过程2-识别风险6.1 识别风险★★★6.2 数据收集★★★6.3 数据分析★★★ 7 过程3-实施定性风险分析7.1 实施定性风险分析7.2 数据分析★★★7.3 数据表现★★★7.4 项目文件(更新&#…...

datart导入hive连接包
datart读取hive数据时,需要先在datart的lib目录下导入hive jdbc相关的包,这里面有几个坑记录下: 1.和springboot中commons-lang3冲突 2.hive中带的jetty和springboot冲突 3.hive jdbc的包的版本号一定要小于登录hive服务端的版本ÿ…...

2023美团秋招一面面经-已过
批处理批处理一个sql下的若干条sql,如何提高速度,如果要分片的话如何分片 1.使用数据库的批处理功能来执行多个 SQL 语句。这可以减少每个 SQL 语句的通信开销。JDBC 中的 addBatch() 和 executeBatch() 方法可以用来执行批处理操作。 在程序开始时候设…...

ARM Day2
目录 实现1-100的累加 思维导图 实现1-100的累加 .text .globl _start _start:mov r1,#0x64mov r2,#0x1mov r4,#0x1 going:cmp r1,r4bcc endleaddcs r3,r3,r4add r4,r4,r2b going endle:stop:b stop .end思维导图...

手把手教你制作独特优惠促销微传单
您是否曾经想要为自己的业务或活动制作一张吸引人的微传单?以下是一份简单易懂的微传单制作教程,帮助您在短短四步内打造出精美的宣传海报。 1. 登录乔拓云,点击【微传单】 首先,打开乔拓云网站并点击【微传单】选项。您将进入一个…...

Qt-QImage-convertTo-copy-convertToFormat-格式转换
文章目录 1.copy2.convertToFormat3.QPainter4.总结 1.copy 深度复制图像格式数据,可以指定区域。 QImage copy(const QRect &rect QRect()) const;inline QImage copy(int x, int y, int w, int h) const{ return copy(QRect(x, y, w, h)); }2.convertToForm…...