目录
一、舵机的基本认知和硬件接线
1.1 舵机的基本认知:
1.2 硬件接线:
1.3 怎么控制舵机旋转不同的角度:
二、Linux定时器
2.1 定时器setitimer()函数原型和头文件:
2.2 信号处理函数signal()原型和头文件:
2.3 定时器setitimer()函数应用实例:
2.4 使用wiringOP库配合定时器驱动舵机旋转0-180°:
一、舵机的基本认知和硬件接线
1.1 舵机的基本认知:
如下图所示:是一个SG90舵机,常用三根或者四根接线,红色为电源VCC,棕色为电源GND,黄色为PWM信号。
控制用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等常见的有0-90°、0-180°、0-360°
1.2 硬件接线:
1.3 怎么控制舵机旋转不同的角度:
-
向黄色信号线“灌入”PWM信号。PWM波的频率不能太高,50hz,即周期=1/频率=1/50=0.02s,20ms左右数据:不同的PWM波形对应不同的旋转角度,以20ms为周期,50hz为频率的PWM波
定时器需要定时20ms,关心的单位0.5ms, 20ms = 0.5ms * 40
二、Linux定时器
2.1 定时器setitimer()函数原型和头文件:
/* setitimer 是一个UNIX系统上的系统调用函数,用于设置和管理定时器。它通常用于定期触发信号或执行某些操作 */
#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
int 函数返回值,成功执行时返回0,失败返回-1
int whic 指定要设置的定时器类型,可以是 ITIMER_REAL、ITIMER_VIRTUAL 或 ITIMER_PROF 中的一个
1. ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
2. ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
3. ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
struct itimerval *new_value 一个struct itimerval结构,用于指定新的定时器值。struct itimerval 结构定义如下:
struct itimerval {
struct timeval it_interval; // 定时器重复的间隔时间
struct timeval it_value; // 定时器的初始值
};
it_interval 计时器的初始值,一般基于这个初始值来加或者来减,看控制函数的参数配置
it_value 程序跑到这之后,多久启动定时器
struct timeval {
__time_t tv_sec; /* 秒 */
__suseconds_t tv_usec; /* 微秒 */
};
struct itimerval *old_value 一个struct itimerval 结构,用于存储旧的定时器值(可选参数)
/*
函数说明:
实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理函数来处理产生的定时信号。从而实现定时器。
*/
2.2 信号处理函数signal()原型和头文件:
/*
Linux下 man 2 signal查看手册
*/
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
sighandler_t 函数返回值,返回信号处理程序的前一个值,或者在错误时SIG ERR。如果发生错误,则设置errno来指示原因。
int signum 指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号
sighandler_t handler 描述了与信号关联的动作,它可以取以下三种值:
1. 一个无返回值的函数地址
此函数必须在signal()被调用前声明,handler中为这个函数的名字。当接收到一个类型为signum的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:
void handler(int signum);
2. SIG_IGN
这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。
3. SIG_DFL
这个符号表示恢复系统对信号的默认处理。
2.3 定时器setitimer()函数应用实例:
实现的功能是:1秒后打开定时器,然后每隔一秒打印一次Hello
#include <stdio.h> // 包含标准输入输出库,用于输入输出操作
#include <sys/time.h> // 包含时间处理库,用于获取系统时间
#include <stdlib.h> // 包含标准库,用于内存分配和程序终止等操作
#include <signal.h> // 包含信号处理库,用于处理系统信号
static int i = 0; //全局变量i
void signal_handler(int signum) //信号处理函数
{
i++;
if(i == 2000){ //每2000次触发一次
printf("Hello\n");
i = 0;
}
}
int main()
{
struct itimerval itv; //定义一个itimerval结构体变量
//设定定时时间
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 500; //每隔0.5毫秒触发一次
//设定启动定时器时间
itv.it_value.tv_sec = 1; //启动定时器1秒后触发
itv.it_value.tv_usec = 0;
//设定定时方式
//int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
if(setitimer(ITIMER_REAL, &itv, NULL) == -1){ //设置定时器
perror("setitimer");
exit(-1);
}
//信号处理
signal( SIGALRM, signal_handler); //设置信号处理函数
while(1);
return 0;
}
2.4 使用wiringOP库配合定时器驱动舵机旋转0-180°:
#include <stdio.h> // 包含标准输入输出库,用于输入输出操作
#include <sys/time.h> // 包含时间处理库,用于获取系统时间
#include <stdlib.h> // 包含标准库,用于内存分配和程序终止等操作
#include <signal.h> // 包含信号处理库,用于处理系统信号
#include <wiringPi.h> // 包含WiringPi库,用于控制GPIO
#define SG90Pin 5 // 定义SG90的引脚号
static int i = 0; //全局变量i
int jd; //全局变量jd
void signal_handler(int signum) //信号处理函数
{
if(i <= jd){
digitalWrite(SG90Pin, HIGH); //设置SG90引脚为高电平
}else{
digitalWrite(SG90Pin, LOW); //设置SG90引脚为低电平
}
if(i == 40){ //每40次信号处理函数被调用一次,输出一次当前时间
i = 0; //i归零
}
i++;
}
int main()
{
struct itimerval itv; //定义一个itimerval结构体变量
jd = 0; //初始化jd为0
wiringPiSetup(); //初始化WiringPi库
pinMode(SG90Pin, OUTPUT); //设置SG90引脚为输出模式
//设定定时时间
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 500; //每隔0.5毫秒触发一次
//设定启动定时器时间
itv.it_value.tv_sec = 1; //启动定时器1秒后触发
itv.it_value.tv_usec = 0;
//设定定时方式
//int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
if(setitimer(ITIMER_REAL, &itv, NULL) == -1){ //设置定时器
perror("setitimer");
exit(-1);
}
//信号处理
signal( SIGALRM, signal_handler); //设置信号处理函数
while(1){
printf("请输入你想要的角度: 1-0 2-45 3-90 4-135 5-180\n");
scanf("%d",&jd); //输入角度
}
return 0;
}