嵌入式系统实验报告

实 验 报 告

( 20## / 20## 学年 第 二 学期)

实验一熟悉Linux开发环境

实验目的:

熟悉Linux开发环境,学会基于S3C2410的Linux开发环境的配置和使用,学习使用Linux操作系统的常用命令。使用Linux的armv4l-unknown-linux-gcc编译,使用基于NFS方式的下载调试,了解嵌入式开发的基本过程。

实验内容与要求

使用Redhat Linux 9.0操作系统环境,安装ARM-Linux的开发库及编译器。创建一个新目录,并在其中编写hello.c和Makefile文件。学习在Linux下的编程和编译过程,以及ARM开发板的使用和开发环境的设置。下载已经编译好的文件到目标开发板上运行。

学会集成环境的安装与开发。

预备知识

C 语言的基础知识、程序调试的基础知识和方法,Linux 的基本操作。

实验设备及工具(包括软件调试工具)

硬件:UP-NETARM2410-S嵌入式实验平台、PC机Pentium 500以上, 硬盘10GB以上。

软件:PC机操作系统REDHAT LINUX 9.0+MINICOM+ARM-LINUX开发环境。

实验过程

1、建立工作目录

运行PC机上的Linux系统,进入系统后,建立工作目录,所用语句如下:

[root@zxt smile]# mkdir hello

[root@zxt smile]# cd hello

2、编写程序源代码

编写的hello.c如下:

#include <stdio.h>

main()

{

printf(“hello world \n”);

}

3、编写Makefile

要使上面的hello.c程序能够运行,必须要编写一个Makefile文件,Makefile文件定义了一系列的规则,它指明了哪些文件需要编译,哪些文件需要先编译,哪些文件需要重新编译等等更为复杂的命令。

本次实验所用的Makefile文件如下:

CC= armv4l-unknown-linux-gcc

EXEC = hello

OBJS = hello.o

CFLAGS +=

LDFLAGS+= –static

all: $(EXEC)

$(EXEC): $(OBJS)

$(CC) $(LDFLAGS) -o $@ $(OBJS)

clean:

-rm -f $(EXEC) *.elf *.gdb *.o

使用如下语句来创建Makefile文件:

[root@zxt hello]# vi Makefile

4、编译应用程序

接下来,进行hello.c的编译,使用以下语句:

[root@zxt hello]# make clean

[root@zxt hello]# make

5、下载调试

在宿主PC计算机上启动NFS服务,并设置好共享的目录,进入MINICOM中建立开发板与宿主PC机之间的通讯。

[root@zxt hello]# minicom

[/mnt/yaffs] mount -t nfs -o nolock 192.168.0.56:/arm2410s /host

实验二:多线程应用程序设计

实验目的:

1.   了解多线程程序设计的基本原理。

2.   学习 pthread 库函数的使用。

实验内容:读懂 pthread.c 的源代码,熟悉几个重要的PTHREAD 库函数的使用。掌握共享锁和信号量的使用方法。进入/arm2410s/exp/basic/02_pthread 目录,运行make 产生pthread 程序,使用NFS 方式连接开发主机进行运行实验。

预备知识:硬件:UP-TECH S2410/P270 DVP 嵌入式实验平台,PC 机 Pentium 500 以上, 硬盘 40G 以上, 内存大于 128M。软件:PC 机操作系统 REDHAT LINUX 9.0 +MINICOM + ARM-LINUX 开发环境。

实验原理及代码分析:

1.多线程程序的优缺点

多线程程序作为一种多任务、并发的工作方式,有以下的优点:

1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系 统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术, 将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。

2) 使多 CPU 系统更加有效。操作系统会保证当线程数不大于 CPU 数目时,不同的线程运行 于不同的 CPU 上。

3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立 的运行部分,这样的程序会利于理解和修改。

LIBC 中的 pthread 库提供了大量的 API 函数,为用户编写应用程序提供支持。

2.实验源代码与结构流程图

* The classic producer-consumer example.

* Illustrates mutexes and conditions.

*    by Zou jian guo <ah_zou@tom.com>

*    20##-12-22

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include "pthread.h"

#define BUFFER_SIZE 16

/*  设置一个整数的圆形缓冲区 */

struct prodcons {

int buffer[BUFFER_SIZE];  /* 缓冲区数组 */

pthread_mutex_t lock;  /* 互斥锁 */

int readpos, writepos; /*  读写的位置*/

pthread_cond_t notempty;  /*  缓冲区非空信号 */

pthread_cond_t notfull;   /*缓冲区非满信号 */

};

/*--------------------------------------------------------*/

/*初始化缓冲区*/

void init(struct prodcons * b)

