实 验 报 告
( 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下的很多技巧知之甚少,这是我需要加强的,在以后的学习中需要更进一步的学习。
郑州航空工业管理学院嵌入式系统实验报告修订版20第赵成张克新编著院姓专学系名业号电子通信工程系20xx年3月制实验一ARM体系结构…
嵌入式系统设计实验报告班级学号姓名成绩指导教师1实验一11实验名称博创UP3000实验台基本结构及使用方法12实验目的1学习嵌入式…
实验报告20xx20xx学年第二学期课程名称ARM嵌入式系统原理与开发实验名称嵌入式Linux交叉开发环境建立与程序开发实验时间指…
嵌入式系统设计实验报告班级学号姓名成绩指导教师20xx061420xx061411刘群峰赵国冬孟昭林1实验一11实验名称博创UP3…
嵌入式系统实验报告学院测量与通信工程学院专业信号与信息处理学生姓名姜元学号1320xx0050指导教师董静薇一实验目的了解Boot…
重庆航天职业技术学院课程作业课程名称嵌入式系统专选班级学号姓名成绩前言嵌入式系统是基于单片机的一种升级版它是以应用为中心以计算机技…
嵌入式系统设计实验报告班级学号姓名成绩指导教师1实验一11实验名称博创UP3000实验台基本结构及使用方法12实验目的1学习嵌入式…
嵌入式系统实验报告姓名:##学号:##班级:计算机科学与技术非师范121班实验一LCD控制一、实验目的1、初步掌握液晶显示屏的使用…
嵌入式系统实验报告书院系姓名专业学号指导老师电子通信工程系蒋瑾通信工程101307313赵成实验一认识嵌入式开发平台一实验目的认识…
郑州航空工业管理学院嵌入式系统实验报告修订版20第赵成张克新编著院姓专学系名业号电子通信工程系20xx年3月制实验一ARM体系结构…