嵌入式C编程实战

978-7-115-42987-2
作者: 【瑞典】Lars Bengtsson(本特松) Lennart Lindh(林德)
译者: 李华峰
编辑: 胡俊英陈冀康

图书目录:

详情

本书介绍了一些底层的C语言编程,非常适用于一般的嵌入式设计人员学习。通过阅读本书,能够帮助嵌入式项目的设计人员更好地开展工作。当然,本书也可以作为C编程入门的一本初学教程,非常适合初学者阅读。相关的硬件设计实例还可以在网上下载到,非常有利于读者的参考学习。

图书摘要

版权信息

书名:嵌入式C编程实战

ISBN:978-7-115-42987-2

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [瑞典] Lars Bengtsson Lennart Lindh

  译    李华峰

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Simplified Chinese translation copyright © 2016 by Posts and Telecommunications Press

ALL RIGHTS RESERVED

Low Level C Programming of Microcontrollers

Copyright © 2015 by Lars Bengsson and Lennart Lindh

本书中文简体版由作者Lars Bengsson和Lennart Lindh授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


嵌入式系统一般指非PC系统,它包括硬件和软件两部分。嵌入式产品应用领域极为广泛,并且具备非常好的发展前景。

本书致力于使用微控制器来设计嵌入式系统,全书包含8章内容,介绍了嵌入式软件设计基础理论,还提供了多个C语言的编程示例。通过理论与实践相结合的方式,本书可以帮助读者快速掌握嵌入式开发的核心技巧。

本书适合嵌入式开发人员以及想要学习嵌入式C语言开发的学生和普通程序员阅读。


李华峰,毕业于东北师范大学,目前在河北省一所高校任教,也是《精通Metasploit渗透测试》一书的译者。他自幼便是一个狂热的黑客技术追求者,现在主要从事网络安全与渗透测试方面的教育工作。作为一名资深的计算机安全领域的研究人员,研究的主要方向为网络扫描技术、漏洞检测、入侵检测等。读者可以通过lihuafeng1999@163.com联系到译者。


本书是为致力于使用微控制器来设计嵌入式系统的人们准备的。本书将翔实的理论与真实的实践相结合。现在由微控制器管理的嵌入式系统无处不在,汽车、机器人、办公室、医疗设备、机械、家居,以及其他各种各样的需求中都可以看到它们的身影。一个典型的微控制器包括了处理器、存储器和外部设备。一个嵌入式系统C开发者需要对微控制器的体系结构和软件开发中的底层操作有一个深入的了解。

本书的主要内容是介绍嵌入式软件的设计,通过真实的案例,使读者“学会如何去做”。读者通过书中提供的实验,可以快速地做到理论与实践相结合。

开始学习本书前,需做如下工作:

