嵌入式系统实验报告(C 语言实验)

2019-07-13 06:08发布

第一部分指导手册实验分析1 LED 流水灯
这是我们学习C 语言实验的第一个示例,项目的烧录、执行与汇编相同,这里我们简要分析一下我们的项目。
首先我们从main 函数开始,它有两个函数,do_system_initial 和led_initial。do_system_initial 函数首先会调用led_initial 函数初始化led,再通过tester_led 函数控制相应LED 的亮与灭。1.1 do_system_initial
查阅手册可知,6818 芯片共有160 个GPIO 接口,分为5 组。我们只需通过修改对应寄存器的值,就能完成对他们的初始化。在之前汇编语言编程中,我们就修改了它的复用功能选择寄存器、输出使能寄存器、输出寄存器等。
我们先简要介绍一下LED 灯的初始化。
对LED 灯的初始化方法如下:
在这里,16 端口代表LED1 灯。具体如下:
(1)gpio_set_cfg 函数用于将GPIO_A16 设置为GPIO 功能, 如要作为输出使用,需要将复用功能寄存器设为00, 即第二个参数为0。
(2)gpio_set_pull 函数用于将GPIO_A16 设置为上拉或者下拉。这涉及到CMOS 制作工艺的问题,在此不再详细讨论,只需知道,低电平有效设为下拉,高电平有效设为上拉。
(3)gpio_direction_output 函数通过修改GPIO_OUTENB 和GPIO_OUT寄存器将GPIO_A16 设置为输出,
(4)gpio_set_value 函数用于GPIO_A16 设置为低电平。对其它的LED 灯执行同样的操作,我们便完成了LED 灯的初始化。1.2 tester_led
函数如下:


通过全局变量index 选择LED 灯,利用led_set_status 函数控制LED 灯的亮灭。led_set_status 函数内部调用gpio_direction_output 函数,将对应的端口设置为1 或0, 从而控制LED 灯。
2 按键控制LED
该实验实现了,按下VOL+,VOL-两个按键来控制LED 灯的亮灭。
2.1 初始化
LED 灯初始化不再赘述。按键初始化方法如下:
首先还是用gpio_set_cfg 函数,将VOL+ 和VOL-端口设为GPIO 模式,gpio_set_pull 将其上拉,gpio_direction_input 将端口方向设定为输入。
2.2 轮询
初始化之后,用while 循环进入忙等待状态,不断调用get_key_event(&keyup,&keydown),当发生按下时,keydown 被修改为1; 当按键按起时,keyup 被修改为1。当检测到修改为1 时,我们便可以调用led_set_status 函数修改LED 灯的亮灭。代码太多,不在此展示了。至此,该实验完成。3 按键中断
在上一个实验中,忙等待的方式浪费了CPU 的大量时间,效率非常大。一种很好的改进方法是采用中断的方式,节省CPU 开销。(当然缺点是,高效的中断需要硬件实现,增加了电路复杂度) 下面我们介绍一下编程的流程。

3.1 中断初始化调用s5p6818_irq_init() 函数实现对中断硬件寄存器的初始化。在函数内部,定义中断句柄的func,在指令执行的中断周期,CPU 就会去访问event detect 寄存器,如发现有1, 则执行中断处理程序。
这些是计算机组成原理的知识,只编程的话不需要考虑那么多,所以在此不再赘述,只需知道如要用到中断,先调用一下s5p6818_irq_init() 函数即可。

