【嵌入式Linux】基于orangepi的官方外设开发

一、WiringPi外设SDK安装

git clone https://github.com/orangepi-xunlong/wiringOP	//下载源码
cd wiringOP						//进入文件夹
sudo ./build clean				//删除编译信息
sudo ./build					//编译

通过windows浏览器打开 https://github.com/orangepi-xunlong/wiringOP
下载压缩包
把压缩包通过xtrem传到开发板
解压		unzip xxx.zip
cd xxx
sudo ./build
gpio readall

验证指令:gpio readall
如下方所示,外设库就完成安装了

在这里插入图片描述


二、蜂鸣器开发

原理:基本IO口的应用

1. 蜂鸣器配合时间函数开发:

  1 #include <stdio.h>
  2 #include <wiringPi.h>
  3 #include <unistd.h>
  4
  5 #define BEEP 0   //设置0脚为控制引脚
  6
  7 int main (void)
  8 {
  9
 10     wiringPiSetup () ;//初始化wiringP库
 11     pinMode (BEEP, OUTPUT) ;//设置IO口的输入输出,输出
 12     while(1){
 13         usleep(500000);
 14         digitalWrite (BEEP, LOW) ;//设置IO口输出低电平,蜂鸣器响
 15         usleep(500000);
 16         digitalWrite (BEEP, HIGH) ;//设置IO口输出高电平,蜂鸣器不响
 17     }
 18
 19     return 0;
 20 }


三、超声波测距模块开发

1. 原理说明

超声波测距模块是用来测量距离的一种产品,通过发送和接收超声波,利用时间差和声音传播素的,计算出模块到前方障碍物的距离。

型号:HC-SR04
在这里插入图片描述
过程分析:

  • 怎么让它发送波

Trig ,给Trig端口至少10us的高电平

  • 怎么知道它开始发了

Echo信号,由低电平跳转到高电平,表示开始发送波

  • 怎么知道接收了返回波

Echo,由高电平跳转回低电平,表示波回来了

  • 怎么算时间

Echo引脚维持高电平的时间!
波发出去的那一下,开始启动定时器
波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间

  • 怎么算距离

距离 = 速度 (340m/s)* 时间/2

时序图:

在这里插入图片描述

2. 时间函数

函数原型

#include <sys/time.h>

int gettimeofday(struct timeval *tv,struct timezone *tz)

gettimeofday()会把目前的时间用tv结构体返回,当时时区的信息则放到tz所指的结构中

struct timeval
{
	long tv_sec;//秒
	long tv_usec;//微秒
}

测试代码:

//计算程序在当前环境中数数10万次耗时多少
#include <stdio.h>
#include <sys/time.h>

//int gettimeofday(struct timeval *tv,struct timezone *tz)

void cntTest()
{
     int i,j;

     for(i=0;i<100;i++)
              for(j=0;j<1000;j++);
}

int main(){

     struct timeval start;
     struct timeval end;

     gettimeofday(&start, NULL);
     cntTest();
     gettimeofday(&end, NULL);

     long diffTime = 1000000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);

     printf("difTime = %ld\n",diffTime);

     return 0;
}

3. 超声波测距代码实现

#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <sys/time.h>
#include <unistd.h>

#define Trig 0
#define Echo 1

double getDistance()
{
     double dis;
     struct timeval start;
     struct timeval end;

     pinMode(Trig, OUTPUT);
     pinMode(Echo, INPUT);

     digitalWrite(Trig, LOW);
     usleep(5);

     digitalWrite(Trig, HIGH);
     usleep(10);
     digitalWrite(Trig, LOW);

     while(!digitalRead(Echo));
     gettimeofday(&start, NULL);
     while(digitalRead(Echo));
     gettimeofday(&end, NULL); 


     long diffTime = 1000000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);

     dis = (double)diffTime/1000000 * 34000 / 2;

     return dis;
}