{

pthread_mutex_init(&b->lock, NULL); pthread_cond_init(&b->notempty, NULL); pthread_cond_init(&b->notfull, NULL);

b->readpos = 0;

b->writepos = 0;

}

/*--------------------------------------------------------*/

/*  向缓冲区中写入一个整数*/

void put(struct prodcons * b, int data)

{

pthread_mutex_lock(&b->lock);

/*等待缓冲区非满*/

while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {

printf("wait for not full\n");

pthread_cond_wait(&b->notfull, &b->lock);

}

/*写数据并且指针前移*/

b->buffer[b->writepos] = data;

b->writepos++;

if (b->writepos >= BUFFER_SIZE) b->writepos = 0;

/*设置缓冲区非空信号*/

pthread_cond_signal(&b->notempty);

pthread_mutex_unlock(&b->lock);

}

/*--------------------------------------------------------*/

/*从缓冲区中读出一个整数 */

int get(struct prodcons * b)

{

int data;

pthread_mutex_lock(&b->lock);

/*  等待缓冲区非空*/

while (b->writepos == b->readpos) { printf("wait for not empty\n"); pthread_cond_wait(&b->notempty, &b->lock);

}

/*  读数据并且指针前移 */ data = b->buffer[b->readpos]; b->readpos++;

if (b->readpos >= BUFFER_SIZE) b->readpos = 0;

/*  设置缓冲区非满信号*/

pthread_cond_signal(&b->notfull);

pthread_mutex_unlock(&b->lock);

return data;

}

/*--------------------------------------------------------*/

#define OVER (-1)

struct prodcons buffer;

/*--------------------------------------------------------*/

void * producer(void * data)

{

int n;

for (n = 0; n < 1000; n++) { printf(" put-->%d\n", n); put(&buffer, n);

}

put(&buffer, OVER); printf("producer stopped!\n"); return NULL;

}

/*--------------------------------------------------------*/

void * consumer(void * data)

{

int d;

while (1) {

d = get(&buffer);

if (d == OVER ) break;

printf(" %d-->get\n", d);

}

printf("consumer stopped!\n");

return NULL;

}

/*--------------------------------------------------------*/

int main(void)

{

pthread_t th_a, th_b; void * retval; init(&buffer);

pthread_create(&th_a, NULL, producer, 0);

pthread_create(&th_b, NULL, consumer, 0);

/*  等待生产者和消费者结束 */ pthread_join(th_a, &retval); pthread_join(th_b, &retval); return 0;

}

3.主要函数分析:

下面我们来看一下,生产者写入缓冲区和消费者从缓冲区读数的具体流程,生产者首先要 获得互斥锁,并且判断写指针+1 后是否等于读指针,如果相等则进入等待状态,等候条件 变量 notfull;如果不等则向缓冲区中写一个整数,并且设置条件变量为 notempty,最后 释放互斥锁。消费者线程与生产者线程类似,这里就不再过多介绍了。流程图如下:

  生产者写入共享的循环缓冲区函数 PUT

void put(struct prodcons * b, int data)

{

pthread_mutex_lock(&b->lock); //获取互斥锁

while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {

//如果读写位置相同

pthread_cond_wait(&b->notfull, &b->lock);

//等待状态变量 b->notfull,不满则跳出阻塞

}

b->buffer[b->writepos] = data;   //写入数据

b->writepos++;

if (b->writepos >= BUFFER_SIZE) b->writepos = 0;

pthread_cond_signal(&b->notempty);   //设置状态变量

pthread_mutex_unlock(&b->lock);  //释放互斥锁

}

消费者读取共享的循环缓冲区函数 GET

int get(struct prodcons * b)

{

int data;

pthread_mutex_lock(&b->lock); //获取互斥锁

while (b->writepos == b->readpos) {  //如果读写位置相同

pthread_cond_wait(&b->notempty, &b->lock);

//等待状态变量 b->notempty,不空则跳出阻塞。否则无数据可读。

}

data = b->buffer[b->readpos]; //读取数据

b->readpos++;

if (b->readpos >= BUFFER_SIZE) b->readpos = 0; pthread_cond_signal(&b->notfull);  //设置状态变量 pthread_mutex_unlock(&b->lock);     //释放互斥锁

return data;

}

4.主要的多线程 API 在本程序的代码中大量的使用了线程函数,如 pthread_cond_signal、 pthread_mutex_init、pthread_mutex_lock 等等,这些函数的作用是什么,在哪里定义的, 我们将在下面的内容中为大家做一个简单的介绍,并且为其中比较重要的函数做一些详细 的说明。

¾    线程创建函数:

int pthread_create (pthread_t * thread_id,      const pthread_attr_t *     attr,

void *(*    start_routine) (void *),void *    restrict     arg)