3.2 中断处理函数在操作系统中,每一种中断都绑定了一种中断处理函数,存在中断向量表中。类似的,在这里我们需要为端口绑定中断处理函数,request_irq 函数帮我们解决了这个问题。它的函数原型如下:
它做了三件事,如下:
(1) 函数内部调用irq_search(name), 根据参数1 传入的端口名称找到对应的中断请求结构struct irq_t,该结构类似中断向量表,保存了它的中断名称,编号,中断函数,使能,触发条件等。(2) 将第二个参数,即你自己定义的函数指针,赋值给中断请求结构,将第三个参数,即触发条件(下降沿触发或者上升沿触发) 赋值给irq_t,并将data 赋值给irq_t->handler->data。
(3) 开中断
于是,我们写好中断处理程序,用该函数绑定好端口,便实现了中断功能。具体中断处理函数,不再赘述。4 串口输入
串口通信是我们的PC 与实验箱通信的一种方式。通过串口线连接,便能够实现PC 和实验箱的交互。
4.1 初始化
s5p4418_serial_initial(void) 函数初始化串口,内部实现了对输入输出串口端口的初始化。
4.2 测试
测试函数如下:

首先调用s5p6818_serial_write_string 函数向串口中输出一条信息。之后,在while 循环中调用s5p6818_serial_read 监听串口的信息,从缓冲区中读取数据,第一个参数是串口号,第二个参数是存放数据的缓冲区,第三个参数是读取的字节数,函数返回1 说明读取成功。之后调用s5p6818_serial_write函数再写回串口。我们的PC 端的Putty 程序会检测到实验箱发来的串口数据,在屏幕上显示出来。
5 移植printf 函数实验
serial_printf 函数内部封装了s5p6818_serial_write_string,并实现了我们C 语言中的printf 函数的功能(即字符串的格式化输出,例如使用占位符等等)。
函数原型如下:
ch 是串口号,fmt 是你要输出的字符串,后面的参数是替代占位符的变量。

第二部分本次实验

6 实验介绍第一次实验中,我们在裸机下用汇编语言实现了蜂鸣器和跑马灯相结合,知道了裸机启动流程以及如何使用GPIO 端口控制灯光和蜂鸣器。现在,我们要用C++ 来再次实现它,并添加一些额外的功能。
7 实验要求
1:按下Vol+,4 个LED 灯逐个亮灭并伴随蜂鸣器。按下Vol-,LED 灯灭,蜂鸣器关闭。
2:通过print 将“LED Lighting” 和”buzzer working” 输出到串口
3:按下PC 的”p”按键,通过串口输入显示。
8 实验实现
8.1 步骤一:System initial
系统初始化函数执行如下:

可以看到,第15 行之前的代码都是比较固定的代码,包括芯片的初始化,串口的初始化等等,之后便是我们需要用到的LED、蜂鸣器以及按键的初始化。
这三种器件的初始化流程还是和汇编的流程相同,读入功能选择寄存器GPIOAALTFN0 和GPIOAALTFN1,将对应的位设为0,在输出使能端GPIOAOUTENB 将我们需要使用的端口设为1,即使能。之后,我们开始执行我们自己写的函数。

8.2 步骤二:按键控制为了实现按键控制,显然忙等待是一种解决思路,但从性能上看并不是一个好的选择。利用中断实现似乎是更好的方法。ARM 架构为我们提供了中断的接口,我们只需用函数调用即可。基本流程同之前的按键中断实验,
不再赘述。
但要注意的是,由于该架构不支持中断嵌套,在执行中断时会屏蔽其他中断,所以我们不能在中断函数中写循环函数,只能用一个临界区的变量来控制,主函数中执行循环。因此,Vol+ 的中断函数如下:

Vol-函数类似,不再赘述。
而主函数执行如下代码:
其中调用的函数执行如下:





8.3 步骤三:输出串口
这一步的实现,仍然可以调用封装好的函数。serial_printf 函数执行输出一个字符串到指定串口的功能。它的第一个参数是串口号,第二个参数就是你要输出的字符串了。8.4 步骤四:PC 按键的串口显示
s5p6818_serial_read(int ch, u8_t * buf, size_t count)函数从ch 串口读入count 个字符,放入缓冲区buf 中。若读取成功,返回1, 否则返回0.
之后我们再利用s5p6818_serial_write(0, &buf, 1); 这个函数,将字符写入串口中,就完成了重定向,也即将PC 的输入重定向到串口中,在显示器上显示出来。9 实验结果
结果如下所示:
至此,C 语言的实验完成!