int main()
{
     double dis;

     if(wiringPiSetup() == -1){
         fprintf(stderr,"%s","initwiringPi error");
         exit(-1);
     }

     while(1){
         dis = getDistance();
         printf("dis = %lf\n",dis);
         usleep(500000);
     }

 
     return 0;
}


四、sg90舵机开发

1. 舵机基本介绍

如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制。
用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等
常见的有0-90°、0-180°、0-360°

在这里插入图片描述

怎么控制舵机

向黄色信号线“灌入”PWM信号。

  • PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右

数据:

1.0ms------------45度; 5.0% 对应函数中占空比为500
1.5ms------------90度; 7.5% 对应函数中占空比为750
2.0ms-----------135度; 10.0% 对应函数中占空比为1000
2.5ms-----------180度; 12.5% 对应函数中占空比为1250

在这里插入图片描述

2. Linux定时器

分析:实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理函数来处理产生的定时信号。从而实现定时器。
先看itimerval的结构体

struct itimerval {
    struct timeval it_interval; /* timer interval */
    struct timeval it_value;    /* current value */
};

it_interval:计时器的初始值,一般基于这个初始值来加或减,看控制参数的配置
it_value:程序跑到这里多久后,启动定时器

struct timeval
{
	long tv_sec;//秒
	long tv_usec;//微秒
}

int setitimer(int which,const struct itimerval *new_value,struct itimerval *old_value);

setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。

which:三种类型

ITIMER_REAL //数值为0,计时器的值真实递减,发送的信号是SIGALARM。
ITIMER_VIRTUAL //数值为1,进程指向时递减计时器的值,发送的信号是SIGPROF。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号时SIGPROF。

很明显,这边需要捕获对应的信号进行逻辑相关处理 signal(SIGALRM,signal_handler);

返回说明:
成功执行时,返回0。失败返回-1

代码实现每隔1s打印hello:

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>

static int i = 0;

void signal_handler(int signum)
{
    i++;
    if(i == 2000){
        printf("hello\n");
        i = 0;
    }

}


int main()
{
    struct itimerval itv;

    //设置定时器值的定时时间
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 500;
    //设置定时器开始定时的时间
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 1;
    //设置定时器的工作方式
    if(setitimer(ITIMER_REAL, &itv, NULL) == -1){
        perror("error");
        exit(-1);
    }
    //设置信号处理函数
    signal(SIGALRM, signal_handler);

    while(1);
    return 0;
}

3. Linux定时器控制舵机实战开发:

通过键盘输入控制舵机角度:

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <wiringPi.h>

#define SG90Pin 5

static int i = 0;
int jd;

void signal_handler(int signum)
{
    if(i <= jd){
        digitalWrite(SG90Pin, HIGH);
    }else{
        digitalWrite(SG90Pin, LOW);
    }

    if(i == 40){
        i = 0;
    }
    i++;
}


int main()
{
    struct itimerval itv;

    jd = 0;
    wiringPiSetup();
    pinMode(SG90Pin, OUTPUT);

    //设置定时器值的定时时间
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 500;
    //设置定时器开始定时的时间
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 1;
    //设置定时器的工作方式
	if(setitimer(ITIMER_REAL, &itv, NULL) == -1){
        perror("error");
        exit(-1);
    }
    //设置信号处理函数
    signal(SIGALRM, signal_handler);

    while(1){
        printf("input jd: 1-0 2-45 3-90 4-135 5-180\n");
        scanf("%d",&jd);
    }
    return 0;
}


五、OLED屏幕应用-IIC协议

1. 准备过程

  • 启动 linux 系统后,先确认一下 i2c-3 的设备节点

从命令运行结果可以观察到系统支持 I2C-3 和 I2C-5 的驱动,而H616的外设我们看到只有一个IIC接口,用的是IIC-3
Linux一切皆文件,每个硬件设备“对应”一个文件,由驱动程序提供映射
在这里插入图片描述

  • 开始测试i2c,首先安装i2c-tools