¾    获得父进程 ID:

pthread_t pthread_self (void)

  测试两个线程号是否相同:

int pthread_equal (pthread_t      thread1, pthread_t     thread2)

  线程退出:

void pthread_exit (void *    retval)

    等待指定的线程结束:

int pthread_join (pthread_t      th, void **    thread_return)   互斥量初始化:

pthread_mutex_init (pthread_mutex_t *,    const pthread_mutexattr_t *)

    销毁互斥量:

int pthread_mutex_destroy (pthread_mutex_t *

mutex)   再试一次获得对互斥量的锁定(非阻塞)

int pthread_mutex_trylock (pthread_mutex_t *    mutex)

    锁定互斥量(阻塞):

int pthread_mutex_lock (pthread_mutex_t *    mutex)   解锁互斥量:

int pthread_mutex_unlock (pthread_mutex_t *    mutex)

  条件变量初始化:

int pthread_cond_init (pthread_cond_t *    restrict      cond,

    const pthread_condattr_t *    restrict      cond_attr)

  销毁条件变量 COND:

int pthread_cond_destroy (pthread_cond_t *    cond)   唤醒线程等待条件变量:

int pthread_cond_signal (pthread_cond_t *    cond)

  等待条件变量(阻塞):

int pthread_cond_wait (pthread_cond_t *    restrict      cond, pthread_mutex_t *    restrict      mutex)

在指定的时间到达前等待条件变量:

int pthread_cond_timedwait (pthread_cond_t *    restrict      cond,

pthread_mutex_t *    restrict      mutex,        const struct timespec *    restrict      abstime)

PTHREAD 库中还有大量的 API 函数,用户可以参考其他相关书籍。

六、实验步骤

1、阅读源代及编译应用程序

进入 exp/basic/02_pthread 目录,使用 vi 编辑器或其他编辑器阅读理解源代码。运行 make

产生 pthread 可执行文件。

2、下载和调试

切换到 minicom 终端窗口,使用 NFS mount 开发主机的/arm2410cl  到/host 目录。

进入/host/exp/basic/pthread 目录,运行 pthread,观察运行结果的正确性。运行程序最 后一部分结果如下:

wait for not empty put-->994

put-->995 put-->996 put-->997 put-->998 put-->999

producer stopped!

993-->get

994-->get

995-->get

996-->get

997-->get

998-->get

999-->get consumer stopped!

[/host/exp/basic/02_pthread]

实验串行端口程序设计

实验目的:

了解在 linux 环境下串行程序设计的基本方法。

 掌握终端的主要属性及设置方法,熟悉终端 I /O 函数的使用。

学习使用多线程来完成串口的收发处理。

实验内容与要求:

了解在Linux环境下串行程序设计的基本方法。掌握终端的主要属性及设置方法,熟悉终端I /O 函数的使用。学习使用多线程来完成串口的收发处理。

读懂程序源代码,学习终端I/O函数的使用方法,学习将多线程编程应用到串口的接收和发送程序设计中。

预备知识:

有 C 语言基础。

掌握在 Linux 下常用编辑器的使用。

? 掌握 Makefile 的编写和使用。

? 掌握 Linux 下的程序编译与交叉编译过程。

实验设备:

硬件:UP-NETARM2410-S嵌入式实验平台、PC机Pentium 500以上, 硬盘10GB以上。

软件:PC机操作系统REDHAT LINUX 9.0+MINICOM+ARM-LINUX开发环境。

实验原理:

异步串行I/O方式是将传输数据的每个字符一位接一位(例如先低位、后高位)地传送。数据的各不同位可以分时使用同一传输通道,因此串行I/O可以减少信号连线,最少用一对线即可进行。接收方对于同一根线上一连串的数字信号,首先要分割成位,再按位组成字符。为了恢复发送的信息,双方必须协调工作。在微型计算机中大量使用异步串行I/O方式,双方使用各自的时钟信号,而且允许时钟频率有一定误差,因此实现较容易。但是由于每个字符都要独立确定起始和结束(即每个字符都要重新同步),字符和字符间还可能有长度不定的空闲时间,因此效率较低。

上图给出异步串行通信中一个字符的传送格式。开始前,线路处于空闲状态,送出连续“1”。传送开始时首先发一个“0”作为起始位,然后出现在通信线上的是字符的二进制编码数据。每个字符的数据位长可以约定为5位、6位、7位或8位,一般采用ASCII编码。后面是奇偶校验位,,根据约定,用奇偶校验位将所传字符中为“1”的位数凑成奇数个或偶数

