超声波模块
超声波是 4Pin(VCC, Trig, Echo, GND),工作时需要 Trig 发送触发信号,发送超声波信号,回波检测引脚 Echo 接收超声波返回信号。
工作过程:
1、Trig 设置成输出模式,给至少 10us 的高电平信号。
2、Echo设置成输入模式,等待有信号返回,当检测到一个高电平,高电平持续的时间就是超声波从发射到返回的时间,测试距离=(高电平时间*声速(340m/s))/2。
基于wiringPi库的超声波检测程序
#include "wiringPi.h" #include#include #define Trig 4 #define Echo 5 #define Bee 7 void ultraInit(void) { pinMode(Echo, INPUT); pinMode(Trig, OUTPUT); pinMode(Bee,OUTPUT); } float disMeasure(void) { struct timeval tv1; //timeval是time.h中的预定义结构体 其中包含两个一个是秒>,一个是微秒 struct timeval tv2; long start, stop; float dis; digitalWrite(Trig, LOW); delayMicroseconds(2); digitalWrite(Trig, HIGH); delayMicroseconds(10); //发出超声波脉冲 digitalWrite(Trig, LOW); while(!(digitalRead(Echo) == 1)); gettimeofday(&tv1, NULL); //获取当前时间 开始接收到返回信号的时候 while(!(digitalRead(Echo) == 0)); gettimeofday(&tv2, NULL); //获取当前时间 最后接收到返回信号的时>候 start = tv1.tv_sec * 1000000 + tv1.tv_usec; //微秒级的时间 stop = tv2.tv_sec * 1000000 + tv2.tv_usec; dis = (float)(stop - start) / 1000000 * 34000 / 2; //计算时间差求出距离 return dis; } int main(void) { float dis; if(wiringPiSetup() == -1){ //如果初始化失败,就输出错误信息 程序初始化时务>必进行 printf("setup wiringPi failed !"); return -1; } ultraInit(); digitalWrite(Bee,HIGH); while(1){ dis = disMeasure(); printf("distance = %0.2f cmn",dis); if(dis<10) { digitalWrite(Bee,LOW); } else { digitalWrite(Bee,HIGH); } delay(1000); } return 0; }
既然学了设备驱动,就任性一回,不用wiringPi库的函数,自己写驱动控制I/O口。
上一节只对pin4设置输出模式,控制输出高/低电平。这节对代码进行修改,pin17为发射引脚Trig,设置为输出模式。pin27为接收引脚Echo,设置为输入模式。pin4接蜂鸣器模块的信号线,设置为输出模式。
1、寄存器
GPLEVn (物理地址:0x3F200034) 检测I/O口:高/低电平
2、内核函数
copy_to_user函数
unsigned long copy_to_user(void *to, const void *from, unsigned long n)
to:用户空间指针
from:内核空间的指针
n:将要拷贝数据的字节数
返回:成功返回0,失败返回没有拷贝成功的数据字节数。
1、pin27–> Echo引脚
//文件pin27driver.c #include//file_operation声明 #include //module_init module_exit声明 #include //_init _exit声明 #include //class device 声明 #include //copy_from_user的头文件 #include //设备号 dev_t类型声明 #include //ioremap iounmap的头文件 volatile unsigned int *GPFSEL2=NULL; volatile unsigned int *GPLEV0=NULL; static struct class *pin27_class; static struct device *pin27_class_dev; static dev_t devno;//设备号 static int major=232;//主设备号 static int minor=0;//次设备号 static char *module_name="pin27";//模块名 static int pin27_open(struct inode *inode, struct file *file) { printk("pin27_openn");//内核的打印函数,和printf函数GPIO初始化:配置pin27引脚为输入模式 *GPFSEL2&=~(0x07<<21); return 0; } static ssize_t pin27_read(struct file *filep, char __user *buf, size_t count, loff_t *ppos) { static int iostatus; //printk("pin27_readn"); iostatus=(*GPLEV0>>27)&0x01; if(copy_to_user(buf, (void*)&iostatus, count)) { return -1; } else { return 0; } } static struct file_operations pin27_flops = { .owner = THIS_MODULE, .open = pin27_open, .read = pin27_read, }; int __init pin27_drv_init(void)//真实驱动入口 { devno=MKDEV(major,minor);//创建设备号 register_chrdev(major, module_name, &pin27_flops);//注册字符设备,并告诉内核把驱动加入到驱动链表中 pin27_class=class_create(THIS_MODULE,"pin27_class");//创建类 pin27_class_dev=device_create(pin27_class,NULL,devno,NULL,module_name);//创建设备文件,让代码在dev下自动生成设备 GPFSEL2=(volatile unsigned int *)ioremap(0x3f200008,4);//物理地址转化虚拟地址,io口寄存器映射成普通内存单元进行访问 GPLEV0=(volatile unsigned int *)ioremap(0x3f200034,4); return 0; } void __exit pin27_drv_exit(void) { iounmap(GPFSEL2); iounmap(GPLEV0); device_destroy(pin27_class,devno);//注销设备 class_destroy(pin27_class);//注销类 unregister_chrdev(major, module_name);//注销字符设备 } module_init(pin27_drv_init);//入口 内核加载该驱动的时候,这个宏被调用 module_exit(pin27_drv_exit);//出口 内核卸载该驱动的时候,这个宏被调用 MODULE_LICENSE("GPL v2");//GPL v2规范
2、pin17–> Trig引脚
//文件pin17driver.c #include//udelay的头文件 #include //file_operation声明 #include //module_init module_exit声明 #include //_init _exit声明 #include //class device 声明 #include //copy_from_user的头文件 #include //设备号 dev_t类型声明 #include //ioremap iounmap的头文件 volatile unsigned int *GPFSEL1=NULL; volatile unsigned int *GPSET0=NULL; volatile unsigned int *GPCLR0=NULL; static struct class *pin17_class; static struct device *pin17_class_dev; static dev_t devno;//设备号 static int major=233;//主设备号 static int minor=0;//次设备号 static char *module_name="pin17";//模块名 static int pin17_open(struct inode *inode, struct file *file) { printk("pin17_openn");//内核的打印函数,和printf函数GPIO初始化:配置pin17引脚为输出模式 *GPFSEL1&=~(0x06<<21); *GPFSEL1|=(0x01<<21); return 0; } static ssize_t pin17_write(struct file *file, const char __user * buf, size_t count, loff_t *ppos) { static int usrcmd; //printk("pin17_writen"); copy_from_user(&usrcmd,buf,count); if(usrcmd==1) { //printk("set 1n"); *GPSET0|=0x01<<17; } else if(usrcmd==0) { //printk("set 0n"); *GPCLR0|=0x01<<17; } else if(usrcmd==2) { *GPCLR0|=0x01<<17; *GPSET0&=~(0x01<<17); udelay(2);//延时2us *GPSET0|=0x01<<17; *GPCLR0&=~(0x01<<17); udelay(10);//延时10us *GPCLR0|=0x01<<17; *GPSET0&=~(0x01<<17); } else { printk("undo 17n"); } return 0; } static struct file_operations pin17_flops = { .owner = THIS_MODULE, .open = pin17_open, .write = pin17_write, }; int __init pin17_drv_init(void)//真实驱动入口 { devno=MKDEV(major,minor);//创建设备号 register_chrdev(major, module_name, &pin17_flops);//注册字符设备,并告诉内核把驱动加入到驱动链表中 pin17_class=class_create(THIS_MODULE,"pin17_class");//创建类 pin17_class_dev=device_create(pin17_class,NULL,devno,NULL,module_name);//创建设备文件,让代码在dev下自动生成设备 GPFSEL1=(volatile unsigned int *)ioremap(0x3f200004,4);//物理地址转化虚拟地址,io口寄存器映射成普通内存单元进行访问 GPSET0=(volatile unsigned int *)ioremap(0x3f20001C,4); GPCLR0=(volatile unsigned int *)ioremap(0x3f200028,4); return 0; } void __exit pin17_drv_exit(void) { iounmap(GPFSEL1); iounmap(GPSET0); iounmap(GPCLR0); device_destroy(pin17_class,devno);//注销设备 class_destroy(pin17_class);//注销类 unregister_chrdev(major, module_name);//注销字符设备 } module_init(pin17_drv_init);//入口 内核加载该驱动的时候,这个宏被调用 module_exit(pin17_drv_exit);//出口 内核卸载该驱动的时候,这个宏被调用 MODULE_LICENSE("GPL v2");//GPL v2规范
3、pin4->蜂鸣器Bee引脚
//文件pin4driver.c #include三、应用层测试代码//file_operation声明 #include //module_init module_exit声明 #include //_init _exit声明 #include //class device 声明 #include //copy_from_user的头文件 #include //设备号 dev_t类型声明 #include //ioremap iounmap的头文件 volatile unsigned int *GPFSEL0=NULL; volatile unsigned int *GPSET0=NULL; volatile unsigned int *GPCLR0=NULL; static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno;//设备号 static int major=231;//主设备号 static int minor=0;//次设备号 static char *module_name="pin4";//模块名 static int pin4_open(struct inode *inode, struct file *file) { printk("pin4_openn");//内核的打印函数,和printf函数GPIO初始化:配置pin4引脚为输出模式 *GPFSEL0&=~(0x06<<12); *GPFSEL0|=(0x01<<12); return 0; } static ssize_t pin4_write(struct file *file, const char __user * buf, size_t count, loff_t *ppos) { static int usrcmd; //printk("pin4_writen"); copy_from_user(&usrcmd,buf,count); if(usrcmd==1) { printk("set 1n"); *GPSET0|=0x01<<4; } else if(usrcmd==0) { printk("set 0n"); *GPCLR0|=0x01<<4; } else { printk("undon"); } return 0; } static struct file_operations pin4_flops = { .owner = THIS_MODULE, .open = pin4_open, .write = pin4_write, }; int __init pin4_drv_init(void)//真实驱动入口 { devno=MKDEV(major,minor);//创建设备号 register_chrdev(major, module_name, &pin4_flops);//注册字符设备,并告诉内核把驱动加入到驱动链表中 pin4_class=class_create(THIS_MODULE,"myfirstdemo");//创建类 pin4_class_dev=device_create(pin4_class,NULL,devno,NULL,module_name);//创建设备文件,让代码在dev下自动生成设备 GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);//物理地址转化虚拟地址,io口寄存器映射成普通内存单元进行访问 GPSET0=(volatile unsigned int *)ioremap(0x3f20001C,4); GPCLR0=(volatile unsigned int *)ioremap(0x3f200028,4); return 0; } void __exit pin4_drv_exit(void) { iounmap(GPFSEL0); iounmap(GPSET0); iounmap(GPCLR0); device_destroy(pin4_class,devno);//注销设备 class_destroy(pin4_class);//注销类 unregister_chrdev(major, module_name);//注销字符设备 } module_init(pin4_drv_init);//入口 内核加载该驱动的时候,这个宏被调用 module_exit(pin4_drv_exit);//出口 内核卸载该驱动的时候,这个宏被调用 MODULE_LICENSE("GPL v2");//GPL v2规范
方法一
//文件ultra.c #include#include #include #include #include #include #include void ultraInit(int* trigfd,int* echofd,int* beefd) { *trigfd=open("/dev/pin17",O_RDWR); *echofd=open("/dev/pin27",O_RDWR); *beefd=open("/dev/pin4",O_RDWR); } float disMeasure(int* trigfd,int* echofd) { static int trigstatus=0; int echostatus=0; struct timeval tv1; //timeval是time.h中的预定义结构体 其中包含两个一个是秒,一个是微秒 struct timeval tv2; long start, stop; float dis; trigstatus=2; gettimeofday(&tv1, NULL); write(*trigfd,&trigstatus,1); gettimeofday(&tv2, NULL); start = tv1.tv_sec * 1000000 + tv1.tv_usec; //微秒级的时间 stop = tv2.tv_sec * 1000000 + tv2.tv_usec; printf("sleep:%ldn",(stop-start)); printf("msg 1n"); while(!(echostatus == 1)) { read(*echofd,&echostatus,1); } printf("msg 2n"); gettimeofday(&tv1, NULL); //获取当前时间 开始接收到返回信号的时候 while(!(echostatus == 0)) { read(*echofd,&echostatus,1); } printf("msg 3n"); gettimeofday(&tv2, NULL); //获取当前时间 最后接收到返回信号的时候 start = tv1.tv_sec * 1000000 + tv1.tv_usec; //微秒级的时间 stop = tv2.tv_sec * 1000000 + tv2.tv_usec; dis = (float)(stop - start) / 1000000 * 34000 / 2; //计算时间差求出距离 return dis; } int main(void) { float dis; int beestatus=0; int trigfd=0; int echofd=0; int beefd=0; ultraInit(&trigfd,&echofd,&beefd); printf("trig_fd:%dnecho_fd:%dnbee_fd:%dn"); beestatus=1; write(beefd,&beestatus,1); while(1) { dis = disMeasure(&trigfd,&echofd); printf("distance = %0.2f cmn",dis); if(dis<10) { beestatus=0; write(beefd,&beestatus,1); } else { beestatus=1; write(beefd,&beestatus,1); } sleep(1); } close(beefd); close(trigfd); close(echofd); return 0; }
方法二
//文件ultra.c #include#include #include #include #include #include #include void ultraInit(int* trigfd,int* echofd,int* beefd) { *trigfd=open("/dev/pin17",O_RDWR); *echofd=open("/dev/pin27",O_RDWR); *beefd=open("/dev/pin4",O_RDWR); } float disMeasure(int* trigfd,int* echofd) { static int trigstatus=0; int echostatus=0; struct timeval tv1; //timeval是time.h中的预定义结构体 其中包含两个一个是秒,一个是微秒 struct timeval tv2; long start, stop; float dis; trigstatus=0; gettimeofday(&tv1, NULL); write(*trigfd,&trigstatus,1); gettimeofday(&tv2, NULL); usleep(2); start = tv1.tv_sec * 1000000 + tv1.tv_usec; //微秒级的时间 stop = tv2.tv_sec * 1000000 + tv2.tv_usec; printf("sleep 1:%ldn",(stop-start)); trigstatus=1; gettimeofday(&tv1, NULL); write(*trigfd,&trigstatus,1); gettimeofday(&tv2, NULL); usleep(10); start = tv1.tv_sec * 1000000 + tv1.tv_usec; //微秒级的时间 stop = tv2.tv_sec * 1000000 + tv2.tv_usec; printf("sleep 2:%ldn",(stop-start)); trigstatus=0; gettimeofday(&tv1, NULL); write(*trigfd,&trigstatus,1); gettimeofday(&tv2, NULL); start = tv1.tv_sec * 1000000 + tv1.tv_usec; //微秒级的时间 stop = tv2.tv_sec * 1000000 + tv2.tv_usec; printf("sleep 3:%ldn",(stop-start)); printf("msg 1n"); while(!(echostatus == 1)) { read(*echofd,&echostatus,1); } printf("msg 2n"); gettimeofday(&tv1, NULL); //获取当前时间 开始接收到返回信号的时候 while(!(echostatus == 0)) { read(*echofd,&echostatus,1); } printf("msg 3n"); gettimeofday(&tv2, NULL); //获取当前时间 最后接收到返回信号的时候 start = tv1.tv_sec * 1000000 + tv1.tv_usec; //微秒级的时间 stop = tv2.tv_sec * 1000000 + tv2.tv_usec; dis = (float)(stop - start) / 1000000 * 34000 / 2; //计算时间差求出距离 return dis; } int main(void) { float dis; int beestatus=0; int trigfd=0; int echofd=0; int beefd=0; ultraInit(&trigfd,&echofd,&beefd); printf("trig_fd:%dnecho_fd:%dnbee_fd:%dn"); beestatus=1; write(beefd,&beestatus,1); while(1) { dis = disMeasure(&trigfd,&echofd); printf("distance = %0.2f cmn",dis); if(dis<10) { beestatus=0; write(beefd,&beestatus,1); } else { beestatus=1; write(beefd,&beestatus,1); } sleep(1); } close(beefd); close(trigfd); close(echofd); return 0; }
方法一与方法二的区别在于:方法一中pin17直接通过内核驱动输出10us的脉冲,而方法二中的pin17通过内核驱动先被拉高10us后被拉低。



