裸机C编程:嵌入式系统C程序设计 pdf下载
isbn:9787111792017
限时特惠
00:00:00
活动结束后恢复原价
纸质书参考价
¥8
电子版限时价
¥0.00
省 8 元
选择版本
内容简介
本篇主要提供裸机C编程:嵌入式系统C程序设计电子书的pdf版本下载,本电子书下载方式为百度网盘方式,点击以上按钮下单完成后即会通过邮件和网页的方式发货,有问题请联系邮箱ebook666@outlook.com
编辑推荐
本书由拥有 50 余年嵌入式系统编程经验的 Stephen Oualline 撰写,经电气 / 电子工程领域专家 Frank Duignan 审校,专业性与权威性兼具。内容围绕嵌入式开发核心需求展开,既系统讲解嵌入式系统基础知识、开发环境搭建,又深入剖析 C 语言在动态内存管理、中断控制、寄存器操作等场景的实战技巧,且全程以 STM32F030x4 处理器为实例,结合编译原理、链接器应用等底层逻辑,为不同阶段开发者提供从理论到实践的完整知识体系,是嵌入式 C 语言开发领域兼具深度与实用性的专业参考用书。
内容简介
本书是一部专注于嵌入式系统编程的指南,旨在帮助你深入探索嵌入式系统的奥秘,掌握C语言在嵌入式领域的应用技巧,从而在嵌入式开发领域取得成功。全书分为两部分:第一部分(第1~12章)介绍了嵌入式系统的基础知识和开发环境的搭建,包括安装必要的工具和软件、配置开发环境等内容,通过一步步的实践示例,带你快速入门嵌入式系统编程,了解嵌入式程序的编写流程和基本技巧;第二部分(第13~18章)更深入地探讨了C语言在嵌入式系统中的高级应用,从动态内存管理、文件I/O操作、命令行参数处理到模块化编程等方面,全面介绍了C语言在嵌入式系统中的实际应用技巧和实践,帮助你进一步提升编程技能,应对更复杂的编程任务。
作者简介
斯蒂芬·欧林
(Steve Oualline)
在嵌入式领域编写可靠、低缺陷的系统程序已超过50年,拥有南加州大学的理学硕士学位,为O'Reilly和No Starch出版社撰写了多本书籍。他还是南加州铁路博物馆的志愿者,并为Acme交通信号灯调试计算机控制器。
目录
目 录
译者序
前言
关于作者
关于技术审校
第一部分 嵌入式编程
第1章 Hello World 3
1.1 安装GCC 3
1.2 下载STM32系统工作台 4
1.3 我们的第一个程序 4
1.3.1 编译程序 5
1.3.2 犯错 5
1.3.3 理解程序 6
1.3.4 添加注释 7
1.4 改进程序和构建流程 7
1.4.1 make 程序 8
1.4.2 编译器标志 9
1.5 编译器在幕后的工作方式 9
1.5.1 预处理器 10
1.5.2 编译器 10
1.5.3 汇编器 11
1.5.4 链接器 12
1.6 在Makefile中添加内容 13
1.7 总结 14
1.8 问题 15
第2章 集成开发环境介绍 16
2.1 使用STM32的System
Workbench 16
2.1.1 启动IDE 17
2.1.2 创建Hello World 18
2.1.3 调试程序 21
2.2 IDE为我们做了什么 24
2.3 导入本书的编程示例 25
2.4 总结 25
2.5 编程问题 26
2.6 其他问题 26
第3章 嵌入式系统编程 27
3.1 NUCLEO-F030R8开发板 27
3.1.1 对开发板进行编程和调试 28
3.1.2 配置开发板 28
3.2 建立嵌入式项目 30
3.3 你的第一个嵌入式程序 33
3.3.1 初始化硬件 33
3.3.2 GPIO引脚编程 34
3.3.3 切换LED 35
3.3.4 构建完成的程序 35
3.4 探索构建过程 36
3.5 探索项目文件 38
3.6 调试应用程序 39
3.7 逐步执行程序 41
3.8 总结 42
3.9 编程问题 43
3.10 其他问题 43
第4章 数字和变量 44
4.1 使用整数 44
4.1.1 声明保存整数的变量 45
4.1.2 给变量赋值 46
4.1.3 初始化变量 47
4.2 整数大小和表示 47
4.2.1 数字表示 49
4.2.2 标准整数 50
4.2.3 无符号整数类型 51
4.2.4 溢出 52
4.2.5 有符号整数类型中的补码
表示 54
4.3 缩写操作符 55
4.4 使用位操作控制内存映射的I/O
寄存器 56
4.4.1 或 56
4.4.2 与 57
4.4.3 非 58
4.4.4 异或 58
4.4.5 位移 59
4.5 定义位的含义 60
4.5.1 同时设置两个位的值 61
4.5.2 关闭一个位 61
4.5.3 检查位的值 62
4.6 总结 64
4.7 编程问题 64
第5章 决策和控制语句 65
5.1 if语句 65
5.2 if/else语句 67
5.3 循环语句 67
5.3.1 while循环 68
5.3.2 for循环 69
5.4 使用按钮 70
5.4.1 初始化 71
5.4.2 选择下拉电路 71
5.4.3 获取按钮的状态 73
5.4.4 运行程序 73
5.5 循环控制 74
5.5.1 break语句 74
5.5.2 continue语句 74
5.6 反模式 75
5.6.1 空的while循环 75
5.6.2 while中的赋值 76
5.7 总结 76
5.8 编程问题 77
第6章 数组、指针和字符串 78
6.1 数组 78
6.1.1 底层细节:指针 80
6.1.2 数组和指针算术 83
6.1.3 数组溢出 84
6.2 字符和字符串 86
6.3 总结 88
6.4 编程问题 88
第7章 局部变量和函数 89
7.1 局部变量 89
7.2 隐藏变量 90
7.3 函数 91
7.4 堆栈帧 92
7.5 递归 95
7.6 编程风格 96
7.7 总结 97
7.8 编程问题 97
第8章 复杂数据类型 98
8.1 枚举 98
8.2 预处理器技巧和枚举 99
8.3 结构体 101
8.3.1 内存中的结构体 103
8.3.2 访问未对齐的数据 104
8.3.3 结构体初始化 106
8.3.4 结构体赋值 107
8.3.5 结构体指针 108
8.3.6 结构体命名 109
8.4 联合体 110
8.5 创建自定义类型 111
8.6 结构体和嵌入式编程 113
8.7 typedef 114
8.7.1 函数指针和typedef 115
8.7.2 typedef和struct 116
8.8 总结 117
8.9 编程问题 117
第9章 STM上的串口输出 119
9.1 逐字符写入字符串 119
9.2 串行输出 121
9.2.1 串行通信简史 122
9.2.2 串行Hello World 123
9.2.3 UART初始化 124
9.2.4 发送一个字符 126
9.3 与设备通信 131
9.3.1 Windows 132
9.3.2 Linux和macOS 133
9.4 总结 133
9.5 编程问题 134
第10章 中断 135
10.1 轮询与中断 135
10.2 串行I/O的中断 136
10.3 中断例程 136
10.4 使用中断写入字符串 137
10.4.1 程序细节 140
10.4.2 中断问题 143
10.5 使用缓冲区来提高速度 144
10.5.1 发送函数 145
10.5.2 中断例程 146
10.5.3 完整程序 147
10.5.4 问题 149
10.6 总结 153
10.7 编程问题 153
第11章 链接器 154
11.1 链接器的作用 155
11.2 编译和链接内存模型 155
11.2.1 理想的C语言模型 155
11.2.2 非标准部分 159
11.3 链接过程 160
11.4 链接器定义的符号 161
11.5 重定位和链接目标文件 162
11.6 链接器映射文件 163
11.7 高级链接器的用法 164
11.7.1 用于“永久”存储的
闪存 164
11.7.2 多个配置项 171
11.7.3 定制示例 172
11.7.4 固件升级 172
11.8 总结 173
11.9 编程问题 173
第12章 预处理器 174
12.1 简单宏 174
12.1.1 参数化宏 176
12.1.2 代码宏 177
12.2 条件编译 180
12.3 符号的定义位置 181
12.3.1 命令行符号 182
12.3.2 预定义符号 182
12.4 包含文件 183
12.5 其他预处理器指令 183
12.6 预处理器技巧 184
12.7 总结 185
12.8 编程问题 185
第二部分 用于大型机器的
C语言编程
第13章 动态内存 189
13.1 基本堆分配和释放 189
13.2 链表 191
13.2.1 添加节点 192
13.2.2 打印链表 194
13.2.3 删除节点 195
13.2.4 整合所有内容 196
13.3 动态内存问题 198
13.4 Valgrind和GCC Address
Sanitizer 工具 199
13.5 总结 200
13.6 编程问题 201
第14章 缓冲文件I/O 202
14.1 printf 函数 202
14.1.1 编写ASCII表 203
14.1.2 写入预定义文件 204
14.2 读取数据 204
14.3 打开文件 206
14.4 二进制I/O 207
14.5 缓冲和刷新 209
14.6 关闭文件 211
14.7 总结 211
14.8 编程问题 211
第15章 命令行参数和原始
I/O 212
15.1 命令行参数 212
15.2 原始I/O 213
15.2.1 使用原始I/O 214
15.2.2 使用二进制模式 216
15.3 ioctl 217
15.4 总结 217
15.5 编程问题 218
第16章 浮点数 219
16.1 什么是浮点数 219
16.1.1 浮点类型 219
16.1.2 自动转换 220
16.2 浮点数的问题 220
16.2.1 舍入误差 220
16.2.2 精度位数 221
16.3 无穷大、NaN和次正规数 221
16.4 实现 223
16.5 替代方案 223
16.6 总结 226
16.7 编程问题 226
第17章 模块化编程 227
17.1 简单的模块 227
17.1.1 简单模块的问题 228
17.1.2 制作模块 231
17.2 好的模块有哪些特点 231
17.3 命名空间 232
17.4 库 232
17.4.1 ranlib和库链接 235
17.4.2 确定性与不确定性库 237
17.5 弱符号 237
17.6 总结 238
17.7 编程问题 239
第18章 后记 240
18.1 学会写作 240
18.2 学会阅读 240
18.3 合作与创造性借鉴 241
18.4 有用的开源工具 241
18.4.1 Cppcheck 241
18.4.2 Doxygen 241
18.4.3 Valgrind 242
18.4.4 SQLite 242
18.5 永不停止学习 242
附录 项目创建清单 243
译者序
前言
关于作者
关于技术审校
第一部分 嵌入式编程
第1章 Hello World 3
1.1 安装GCC 3
1.2 下载STM32系统工作台 4
1.3 我们的第一个程序 4
1.3.1 编译程序 5
1.3.2 犯错 5
1.3.3 理解程序 6
1.3.4 添加注释 7
1.4 改进程序和构建流程 7
1.4.1 make 程序 8
1.4.2 编译器标志 9
1.5 编译器在幕后的工作方式 9
1.5.1 预处理器 10
1.5.2 编译器 10
1.5.3 汇编器 11
1.5.4 链接器 12
1.6 在Makefile中添加内容 13
1.7 总结 14
1.8 问题 15
第2章 集成开发环境介绍 16
2.1 使用STM32的System
Workbench 16
2.1.1 启动IDE 17
2.1.2 创建Hello World 18
2.1.3 调试程序 21
2.2 IDE为我们做了什么 24
2.3 导入本书的编程示例 25
2.4 总结 25
2.5 编程问题 26
2.6 其他问题 26
第3章 嵌入式系统编程 27
3.1 NUCLEO-F030R8开发板 27
3.1.1 对开发板进行编程和调试 28
3.1.2 配置开发板 28
3.2 建立嵌入式项目 30
3.3 你的第一个嵌入式程序 33
3.3.1 初始化硬件 33
3.3.2 GPIO引脚编程 34
3.3.3 切换LED 35
3.3.4 构建完成的程序 35
3.4 探索构建过程 36
3.5 探索项目文件 38
3.6 调试应用程序 39
3.7 逐步执行程序 41
3.8 总结 42
3.9 编程问题 43
3.10 其他问题 43
第4章 数字和变量 44
4.1 使用整数 44
4.1.1 声明保存整数的变量 45
4.1.2 给变量赋值 46
4.1.3 初始化变量 47
4.2 整数大小和表示 47
4.2.1 数字表示 49
4.2.2 标准整数 50
4.2.3 无符号整数类型 51
4.2.4 溢出 52
4.2.5 有符号整数类型中的补码
表示 54
4.3 缩写操作符 55
4.4 使用位操作控制内存映射的I/O
寄存器 56
4.4.1 或 56
4.4.2 与 57
4.4.3 非 58
4.4.4 异或 58
4.4.5 位移 59
4.5 定义位的含义 60
4.5.1 同时设置两个位的值 61
4.5.2 关闭一个位 61
4.5.3 检查位的值 62
4.6 总结 64
4.7 编程问题 64
第5章 决策和控制语句 65
5.1 if语句 65
5.2 if/else语句 67
5.3 循环语句 67
5.3.1 while循环 68
5.3.2 for循环 69
5.4 使用按钮 70
5.4.1 初始化 71
5.4.2 选择下拉电路 71
5.4.3 获取按钮的状态 73
5.4.4 运行程序 73
5.5 循环控制 74
5.5.1 break语句 74
5.5.2 continue语句 74
5.6 反模式 75
5.6.1 空的while循环 75
5.6.2 while中的赋值 76
5.7 总结 76
5.8 编程问题 77
第6章 数组、指针和字符串 78
6.1 数组 78
6.1.1 底层细节:指针 80
6.1.2 数组和指针算术 83
6.1.3 数组溢出 84
6.2 字符和字符串 86
6.3 总结 88
6.4 编程问题 88
第7章 局部变量和函数 89
7.1 局部变量 89
7.2 隐藏变量 90
7.3 函数 91
7.4 堆栈帧 92
7.5 递归 95
7.6 编程风格 96
7.7 总结 97
7.8 编程问题 97
第8章 复杂数据类型 98
8.1 枚举 98
8.2 预处理器技巧和枚举 99
8.3 结构体 101
8.3.1 内存中的结构体 103
8.3.2 访问未对齐的数据 104
8.3.3 结构体初始化 106
8.3.4 结构体赋值 107
8.3.5 结构体指针 108
8.3.6 结构体命名 109
8.4 联合体 110
8.5 创建自定义类型 111
8.6 结构体和嵌入式编程 113
8.7 typedef 114
8.7.1 函数指针和typedef 115
8.7.2 typedef和struct 116
8.8 总结 117
8.9 编程问题 117
第9章 STM上的串口输出 119
9.1 逐字符写入字符串 119
9.2 串行输出 121
9.2.1 串行通信简史 122
9.2.2 串行Hello World 123
9.2.3 UART初始化 124
9.2.4 发送一个字符 126
9.3 与设备通信 131
9.3.1 Windows 132
9.3.2 Linux和macOS 133
9.4 总结 133
9.5 编程问题 134
第10章 中断 135
10.1 轮询与中断 135
10.2 串行I/O的中断 136
10.3 中断例程 136
10.4 使用中断写入字符串 137
10.4.1 程序细节 140
10.4.2 中断问题 143
10.5 使用缓冲区来提高速度 144
10.5.1 发送函数 145
10.5.2 中断例程 146
10.5.3 完整程序 147
10.5.4 问题 149
10.6 总结 153
10.7 编程问题 153
第11章 链接器 154
11.1 链接器的作用 155
11.2 编译和链接内存模型 155
11.2.1 理想的C语言模型 155
11.2.2 非标准部分 159
11.3 链接过程 160
11.4 链接器定义的符号 161
11.5 重定位和链接目标文件 162
11.6 链接器映射文件 163
11.7 高级链接器的用法 164
11.7.1 用于“永久”存储的
闪存 164
11.7.2 多个配置项 171
11.7.3 定制示例 172
11.7.4 固件升级 172
11.8 总结 173
11.9 编程问题 173
第12章 预处理器 174
12.1 简单宏 174
12.1.1 参数化宏 176
12.1.2 代码宏 177
12.2 条件编译 180
12.3 符号的定义位置 181
12.3.1 命令行符号 182
12.3.2 预定义符号 182
12.4 包含文件 183
12.5 其他预处理器指令 183
12.6 预处理器技巧 184
12.7 总结 185
12.8 编程问题 185
第二部分 用于大型机器的
C语言编程
第13章 动态内存 189
13.1 基本堆分配和释放 189
13.2 链表 191
13.2.1 添加节点 192
13.2.2 打印链表 194
13.2.3 删除节点 195
13.2.4 整合所有内容 196
13.3 动态内存问题 198
13.4 Valgrind和GCC Address
Sanitizer 工具 199
13.5 总结 200
13.6 编程问题 201
第14章 缓冲文件I/O 202
14.1 printf 函数 202
14.1.1 编写ASCII表 203
14.1.2 写入预定义文件 204
14.2 读取数据 204
14.3 打开文件 206
14.4 二进制I/O 207
14.5 缓冲和刷新 209
14.6 关闭文件 211
14.7 总结 211
14.8 编程问题 211
第15章 命令行参数和原始
I/O 212
15.1 命令行参数 212
15.2 原始I/O 213
15.2.1 使用原始I/O 214
15.2.2 使用二进制模式 216
15.3 ioctl 217
15.4 总结 217
15.5 编程问题 218
第16章 浮点数 219
16.1 什么是浮点数 219
16.1.1 浮点类型 219
16.1.2 自动转换 220
16.2 浮点数的问题 220
16.2.1 舍入误差 220
16.2.2 精度位数 221
16.3 无穷大、NaN和次正规数 221
16.4 实现 223
16.5 替代方案 223
16.6 总结 226
16.7 编程问题 226
第17章 模块化编程 227
17.1 简单的模块 227
17.1.1 简单模块的问题 228
17.1.2 制作模块 231
17.2 好的模块有哪些特点 231
17.3 命名空间 232
17.4 库 232
17.4.1 ranlib和库链接 235
17.4.2 确定性与不确定性库 237
17.5 弱符号 237
17.6 总结 238
17.7 编程问题 239
第18章 后记 240
18.1 学会写作 240
18.2 学会阅读 240
18.3 合作与创造性借鉴 241
18.4 有用的开源工具 241
18.4.1 Cppcheck 241
18.4.2 Doxygen 241
18.4.3 Valgrind 242
18.4.4 SQLite 242
18.5 永不停止学习 242
附录 项目创建清单 243
前言/序言
前 言
本书是为那些使用硬件的读者而写的,不适合那些购买现成计算机并从未了解过它们内部构造的读者阅读。本书所涉及的计算机甚至没有机箱。如果你想使用它,必须把某些东西连接到这块开发板的“裸金属”上。
本书会介绍嵌入式编程。嵌入式计算机是一种位于设备内部并且你几乎察觉不到它存在的计算机。它是运行你的车库门开启器、微波炉、汽车,甚至贺卡的设备。但在能实现任何功能之前,它需要被编程。本书介绍的就是如何对嵌入式系统进行编程。
为什么选择C语言?因为C语言让你对程序的执行有精确的控制,而其他语言,如C++,可能在背后做很多事情。考虑以下语句:
在C++中,这可能会调用一个类的赋值运算符函数,从而可能导致堆内存的分配和释放,并可能引发异常。这些细节暂时不重要,关键是你无法确切地知道会发生什么。
在C语言中,这个语句只是简单地将变量b的值赋给变量a,没有任何副作用;它只是一个赋值操作,没有其他附加操作。这个例子很简单,但是你会在本书中看到其他C语言的用法也都是按你所写的方式执行。
精确的控制很重要,因为我们使用C语言来编程一款基于STM32F030x4处理器(一种廉价的ARM Cortex-M0系统)的低端片上系统(SoC),该处理器具有8KB的RAM。在有限的RAM中进行内存管理非常重要,因此我们不能让高级语言(如C++)暗中操纵内存。另外,因为嵌入式系统没有操作系统,所以你需要直接告诉硬件该做什么。高级语言并不总是允许你与硬件交互,但C语言可以。
本书适合那些对计算机和硬件有基本了解,但在编程方面知识有限的读者阅读,也适合希望将新硬件连接到微控制器的硬件设计者,以及首次使用该硬件的人阅读,还适合对低级编程感兴趣,并希望充分利用38美分芯片的程序员阅读。
注意 ARM Cortex-M0因其低端产品的价格(写作本书时,10 000片的价格为38美分),在低端产品中非常流行。考虑到我们希望销售数百万的嵌入式系统,38美分和56美分芯片之间的差别是显著的。
为了充分发挥程序的性能,你需要了解底层的情况。本书不仅会教你如何编写程序,还会介绍你的程序如何被翻译成ARM芯片能够使用的机器代码。这对于最大化效率至关重要。例如,你将了解如果将程序从使用16位整数改为使用32位整数,会对性能带来多大影响。令人惊讶的是,32位整数更有效率,也更快速(32位是ARM的自然数大小,如果强制进行16位算术,它会执行32位计算,然后丢弃16位)。
为了编程和调试ARM芯片,你需要一些额外的工具:闪存编程器(用于将代码加载到设备)、USB串口转换器(因为我们使用串行线进行调试)和JTAG调试器。由于几乎所有开发者都需要这种组合工具,因此STMicroelectronics推出了一个名为NUCLEO-F030R8的板卡,提供了你所需的所有硬件。你还需要一根迷你USB线(与手机不兼容的那种),这样你就可以将板卡连接到计算机。
你的第一个任务是订购一块NUCLEO-F030R8开发板。然后开始阅读第1章。收到板卡后,你就可以准备开始后续的学习了。
本书是为那些使用硬件的读者而写的,不适合那些购买现成计算机并从未了解过它们内部构造的读者阅读。本书所涉及的计算机甚至没有机箱。如果你想使用它,必须把某些东西连接到这块开发板的“裸金属”上。
本书会介绍嵌入式编程。嵌入式计算机是一种位于设备内部并且你几乎察觉不到它存在的计算机。它是运行你的车库门开启器、微波炉、汽车,甚至贺卡的设备。但在能实现任何功能之前,它需要被编程。本书介绍的就是如何对嵌入式系统进行编程。
为什么选择C语言?因为C语言让你对程序的执行有精确的控制,而其他语言,如C++,可能在背后做很多事情。考虑以下语句:
在C++中,这可能会调用一个类的赋值运算符函数,从而可能导致堆内存的分配和释放,并可能引发异常。这些细节暂时不重要,关键是你无法确切地知道会发生什么。
在C语言中,这个语句只是简单地将变量b的值赋给变量a,没有任何副作用;它只是一个赋值操作,没有其他附加操作。这个例子很简单,但是你会在本书中看到其他C语言的用法也都是按你所写的方式执行。
精确的控制很重要,因为我们使用C语言来编程一款基于STM32F030x4处理器(一种廉价的ARM Cortex-M0系统)的低端片上系统(SoC),该处理器具有8KB的RAM。在有限的RAM中进行内存管理非常重要,因此我们不能让高级语言(如C++)暗中操纵内存。另外,因为嵌入式系统没有操作系统,所以你需要直接告诉硬件该做什么。高级语言并不总是允许你与硬件交互,但C语言可以。
本书适合那些对计算机和硬件有基本了解,但在编程方面知识有限的读者阅读,也适合希望将新硬件连接到微控制器的硬件设计者,以及首次使用该硬件的人阅读,还适合对低级编程感兴趣,并希望充分利用38美分芯片的程序员阅读。
注意 ARM Cortex-M0因其低端产品的价格(写作本书时,10 000片的价格为38美分),在低端产品中非常流行。考虑到我们希望销售数百万的嵌入式系统,38美分和56美分芯片之间的差别是显著的。
为了充分发挥程序的性能,你需要了解底层的情况。本书不仅会教你如何编写程序,还会介绍你的程序如何被翻译成ARM芯片能够使用的机器代码。这对于最大化效率至关重要。例如,你将了解如果将程序从使用16位整数改为使用32位整数,会对性能带来多大影响。令人惊讶的是,32位整数更有效率,也更快速(32位是ARM的自然数大小,如果强制进行16位算术,它会执行32位计算,然后丢弃16位)。
为了编程和调试ARM芯片,你需要一些额外的工具:闪存编程器(用于将代码加载到设备)、USB串口转换器(因为我们使用串行线进行调试)和JTAG调试器。由于几乎所有开发者都需要这种组合工具,因此STMicroelectronics推出了一个名为NUCLEO-F030R8的板卡,提供了你所需的所有硬件。你还需要一根迷你USB线(与手机不兼容的那种),这样你就可以将板卡连接到计算机。
你的第一个任务是订购一块NUCLEO-F030R8开发板。然后开始阅读第1章。收到板卡后,你就可以准备开始后续的学习了。