sudo apt-get install i2c-tools

在这里插入图片描述

2. 显示自己想要的字符:

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>

#include "oled.h"
#include "font.h"

int oled_show(struct display_info *disp) {
    int i;
    char buf[100];
    oled_putstrto(disp, 0, 10, "Welcome to My HomeAssitant");
    disp->font = font1;
    oled_putstrto(disp, 0, 20, " ---This is menu---  ");
    disp->font = font1;
    oled_send_buffer(disp);

return 0;
}

void show_error(int err, int add) {
    //const gchar* errmsg;
    //errmsg = g_strerror(errno);
    printf("\nERROR: %i, %i\n\n", err, add);
    //printf("\nERROR\n");
}

void show_usage(char *progname) {
    printf("\nUsage:\n%s <I2C bus device node >\n", progname);
}

int main(int argc, char **argv) {
    int e;
    char filename[32];
    struct display_info disp;

    if (argc < 2) {
    show_usage(argv[0]);

        return -1;
    }

    memset(&disp, 0, sizeof(disp));
    sprintf(filename, "%s", argv[1]);
    disp.address = OLED_I2C_ADDR;
    disp.font = font2;

    e = oled_open(&disp, filename);
    e = oled_init(&disp);
    oled_show(&disp);

    return 0;
}


六、Linux串口开发

1. wiringPi的串口开发及优化

通过拷贝 / w i r i n g O P / e x a m p l e s / s e r i a l T e s t . c /wiringOP/examples/serialTest.c /wiringOP/examples/serialTest.c 文件下的 w i r i n g P i wiringPi wiringPi 有关串口的例程,我们发现代码有些复杂,通过利用收发的多线程运行,进行了修改和优化,实现了串口收发通信:

这是修改前的代码:

/*
 * serialTest.c:
 *  Very simple program to test the serial port. Expects
 *  the port to be looped back to itself
 *
 * Copyright (c) 2012-2013 Gordon Henderson. <projects@drogon.net>
 ***********************************************************************
 * This file is part of wiringPi:
 *  https://projects.drogon.net/raspberry-pi/wiringpi/
 *
 *    wiringPi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Lesser General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    wiringPi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public License
 *    along with wiringPi.  If not, see <http://www.gnu.org/licenses/>.
 ***********************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <wiringPi.h>
#include <wiringSerial.h>

int main ()
{
  int fd ;
  int count ;
  unsigned int nextTime ;

  if ((fd = serialOpen ("/dev/ttyS2", 115200)) < 0)
  {
    fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
    return 1 ;
  }

  if (wiringPiSetup () == -1)
  {
    fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
    return 1 ;
  }

  nextTime = millis () + 300 ;

  for (count = 0 ; count < 256 ; )
  {
    if (millis () > nextTime)
    {
      printf ("\nOut: %3d: ", count) ;
      fflush (stdout) ;
      serialPutchar (fd, count) ;
      nextTime += 300 ;
      ++count ;
    }

    delay (3) ;

    while (serialDataAvail (fd))
    {
      printf (" -> %3d", serialGetchar (fd)) ;
      fflush (stdout) ;
    }
  }

  printf ("\n") ;
  return 0 ;
}

这是自己修改优化后的代码:

/*
 * serialTest.c:
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdlib.h>

int fd ;

void* SendHandler()
{
    char *sendBuf;
    sendBuf = (char *)malloc(32*sizeof(char));

    while(1){
        memset(sendBuf, '\0', 32);
        scanf("%s",sendBuf);
        while(*sendBuf){
            serialPutchar(fd, *sendBuf++);
        }
    }
}

void* RevHandler()
{
    while(1){
        while (serialDataAvail (fd))
        {
          printf ("%c", serialGetchar (fd)) ;
          fflush (stdout) ;
        }
    }
}

int main ()
{
 int count ;
  unsigned int nextTime ;

  pthread_t idSend;
  pthread_t idRev;

  if ((fd = serialOpen ("/dev/ttyS5", 115200)) < 0)
  {
    fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
    return 1 ;
  }

  pthread_create(&idSend, NULL, SendHandler, NULL);
  pthread_create(&idRev, NULL, RevHandler, NULL);

  if (wiringPiSetup () == -1)
  {
    fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
    return 1 ;
  }

  while(1);
  return 0 ;
}

运行结果:

在这里插入图片描述

在这里插入图片描述

2. 不用wiringPi库自己实现串口通信

上面我们通过修改wiringPi库实现了简单版的串口通信代码,下面我们通过对wiringPi库串口代码的理解,自己实现串口通信。

主程序:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include "uartTool.h"

#include "wiringSerial.h"

int fd;

void* readSerial()
{
    char buffer[32];

    while(1){
        memset(buffer, '\0', sizeof(buffer));
        myserialGetstring(fd, buffer);
        printf("GET->%s\n",buffer);
    }
}

void* sendSerial()
{
    char buffer[32];

    while(1){
        memset(buffer, '\0', sizeof(buffer));
        scanf("%s",buffer);
        myserialSendstring(fd, buffer);
    }
}

int main(int argc, char **argv)
{
    char deviceName[32] = {'\0'};

    pthread_t readt;
    pthread_t sendt;


    if(argc < 2){
        printf("uage:%s /dev/ttyS?\n",argv[0]);
        return -1;
    }

    strcpy(deviceName, argv[1]);

    if((fd = myserialOpen (deviceName, 115200)) == -1){
        printf("open %s error\n",deviceName);
        return -1;
    }

    pthread_create(&readt, NULL, readSerial, NULL);
    pthread_create(&sendt, NULL, sendSerial, NULL);

    while(1);

    return 0;
}

头文件:

int myserialOpen (const char *device, const int baud);
void myserialSendstring (const int fd, const char *s);
int myserialGetstring (const int fd, char *buffer);

头文件中函数程序:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "wiringSerial.h"

int myserialOpen (const char *device, const int baud)
{
    struct termios options ;
    speed_t myBaud ;
    int     status, fd ;

    switch (baud)
  {
        case    9600:   myBaud =    B9600 ; break ;
        case  115200:   myBaud =  B115200 ; break ;
    }
    if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
    return -1 ;

    fcntl (fd, F_SETFL, O_RDWR) ;

    // Get and modify current options:

    tcgetattr (fd, &options) ;

    cfmakeraw   (&options) ;
    cfsetispeed (&options, myBaud) ;
    cfsetospeed (&options, myBaud) ;

    options.c_cflag |= (CLOCAL | CREAD) ;
    options.c_cflag &= ~PARENB ;
    options.c_cflag &= ~CSTOPB ;
     options.c_cflag &= ~CSIZE ;
    options.c_cflag |= CS8 ;
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
    options.c_oflag &= ~OPOST ;

    options.c_cc [VMIN]  =   0 ;
    options.c_cc [VTIME] = 100 ;    // Ten seconds (100 deciseconds)

    tcsetattr (fd, TCSANOW, &options) ;

    ioctl (fd, TIOCMGET, &status);

    status |= TIOCM_DTR ;
    status |= TIOCM_RTS ;

    ioctl (fd, TIOCMSET, &status);

    usleep (10000) ;    // 10mS

    return fd ;

}

void myserialSendstring (const int fd, const char *s)
{
    int ret;
    ret = write (fd, s, strlen (s));
    if (ret < 0)
        printf("Serial Puts Error\n");

}

int myserialGetstring (const int fd, char *buffer)
{
    int n_read;

    n_read = read(fd, buffer, 32);

	return n_read;
}

运行结果:

在这里插入图片描述

在这里插入图片描述



本次学习记录分享就到这里。

点滴知识靠积累。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT阳晨。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值