基于官方外设开发(2)
一、SG90舵机开发
- 舵机基本介绍
如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM。信号控制用处:垃圾桶项目开盖 用、智能小车的全比例转向、摄像头云台、机械臂等。常见的有0-90°、0-180°、0-360°。 - 怎么控制转角
- 向黄色信号线“灌入”PWM信号。
- PWM波的频率不能太高,50hz,即周期=1/频率=1/50=0.02s,20ms左右数据:
- 不同的PWM波形对应不同的旋转角度,以20ms为周期,50hz为频率的PWM波。
- 时器需要定时20ms,关心的单位0.5ms, 20ms = 0.5ms * 40:
- Linux定时器
- 分析:实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理。
- 函数来处理产生的定时信号。从而实现定时器。
- 先看itimerval的结构体
- setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
- which:三种类型
- ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
- ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
- ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
- 很明显,这边需要捕获对应的信号进行逻辑相关处理 signal(SIGALRM,signal_handler);
- 返回说明:成功执行时,返回0。失败返回-1
struct itimerval { /* Value to put into `it_value' when the timer expires. */ struct timeval it_interval; /* Time to the next timer expiration. */ struct timeval it_value; }; //it_interval:计时器的初始值,一般基于这个初始值来加或者来减,看控制函数的参数配置 //it_value:程序跑到这之后,多久启动定时器 struct timeval { __time_t tv_sec; /* Seconds. */ __suseconds_t tv_usec; /* Microseconds. */ }; int setitimer (__itimer_which_t __which, const struct itimerval *__restrict __new, struct itimerval *__restrict __old)
- 实现代码:
/*该代码实现的功能是: 1s后开启定时器,然后每隔1s向终端打印hello。*/ #include <stdio.h> #include <sys/time.h> #include <stdlib.h> #include <signal.h> static int cnt; void sig_handler(int num) { cnt++; if(cnt == 2000) { cnt = 0; printf("Hello\n"); } } int main(void) { 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 = 0; //设定定时方式 if(-1 == setitimer(ITIMER_REAL,&itv,NULL)){ perror("error"); exit(-1); } signal(SIGALRM, sig_handler); while(1); return 0; }
- 这种方法需要注意的是,一个进程只能创建一个定时器
- SG90编程实现:键盘输入不同的值,让舵机转动,软件PWM实现
#include <stdio.h> #include <sys/time.h> #include <stdlib.h> #include <signal.h> #include <wiringPi.h> static int cnt; int jd = 0; #define SG90 5 void sig_handler(int num) { if(cnt <= jd) { digitalWrite(SG90, HIGH); } else { digitalWrite(SG90, LOW); } if(cnt == 40) { cnt = 0; } cnt++; } int main(void) { struct itimerval itv; jd = 0; wiringPiSetup(); pinMode(SG90, OUTPUT); itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 500; itv.it_value.tv_sec = 1; itv.it_value.tv_usec = 0; //设定定时方式 if(-1 == setitimer(ITIMER_REAL,&itv,NULL)){ perror("error"); exit(-1); } signal(SIGALRM, sig_handler); while(1) { printf("input jd 1-0 2-45 3-90 4-135\n"); scanf("%d",&jd); } return 0; }
二、OLED屏应用-IIC协议
- OLED屏幕
- Orangepi的IIC接口
- 由 26pin 的原理图可知, Orange Pi Zero 2 可用的 i2c 为 i2c3
- 启动 linux 系统后, 先确认下/dev 下存在 i2c-3 的设备节点
- 从命令运行结果能观察到系统支持I2C-3和I2C-5的驱动,而H616的外设我们看到只有一个IIC接口,用的是IIC-3
- Linux一切皆文件,每个硬件设备“对应”一个文件,由驱动程序提供映射
- 由 26pin 的原理图可知, Orange Pi Zero 2 可用的 i2c 为 i2c3
- 开始测试 i2c, 首先安装 i2c-tools
- sudo apt-get install i2c-tools
- sudo apt-get install i2c-tools
- 实战OLED屏幕开发
/* * Copyright (c) 2015, Vladimir Komendantskiy * MIT License * * SSD1306 demo of block and font drawing. */ // // fixed for OrangePiZero by HypHop // #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_demo(struct display_info *disp) { int i; char buf[100]; //putstrto(disp, 0, 0, "Spnd spd 2468 rpm"); // oled_putstrto(disp, 0, 9+1, "Spnd cur 0.46 A"); oled_putstrto(disp, 0, 9+1, "Welcome to"); disp->font = font1; // oled_putstrto(disp, 0, 18+2, "Spnd tmp 53 C"); oled_putstrto(disp, 0, 18+2, "----OrangePi----"); disp->font = font2; // oled_putstrto(disp, 0, 27+3, "DrvX tmp 64 C"); oled_putstrto(disp, 0, 27+3, "This is 0.96OLED"); oled_putstrto(disp, 0, 36+4, ""); oled_putstrto(disp, 0, 45+5, ""); disp->font = font1; // oled_putstrto(disp, 0, 54, "Total cur 2.36 A"); oled_putstrto(disp, 0, 54, "*****************"); oled_send_buffer(disp); disp->font = font3; for (i=0; i<100; i++) { sprintf(buf, "Spnd spd %d rpm", i); oled_putstrto(disp, 0, 0, buf); oled_putstrto(disp, 135-i, 36+4, "==="); oled_putstrto(disp, 100, 0+i/2, "."); oled_send_buffer(disp); } //oled_putpixel(disp, 60, 45); //oled_putstr(disp, 1, "hello"); 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); if (e < 0) { show_error(1, e); } else { e = oled_init(&disp); if (e < 0) { show_error(2, e); } else { printf("---------start--------\n"); if (oled_demo(&disp) < 0) show_error(3, 777); printf("----------end---------\n"); } } return 0; }