个。也可以约定不要奇偶校验,这样就取消奇偶校验位。最后是表示停止位的“1”信号,这个停止位可以约定持续1 位、1.5 位或2 位的时间宽度。至此一个字符传送完毕,线路又进入空闲,持续为“1”。经过一段随机的时间后,下一个字符开始传送才又发出起始位。每一个数据位的宽度等于传送波特率的倒数。微机异步串行通信中,常用的波特率为50,95,110,150,300,600,1200,2400,4800,9600 等。

接收方按约定的格式接收数据,并进行检查,可以查出以下三种错误:

奇偶错:在约定奇偶检查的情况下,接收到的字符奇偶状态和约定不符。

帧格式错:一个字符从起始位到停止位的总位数不对。

溢出错:若先接收的字符尚未被微机读取,后面的字符又传送过来,则产生溢出错。

每一种错误都会给出相应的出错信息,提示用户处理。一般串口调试都使用空的。

MODEM 连接电缆,其连接方式如下:

程序分析

Linux操作系统从一开始就对串行口提供了很好的支持,为进行串行通讯提供了大量的函数,本次实验主要使用在Linux中进行串行通讯编程的基本方法。程序流程图如下:

程序如下:

#include <termios.h>

#include <stdio.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/signal.h>

#include <pthread.h>

#define BAUDRATE B115200

#define COM1 "/dev/ttyS0"

#define COM2 "/dev/ttyS1"

#define ENDMINITERM 27 /* ESC to quit miniterm */

#define FALSE 0

#define TRUE 1

volatile int STOP=FALSE;

volatile int fd;

void child_handler(int s)

{

printf("stop!!!\n");

STOP=TRUE;

}

/*--------------------------------------------------------*/

void* keyboard(void * data)

{

int c;

for (;;){

c=getchar();

if( c== ENDMINITERM){

STOP=TRUE;

break ;

}

}

return NULL;

}

/*--------------------------------------------------------*/

/* modem input handler */

void* receive(void * data)

{

int c;

printf("read modem\n");

while (STOP==FALSE)

{

read(fd,&c,1); /* com port */

write(1,&c,1); /* stdout */

}

printf("exit from reading modem\n");

return NULL;

}

/*--------------------------------------------------------*/

void* send(void * data)

{

int c='0';

printf("send data\n");

while (STOP==FALSE) /* modem input handler */

{

c++;

c %= 255;

write(fd,&c,1); /* stdout */

usleep(100000);

}

return NULL; }

/*--------------------------------------------------------*/

int main(int argc,char** argv)

{

struct termios oldtio,newtio,oldstdtio,newstdtio;

struct sigaction sa;

int ok;

pthread_t th_a, th_b, th_c;

void * retval;

if( argc > 1)

fd = open(COM2, O_RDWR );

else

fd = open(COM1, O_RDWR ); //| O_NOCTTY |O_NONBLOCK);

if (fd <0) {

error(COM1);

exit(-1);

}

tcgetattr(0,&oldstdtio);

tcgetattr(fd,&oldtio); /* save current modem settings */

tcgetattr(fd,&newstdtio); /* get working stdtio */

newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; /*ctrol flag*/

newtio.c_iflag = IGNPAR; /*input flag*/

newtio.c_oflag = 0; /*output flag*/

newtio.c_lflag = 0;

newtio.c_cc[VMIN]=1;

newtio.c_cc[VTIME]=0;

/* now clean the modem line and activate the settings for modem */

tcflush(fd, TCIFLUSH);

tcsetattr(fd,TCSANOW,&newtio); /*set attrib*/

sa.sa_handler = child_handler;

sa.sa_flags = 0;

sigaction(SIGCHLD,&sa,NULL); /* handle dying child */

pthread_create(&th_a, NULL, keyboard, 0);

pthread_create(&th_b, NULL, receive, 0);

pthread_create(&th_c, NULL, send, 0);

pthread_join(th_a, &retval);

pthread_join(th_b, &retval);

pthread_join(th_c, &retval);

tcsetattr(fd,TCSANOW,&oldtio); /* restore old modem setings */

tcsetattr(0,TCSANOW,&oldstdtio); /* restore old tty setings */

close(fd);

exit(0);

}

程序中对于串口的部分主要有:

打开串口,

串口设置:波特率设置、校验位和停止位的设置、设置停止位,

读写串口:发送数据、读取串口数据,

关闭串口。

实验小结

通过这次的实验,让我对Linux系统有了一个初步的了解,对于课堂学习的知识也有了一定的应用,而不仅仅学习了理论知识,更学会了应用到实际中。通过实际操作对嵌入式基本设置和其读、写程序和硬件设置的设计有了不少了解。刚开始学习建立连接的环境花费了一定的时间,而且对于较为复杂的编程以及Linux下的很多技巧知之甚少,这是我需要加强的,在以后的学习中需要更进一步的学习。

相关推荐