SG90舵机开发
舵机基本介绍
如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制用处:垃圾桶项目开盖 用、智能小车的全比例转向、摄像头云台、机械臂等 常见的有0-90°、0-180°、0-360°
接线
红色线:5v
黄色线:GND
灰色线:5号口
怎么控制转角
向黄色信号线“灌入”PWM信号。 PWM波的频率不能太高,50hz,即周期=1/频率=1/50=0.02s,20ms左右数据: 不同的PWM波形对应不同的旋转角度,以20ms为周期,50hz为频率的PWM波
高电平持续时间 | 低电平持续时间 | 波形图 | 角度 |
---|---|---|---|
0.5ms | 19.5ms | ![]() | 0 |
1ms | 19ms | ![]() | 45 |
1.5ms | 18.5ms | ![]() | 90 |
2.0ms | 18ms | 如上图推理 | 135 |
25ms | 17.5ms | 如上图推理 | 180 |
定时器需要定时20ms,关心的单位0.5ms, 20ms = 0.5ms * 40:
Linux定时器
**分析:**实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理 函数来处理产生的定时信号。从而实现定时器。 先看itimerval的结构体
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)
setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
which:三种类型
ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。 ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。 ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
很明显,这边需要捕获对应的信号进行逻辑相关处理signal(SIGALRM,signal_handler);
返回说明:
成功执行时,返回0。失败返回-1
实现代码:
/*创建一个每0.5秒触发一次的定时器,并且在第一次触发1秒后,每隔0.5秒触发一次。每次触发时,都会调用signal_handler函数,该函数内部有一个计数器i,当计数器达到2000时,会打印出"hello",并将计数器重置为0,以便重新开始计数。程序将一直运行,直到外部中断。*/
#include <stdio.h> // 标准输入输出库
#include <sys/time.h> // 系统时间库,用于时间相关的操作
#include <stdlib.h> // 标准库,提供一些通用函数,如exit
#include <signal.h> // 信号处理库
static int i; // 静态变量i,用于计数
// 信号处理函数,当接收到SIGALRM信号时被调用
void signal_handler(int signum) {
i++; // 每次信号到来时,i的值增加1
if(i == 2000){ // 每接收到2000次信号,输出hello
printf("hello\n");
i = 0; // 重置i的值为0,重新开始计数
}
}
int main() {
struct itimerval itv; // 定义一个itimerval结构体,用于设置定时器
// 设置定时器的间隔,即每隔一段时间就发送SIGALRM信号
itv.it_interval.tv_sec = 0; // 间隔的秒数设置为0
itv.it_interval.tv_usec = 500; // 间隔的微秒数设置为500,即0.5秒
// 设置定时器的初始值,即定时器启动后多久发送第一次SIGALRM信号
itv.it_value.tv_sec = 1; // 初始值的秒数设置为1秒
itv.it_value.tv_usec = 0; // 初始值的微秒数设置为0
// 使用setitimer设置定时器
if(-1 == setitimer(ITIMER_REAL, &itv, NULL)){ // 如果设置失败
perror("error"); // 打印错误信息
exit(-1); // 退出程序
}
// 注册SIGALRM信号的处理函数为signal_handler
signal(SIGALRM, signal_handler);
while(1); // 无限循环,等待信号的到来
return 0;
}
这种方法需要注意的是,一个进程只能创建一个定时器
代码与运行结果
1.本次使用的舵机,与前面描述的舵机在控制角度上略有差异,输出占空比为 2.5% 的 PWM 信号时,舵机旋转至 0 度的位置,输出占空比为 12.5% 的 PWM 信号时,舵机旋转至 180 度。
#include <stdio.h> // 标准输入输出库
#include <stdlib.h> // 标准库,提供exit等函数
#include <wiringPi.h> // Wiring Pi GPIO操作库
#include <softPwm.h> // Wiring Pi提供的软件PWM库
#define PWM_PIN 5 // 定义PWM信号输出的GPIO引脚编号为5
#define PWM_CYCLE 200 // 设置PWM周期为20毫秒
#define PWM_DUTY_MAX 25 // 设置PWM最大占空比为12.5%(2.5毫秒脉宽)
#define PWM_DUTY_MIN 5 // 设置PWM最小占空比为2.5%(0.5毫秒脉宽)
int main(void) {
if (wiringPiSetup() == -1) // 初始化WiringPi库
exit(1); // 如果初始化失败,退出程序
softPwmCreate(PWM_PIN, PWM_DUTY_MAX, PWM_CYCLE); // 初始化指定的PWM引脚,设置最大脉宽和周期
int val = PWM_DUTY_MAX; // 初始化PWM脉宽变量
int mark = 0; // 初始化转向标志
while (1) { // 无限循环,持续控制舵机
if (mark == 0) // 如果当前是递减过程
val--; // 脉宽减1
else // 否则(当前是递增过程)
val++; // 脉宽加1
if (val <= PWM_DUTY_MIN) // 如果脉宽小于等于最小脉宽
mark = 1; // 切换到递增过程
if (val >= PWM_DUTY_MAX) // 如果脉宽大于等于最大脉宽
mark = 0; // 切换到递减过程
softPwmWrite(PWM_PIN, val); // 写入当前脉宽值到PWM引脚
delay(100); // 等待100毫秒,产生可见的舵机转动
}
return 0; // 正常退出程序,但实际不会执行到这里,因为while(1)是无限循环
}
2.SG90编程实现:键盘输入不同的值,让舵机转动,软件PWM实现
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <wiringPi.h>
#include <softPwm.h>
#define SG90Pin 5 // 定义舵机连接的GPIO引脚编号
#define PWM_MIN 5 // 舵机最小PWM脉宽值
#define PWM_MAX 25 // 舵机最大PWM脉宽值
#define PWM_RANGE (PWM_MAX - PWM_MIN) // PWM脉宽值范围
int jd; // 用户输入的角度值
// 信号处理函数,根据用户输入的角度值调整PWM占空比
void signal_handler(int signum) {
// 根据用户输入的角度和PWM脉宽值范围计算PWM的占空比
int pwmValue = PWM_MIN + (PWM_RANGE * jd) / 180;
softPwmWrite(SG90Pin, pwmValue); // 设置PWM占空比
}
int main() {
struct itimerval itv;
jd = 0;
if (wiringPiSetup() == -1) {
exit(1); // 初始化wiringPi失败则退出
}
pinMode(SG90Pin, OUTPUT); // 设置SG90Pin为输出模式
softPwmCreate(SG90Pin, PWM_MIN, PWM_MAX); // 创建软件PWM
// 设置定时器间隔为0.5秒,用于周期性更新PWM值
itv.it_interval.tv_sec = 0;
itv.it_interval.tv_usec = 500;
// 设置定时器第一次触发时间为1秒后
itv.it_value.tv_sec = 1;
itv.it_value.tv_usec = 0;
if (-1 == setitimer(ITIMER_REAL, &itv, NULL)) {
perror("error"); // 设置定时器失败则打印错误并退出
exit(-1);
}
// 注册信号SIGALRM的处理函数
signal(SIGALRM, signal_handler);
while (1) {
printf("input angle (0-180): \n"); // 提示用户输入角度
scanf("%d", &jd); // 读取用户输入的角度
if (jd < 0 || jd > 180) {
printf("Angle out of range. Please enter a value between 0 and180.\n");
jd = 0; // 重置角度为0
}
}
return 0;
}