(1)下载所需的工具。当然,这些都是免费的(通过http://lowlevelcprogramming. agstu.com/查看网页上的说明)。

(2)购买一块BeMicro Max10开发板,价格在30美元左右。

书中的案例将会涉及多种不同的系统架构(微控制器),从最简单的架构到使用了大量设备的架构,再到使用了管道流水线技术的CPU的复杂架构。

图1 本书将要学习到的两个世界,硬件(微处理器)和软件

如果出于学习和教育的目的,可以免费下载和使用一些专业的软件工具。软件的设计是在工具中完成的,然后将完成的可执行代码下载到RAM中。另外,Alteras FPGA开发板(现场可编程阵列)是一个可编程的硬件设备。

图2 感谢Alteras的帮助,以及他们提供的优秀开发工具


图1.1为两台不同的计算机。

图1.1 两台计算机

大部分人看到图1.1后,立刻就可以认出左边的机器是一台计算机。不过,几乎没有人会把右侧的机器看作是计算机,事实上,它确实是一台货真价实的计算机。这两台看起来截然不同的计算机最根本的区别在于,右侧的计算机是嵌入在洗衣机内部的。

如果你认识到这两者的差异,就可以将计算机分成通用计算机和嵌入式计算机两类。

1.通用计算机

通用计算机指的是台式机、笔记本电脑以及工作站等。通用计算机(几乎)总是配有键盘、显示设备、硬盘驱动器,以及用来连接互联网的以太网卡这些设备。这些通用计算机都进行了优化,以便运行Windows或者Mac OS之类的(非实时的)操作系统。

2.嵌入式计算机

嵌入式计算机也可以拥有上述的这些设备,但通常他们与此不同。嵌入式计算机要被安装在被控系统的内部。设计嵌入式计算机的目的是用来专门完成一个(或者几个)任务。图1.1中右边的计算机就是被设计用来实现多个不同的洗衣程序。因此,这台计算机上不是用来运行Windows操作系统的,更不可能被用来玩魔兽世界了。但这并不能改变它是一台计算机的事实,它有着一颗计算机的“心”,这颗“心”由CPU、存储器和一些输入/输出单元构成。

由于嵌入式计算机是具有特定功能的专用计算机,所以当和通用计算机进行参数方面的比较时,嵌入式计算机显得弱的可怜。无论是兆赫(Mega Hertz,MHz)、内存空间(以GB计)、每秒百万个浮点操作(Million Floating-point Operations per Second,MFLOPs),或者数据传输速率(以Gbit/s计)这些指标的哪一项,两者都无法相提并论。嵌入式计算机在成本和功耗方面都进行了优化。谁都不会需要使用一个主频高达5GHz的英特尔酷睿处理器和一个存储容量达到5TB的硬盘组成的通用计算机来控制洗衣机,因为这样做意味着巨大的浪费(费用和能源上的)。通用系统和嵌入式系统的关系如图1.2所示。

图1.2 通用系统和嵌入式系统的关系

不过,无论是通用计算机,还是嵌入式计算机,都具有相同的工作原理,它们都是计算机,两者之间的差异仅在于“规模”。

我们很难给出一个让所有人都能满意的关于嵌入式系统的定义。但是,我们对于这个系统定义现在已经取得了一些共识。下面列出的这些应用就是常见的嵌入式系统。

下面列出的系统都不能归类到嵌入式系统。

本书对嵌入式系统的定义如下:

嵌入式系统指的是专门的软件、硬件和接口的集合,目的是用来执行特定的任务(如运行一台洗衣机等)。

当对嵌入式系统和通用系统计算机的产品数量进行比较时,通用系统计算机在市场上占有的比例还不到计算机总量的0.5%。一个普通的家庭(这里指的是西方国家)通常会拥有30个以上的嵌入式计算机和两个通用型计算机。单是一个现代化的汽车,就包含了40~50个嵌入式计算机(发动机控制、温度控制、燃油喷射系统、安全气囊控制、座椅加热、测量燃油液位、速度、转速、雨水传感器等)。

图1.3给出了一个嵌入式计算机系统组件的实例。

图1.3 嵌入式系统可以包括微处理器以及许多其他组件

大多数情况下,一个嵌入式计算机系统并不会包含大型的组件,取而代之的是大量的小型组件。

下面列出了一些这种类型的组件。

与此相比,典型的通用计算机硬件组成部分为硬盘驱动器、以太网接口、显示设备、键盘以及鼠标。

作者点评:时至今日,已经无法在通用计算机和嵌入式计算机之间划出一条明确的界限了。打个比方,你能准确地告诉我iPad或者智能手机是通用计算机,还是嵌入式计算机吗?

下面是一些常用的术语:

美国国家标准协会C标准(American National Standards Institute C,ANSI C)

ANSIC是美国国家标准协会对C语言标准制定的规范。

应用程序编程接口(Application Programming Interface,API)

API是由操作系统或者库文件提供的源代码接口,可以被调用,以此支持一些计算机程序发起的服务请求。

中央处理单元(Central Processing Unit,CPU)

中央处理单元可以是任何一个符合如下定义的硬件。首先,它必须能够处理数据;其次,它必须能控制必要的资源对这些数据进行处理。例如,对两个数求和,CPU就需要读取这两个数,然后将它们添加到一个处理电路(如ALU)中,最后将得到的结果写回。

GNU编译器套件(GNU Compiler Collection,GCC)

由GNU开发的编程语言编译器集合,可以用来处理多种编程语言。

GNU并不等同于UNIX(GNU is Not UNIX,GNU)

GNU是一个完全由自由软件构成的计算机操作系统。开发GNU的项目被称作GNU计划。

图形用户界面(Graphical User Interface,GUI)

一种用户界面,通过它可以实现人类与计算机之间以图形化的图标方式进行交互。

人机界面(Human Machine Interface,HMI)

人机界面用来实现人与机器之间的互动。

微处理器(MicroProcessor,μP)

微处理器是一种通用设备,它包含着ALU,也就是算术逻辑单元和控制单元。微处理器通常都是集成在单个芯片上的独立设备,相比微控制器,它包含了更多的资源,并用来完成复杂运算。

微控制器(Microcontroller,μC)

微控制器拥有一套完整计算机系统“集成”的所有必须单元。微控制器可以被称为“单芯片解决方案”。它通常包括了中央处理单元(CPU)、RAM、EPROM/PROM/ROM、I/O外设、定时器、中断控制器。微控制器可以被认为是一种特殊形式的更通用的微处理器。它专门用来控制运算复杂度较小的应用程序。

通用串行总线(Universal Serial Bus,USB)

通用串行总线用来实现和外部设备通信的通用串行总线标准的接口技术。

图1.4所示的应用程序和平台是一个嵌入式计算机系统的两个组成部分。平台被设计用来帮助应用程序完成真正的任务。一个平台可以是硬件和操作系统的组合,而应用程序则是一个软件。例如,用来控制工业机器人的软件。通常,一些很小的系统中是不需要操作系统的。事实上,支持应用程序的平台和项目的管理之间存在着联系,一些项目中已经证实了这种联系的重要性。这意味着如果有一个可以简化应用程序实现的平台,那么就无需再为项目管理付出高昂的代价了。

图1.4 操作系统+硬件=平台,平台+应用程序=嵌入式计算机系统

性能:如果一个系统不能满足它的性能需求,就是一个毫无用处的系统。性能参数通常指的是功能、速度、成本、按时完成工作的能力(实时性能)和/或电力消耗。

功耗:功耗是一个十分重要的指标,这主要出于以下几个原因:

安全:很多的嵌入式系统,如医疗方面或者汽车方面的应用程序,都对安全性方面有很高的要求。对于这种类型的应用,需要在设计的过程中就进行广泛的和可靠的验证和测试,以保证系统能够按照预期正常运行。有些关键的安全系统,如汽车的安全气囊系统或者支持生命的呼吸系统,则需要设计为冗余系统,这样在当前系统发生故障时,备份的辅助系统就会接管任务(并激活警报)。

灵活性:在一些情况下,系统的实现需要灵活性,即可以对系统进行修改,以适应标准或者功能需求的变化。

正如我们所见,问题的复杂度正在不断地增加,我们必须对此做出应对。可是,关键是怎样才能降低复杂度呢?本节将会就这个问题给出一些基本的答案。

下面给出了一些“与降低复杂度相关的词汇”。

本书在以下部分定义了最小的组件,有时这些组件也被称为叶组件。这些组件包括逻辑门、触发器、存储单元(逻辑组件)。其实,还可以将这些组件再分成更小的部分,如晶体管、电阻、电感和电容等最小的不可分割的组成。这些逻辑组件是总线、CPU的组成部分。而总线和CPU又是构成多处理器系统的组成部分。

组件的复杂性可以通过抽象层的数目和元件的数量来确定。使用抽象层的目的是为了通过隐藏子组件,来降低系统的复杂性。

本书的以下部分定义了不可再分的元件,或者有时也称为叶元件,如门电路、触发器、存储器(逻辑部件)。其实,可以对一些更小的元件进行研究,如晶体管、电阻、电感和电容。

这些逻辑部件共同构成了总线和CPU,而总线和CPU又共同构成了多处理器系统。

一个组件的复杂性可以由它所包含的元件的数量和抽象层次的数目决定。抽象层次通过隐藏组件降低复杂性。

软件组件的抽象层次。

较高的抽象层次往往被描述为工艺无关。例如,一个C程序可以被编译成各种不同的CPU所能执行的程序,硬件的VHDL可以被用来对各种不同类型的FPGA进行开发。然而,较高的抽象层次也并非总都是工艺无关的,一个较高的抽象层次的组件对处理器就是工艺相关的。

结构用来描述各个组成部分的组件,以及各个组件之间的关系。嵌入式系统的软件和硬件都有自己的体系结构,如图1.5所示。

图1.5 硬件体系结构框图

这里有一个关于结构的经典法则指出:任何一个人类使用的设备都不应该包含超过72以上的组成部分。但是,一个计算机却可以同时控制大量(数以百万计)的组件。

现在创建一个结构示意图,如图1.6所示。结构中的每一个分支都在延续,直到一个不再可分的节点,这些组节点有时也被称作叶节点。一个组件是一个分支或者是一个不可分的叶结点。一个叶组件可以是一段没有调用任何其他函数的C代码,或者是没有子组件的VHDL代码。抽象层顶部的部分比底部复杂。

图1.6 抽象层次

本书中使用的所有案例都是基于灵活性极大的FPGA设备上完成的。如果一个设计师使用单片机工作时,他仅仅能完成对硬件架构的测试。但是,如果选用了FPGA,他将能通过设置I/O来实现对所有的从简单到复杂的多处理器体系结构的实验设计。因此,FPGA是最适合进行应用设计教育和学习的平台。

从逻辑上讲,这些FPGA设备可以按照设计师所设想的任何方式进行连接。图1.7显示了FPGA设备的灵活性。左边是一个简单的设计,右边是一个使用了两个32位CPU的复杂设计。

图1.7 FPGA上的一款简单设计和一款复杂设计

可以通过http://www.youtube.com/watch?v=gUsHwi4M4xE访问Dave Jones的EEV博客,看到他发布在YouTube上的视频(视频的前15分钟都是关于FPGA的),了解FPGA的知识。

本书将不会对FPGA进行更深入的讨论。

软件可以使用多种方式来描述。软件的一种特性就是串行性(顺序执行),但是,CPU的工作速度很快,因此设计师会认为多个程序是可以并行执行的。这是使用处理硬件组件的相同方式来处理软件的关键。

软件中的组件包含任务、线程、过程、函数、设备驱动程序等。

图1.8中说明了如果一个设计者在C文件中包含了“#include <stdio.h>”这条语句就可以使用stdio这个库文件。检查你的软件工具支持的各种标准库文件是一个好主意。

图1.8 <stdio.h>库文件中的一部分组件/函数

一个使用C语言的简单组件(函数)的例子;“void print_pix(alt_u16 x, alt_u16 y,alt_u8 rgb)”,“Print_pix”在坐标为(x,y)上的一个像素点写入了一个“RGB”(红绿蓝)颜色。

软件和硬件如同两个不同的世界,为了管理它们之间的接口,须使用软件设备驱动来控制软件,使用寄存器来控制硬件。这里有很多的用于软件和硬件进行通信的标准协议,下面给出了一些实例。

如图1.9所示,接口用来连接不同的组件,它包括各种软件和硬件。这种类型的软件叫做设备驱动程序。

图1.9 硬件和软件系统的逻辑架构

各种不同设备的驱动程序必须被添加到项目中。图1.10中给出了UART的设备驱动程序被添加到项目后的样子。在Altera的软件开发工具中,所有的工艺相关部分都保存在板级支持包(BSP)中。应用程序软件是针对产品的。

图1.10 “BSP”(板级支持包)和驱动程序(Altera公司NIOS II)

现在有很多的软件开发语言,如C、C++、ADA、LISP等。软件的开发都遵循标准化的过程。程序(源代码)可以被编译成目标对象可以执行的代码,一些编译器会将代码转换为汇编程序。而链接器将程序与用到的库文件和其他一些东西组织在一起,而且所有的基地址都是绝对地址。链接器最后产生的可以被下载到目标系统并执行的机器代码,如图1.11所示。

图1.11 软件开发层

编译器是一种计算机软件,它的作用是将源程序转换为目标程序。通常,通过两个步骤,就可以将一个C语言编写的程序转换为目标程序。第一步,将C程序转换为汇编程序;第二步,将汇编转换为目标程序。目标程序不能直接载入到系统中,它还需要和其他目标代码文件链接到一起。

目标程序可能会需要与设备驱动程序和其他程序组织在一起,这一点需要由链接器来实现。编译器不会将符号地址转换为真实地址。因此,这里即使只有一个目标程序,也必须使用链接器来完成地址转换。

链接器允许将不同的模块组织成一个程序,而程序员使用链接器的历史可以追溯到1947年。

链接器的定义

链接器是一个程序,可以将目标程序模块组织在一起,形成一个可执行的程序。简单地说,就是它将多个文件组合到了一起。

目标程序是指一种包含了机器代码和信息的程序模块。这些模块最后需要链接器进行组装。

大多数的编程语言(如C)都允许独立地进行模块的编写。这样做简化了编程的工作,因为你可以将一个大型的程序分解为多个更小、更容易管理的部分。

最后,将这些模块组合在一起,这是链接器的工作。图1.12给出了程序链接的过程。

图1.12 在链接过程中,目标文件和静态库将组装成新的库文件或者可执行文件

elf=可执行链接格式。

互联网是一个无穷无尽的信息来源。本书中大多数章节的结尾部分都会推荐一些Youtube上的视频,这些视频与本章的主题有关。下面给出了几个推荐的视频。

编译器的工作原理:https://www.youtube.com/watch?v=Kd93n4_5UjM

API是什么?:https://www.youtube.com/watch?v=iGkTCObcOqM

SDK是什么?:https://www.youtube.com/watch?v=py5H8gKvw44

FPGA是什么?:http://www.youtube.com/watch?v=gUsHwi4M4xE


为了完成嵌入式系统的开发工作,需要一些硬件和软件。我们使用的软件通常被称为“IDE”,即集成开发环境。我们将使用Altera公司提供的Quartus II集成开发环境,这是一款授权软件。Altera公司同时还提供免费的网络版,可以在他们的网站上(https://www.altera.com/downloads/ download-center.html)下载这个版本。

为了完成整个设计流程,还需要将你的设计下载到一些硬件平台上。这里有很多硬件平台可供选择。本书讲解的所有实例使用的都是Arrow电子公司开发的一款廉价的硬件平台,这款BeMicro MAX 10开发板的价格大约只有30美元(详见第2.2节),这款开发板上集成了MAX 10 FPGA的可编程器件,体积只有一张信用卡那么大(http://parts.arrow.com/item/detail/arrow-development-tools/bemicromax10#RGMn)。

本书中的实验所需要的硬件设计文件可以在本书的主页(www.lowlevelcprogramming.agstu.com)上下载。

从Altera的主页下载了Quartus II软件后,得到的将不仅仅是一个IDE。

Quartus II IDE中还包含一个程序设计器,通过这个程序设计器,可以将我们的设计下载到FPGA。通常,可以通过下面的操作步骤来完成嵌入式系统开发:

(1)使用Qsys完成硬件设计;

(2)在ModelSim中进行验证;

(3)通过Quartus程序设计器将硬件设计下载到目标FPGA中;

(4)在Nios中编写软件;

(5)将软件下载到目标FPGA;

(6)对软件进行调试。

不过,本书重点介绍软件部分(“嵌入式C”),硬件部分不在本书的范围内。我们提供了硬件部分设计,你可以从我们的网站下载(www.lowlevelcpro-gramming.agstu.com)。每个硬件设计通常都包含两个部分(最少):一个.sof文件和一个.sopcinfo文件。

如果想了解关于硬件设计更多的内容,可参考《HW/SW Embedded FPGA SOC Design 2010》(可以在我们的网站上找到它)。

图2.1展示了一个开发板以及开发板上的一些硬件。这个开发板的大小只有50mm×85mm。这是一款Altera开发的FPGA开发板BeMicro MAX 10,它拥有片上非易失性存储器、片内AD转换器以及温度传感器。它还具有锁相环、硬件乘法器,它还支持软核CPUNios II。另外,还可以添加更多的传感器,这一点我们很快会看到。

图2.1 BeMicro MAX 10开发板

这个开发板使用USB电缆供电,电缆还可以用来对FPGA进行编程,因此无需再外接电源。但如果有必要,它还提供了一个2.5mm连接器来连接外部电源提供5V的电压。FPGA本身包含有8000个LE(逻辑单元)、51个M9K存储块、24个18×18乘法器、两个锁相环、250kB的Flash存储器以及250个通用I/O引脚。图2.2给出了一个FPGA的原理概述以及开发板上的I/O单元。

图2.2 BeMicro MAX 10开发板

我们将通过本书中提供的案例来了解各个硬件的细节,但现在只介绍一下时钟和最简单的I/O(LED和按钮)。

Max 10 FPGA也支持Altera 软核CPU——NiosII处理器的执行,我们的设计将基于这个处理器。所有的任何处理器都需要一个时钟,我们的处理器NiosII也不例外。本书使用的所有硬件设计都可以下载,你无需详细理解里面的信息。但这里还是引入这部分内容,以便你能更好地了解系统。

如图2.2所示,在开发板上有一个50MHz的时钟,而且它与FPGA相连。FPGA还有一对锁相环,我们将利用这对锁相环来创建设计工作所需要的时钟。

如果不需要使用外部SDRAM存储器或者内部的AD转换器,那么就只需要一个时钟(为CPU提供时钟周期),这里直接使用50MHz的时钟。

不过,当向设计中添加了外部存储器(SDRAM)后,就需要通过内部的DDR控制器来操作外部存储器了。DDR表示“双数据速率”,它将SDRAM的读写速度提高了2倍。这是因为DDR控制器可以在时钟的上升沿和下降沿分别执行一次读/写操作。

但是,这将需要一个额外的时钟信号,这个时钟与系统时钟的相位差为−90°。为了加快速度,以后也会使用一个PLL来将系统的时钟频率增加到80MHz。

另外,片内ADC需要一个10MHz的时钟,所示,当我们使用ADC时,将不得不调用第二个PLL来为ADC提供一个10MHz的时钟。图2.3给出了为我们更复杂的设计提供的系统时钟。

图2.3 复杂设计的时钟系统

40MHz的时钟系统可以用于“速度慢”的I/O单位。

在第一个案例中的设计并没有使用任何的LED和按钮。不过,嵌入式系统本身需要一个复位按钮。我们将使用BeMicro开发板上的SW4开关来实现这个功能,为了能正确地使用它,需要先了解一些关于主板上按钮的信息,同样,我们也会在这里介绍一下LED灯。

首先需要知道FPGA上的引脚是如何分配给硬件的。表2.1给出了硬件与引脚之间的关系。

表2.1 FPGA上pin的分配:LED和按钮

开发板标签

信号名称

FPGA引脚名称

D1

USER_LED[1]

M2

D2

USER_LED[2]

N1

D3

USER_LED[3]

R2

D4

USER_LED[4]

T1

D5

USER_LED[5]

V4

D6

USER_LED[6]

T6

D7

USER_LED[7]

AB4

D8

USER_LED[8]

AA5

SW1

PB[1]

M1

SW2

PB[2]

R1

SW3

PB[3]

V5

SW4

PB[4]

AB5

50 MHz clock

SYS_CLK

N14

“信号名称”中使用了方括号和数字的表示方法,实际上,可以使用任何名字来称呼它们。注意,表2.1中还包含了向板载的50MHz的时钟的引脚分配。

图2.4所示为一个按钮的电路图。

图2.4 按钮电路

图2.4所示的电路有两个重要的因果关系。首先,RC网络形成了一个消抖电路(这个按钮的作用是用来消除信号抖动)。其次,当按钮被按下时,PBX信号变为逻辑LOW。因此,如果使用SW4重置系统,也就是说,我们希望在按下SW4时系统能够重置,它必须重置为逻辑LOW。

图2.5给出了LED电路。

图2.5 LED电路

图2.5的结果是:当USER_LEDx被设为LOW时,LED的Dx被打开。


相关图书

电子硬件工程师入职图解手册  硬件知识篇
电子硬件工程师入职图解手册 硬件知识篇
RISC-V体系结构编程与实践
RISC-V体系结构编程与实践
Altium Designer 22电路设计与仿真实战从入门到精通
Altium Designer 22电路设计与仿真实战从入门到精通
龙芯嵌入式系统原理与应用开发
龙芯嵌入式系统原理与应用开发
龙芯嵌入式系统软硬件平台设计
龙芯嵌入式系统软硬件平台设计
GPU编程实战(基于Python和CUDA)
GPU编程实战(基于Python和CUDA)

相关文章

相关课程