【十七】视觉垃圾桶-功能完善优化
文章目录
一、增加垃圾桶开关盖
1.引脚
实现功能:使用语音模块和摄像头在香橙派上做垃圾智能分类识别, 同时根据识别结果开关不同的垃圾桶的盖子。
主要用到的PWM引脚:5和7。
2.PWM 频率的公式
这个 PWM 频率的公式可以更详细地表示为:
P
W
M
f
r
e
q
=
1
×
1
0
6
(
p
u
l
s
e
−
w
i
d
t
h
)
×
r
a
n
g
e
PWMfreq = \frac{1 \times 10^6}{(pulse-width) \times range}
PWMfreq=(pulse−width)×range1×106
其中:
-
P W M f r e q PWMfreq PWMfreq :是 PWM 的频率(赫兹)。
-
1 × 1 0 6 {1 \times 10^6} 1×106 :是为了将频率从赫兹(Hz)转换为微秒(μs)。
-
$pulse-width $ 是每个 PWM 脉冲的宽度(微秒)。
-
× r a n g e \times range ×range 是 PWM 的范围,即 PWM 值的最大范围。
这个公式的基本思想是,PWM 的频率与脉冲宽度和范围有关。脉冲宽度表示每个 PWM 脉冲的持续时间,而范围表示 PWM 值的最大范围。通过调整这两个参数,可以控制 PWM 的频率。
在之前《官方外设开发》一节中我们也讲到了舵机的开发,我们用到的是定时器模拟pwm,一个进程只能创建一个定时器,也就意味着只能驱动一个舵机,所以如果我们想要驱动多个的话,就需要下面的方法了。
当range
设置为200时,PWM的频率将是50Hz。这里的range
值表示PWM周期被分成200个步进,每个步进的时间为:0.5微秒。
每个步进时间
=
周期时间
步数
=
100
μ
s
200
=
0.5
μ
s
\text{每个步进时间} = \frac{\text{周期时间}}{\text{步数}} = \frac{100\mu s}{200} = 0.5\mu s
每个步进时间=步数周期时间=200100μs=0.5μs
3.PWM_API
softPwmCreate
和 softPwmWrite
函数用于在不支持硬件PWM或者需要额外PWM通道的微控制器或类似设备上实现软件PWM控制。虽然这些函数不是标准C库的一部分,但是它们的API可能有如下特点:
softPwmCreate
-
函数原型:
void softPwmCreate(uint8_t pin, uint16_t value, uint16_t range);
-
参数:
pin
:指定用于PWM输出的数字引脚编号。value
:初始PWM值,可以是0(表示PWM输出为0%占空比)或其他值,取决于range
参数。range
:定义PWM周期的分辨率。在这个范围内,PWM值将被分割,从而控制占空比。
-
功能:初始化指定引脚的软件PWM功能,并设置其初始值和PWM周期的分辨率。
softPwmWrite
-
函数原型:
void softPwmWrite(uint8_t pin, uint16_t value);
-
参数:
pin
:指定要修改PWM值的数字引脚编号。value
:新的PWM值,这个值将影响引脚的占空比,其具体范围由softPwmCreate
函数中的range
参数决定。
-
功能:设置指定引脚的PWM占空比。这个函数通常在PWM创建后被调用来改变输出信号的占空比。
附加说明
- 周期和频率:PWM的周期是高电平和低电平时间的总和,频率是周期的倒数。在
softPwmCreate
中设置的range
参数会影响周期的计算。 - 精度:
range
参数的值越大,PWM的控制精度越高,但同时可能需要更复杂的算法来计算定时器中断。 - 线程安全:在多线程环境中使用这些API时,需要注意线程安全问题,以避免竞态条件。
- 平台依赖性:这些API可能是针对特定硬件平台或软件库设计的,因此它们的实现和行为可能会因平台而异。
请注意,由于softPwmCreate
和softPwmWrite
不是标准API,具体的函数原型、参数和行为可能会根据实际使用的库或框架有所不同。
softPwmStop
softPwmStop
函数用于停止软件PWM信号的API函数。
函数原型示例:
void softPwmStop(uint8_t pin);
pin
:指定要停止PWM输出的数字引脚编号。
注意事项:
- 在使用
softPwmStop
之前,确保已经通过softPwmCreate
成功创建了PWM信号。 - 如果
softPwmStop
用于释放资源,确保在重新使用PWM功能之前重新初始化。 - 在多线程环境中,如果
softPwmStop
影响共享资源,需要考虑线程同步和互斥。
4.代码
pwm.c
pwm.c
(增加用于实现开关盖(驱动舵机)的源码文件)
#include <wiringPi.h>
#include <softPwm.h>
//根据公式:PWMfreq = 1 x 10^6 / (100 x range) ,要得到PWM频率为50Hz,则range为200,即周期分为200步,控制精度相比硬件PWM较低。
void pwm_write(int pwm_pin)
{
pinMode(pwm_pin, OUTPUT);
softPwmCreate(pwm_pin,0,200);// 起始值为0,周期范围设置为200步。, 周期20ms
softPwmWrite(pwm_pin,10);//1ms 45度,这将产生一个占空比为5%的PWM信号(因为10是200步中的一小部分)。
delay(1000);
softPwmStop(pwm_pin);
}
void pwm_stop(int pwm_pin)
{
pinMode(pwm_pin, OUTPUT);
softPwmCreate(pwm_pin,0,200);// range设置周期分为200步, 周期20ms
softPwmWrite(pwm_pin,5);//0.5ms 0度
delay(1000);
softPwmStop(pwm_pin);
}
pwm.h
pwm.h
#ifndef __PWM__H
#define __PWM__H
#define PWM_GARBAGE 7
#define PWM_RECOVERABLE_GARBAGE 5
void pwm_write(int pwm_pin);
void pwm_stop(int pwm_pin);
#endif
main.c
main.c
里增加调用舵机的控制代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
static int detect_process(const char *process_name)
{
int n = -1;
FILE *strm;
char buf[128]={0};
sprintf(buf,"ps -ax | grep %s|grep -v grep", process_name);
if((strm = popen(buf, "r")) != NULL)
{
if(fgets(buf, sizeof(buf), strm) != NULL)
{
printf("buf=%s\n",buf);
n = atoi(buf);
printf("n=%d\n",n);
}
}
else
{
return -1;
}
pclose(strm);
return n;
}
int main(int argc, char *argv[])
{
int serial_fd = -1;
int len = 0;
int ret = -1;
char *category = NULL;
unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};
wiringPiSetup();
garbage_init();
ret = detect_process("mjpg_streamer");
if ( -1 == ret)
{
printf("detect process failed\n");
goto END;
}
serial_fd = myserialOpen(SERIAL_DEV, BAUD);
if (-1 == serial_fd)
{
printf("open serial failed\n");
goto END;
}
while(1)
{
len = serialGetstring(serial_fd, buffer);
if (len > 0 && buffer[2] == 0x46)
{
buffer[2] = 0x00;
system(WGET_CMD);
if (0 == access(GARBAGE_FILE, F_OK))
{
category = garbage_category(category);
if (strstr(category, "干垃圾"))
{
buffer[2] = 0x41;
}
else if (strstr(category, "湿垃圾"))
{
buffer[2] = 0x42;
}
else if (strstr(category, "可回收垃圾"))
{
buffer[2] = 0x43;
}
else if (strstr(category, "有害垃圾"))
{
buffer[2] = 0x44;
}
else
{
buffer[2] = 0x45;
}
}
else
{
buffer[2] = 0x45;
}
printf("buffer[2] =%d\n", buffer[2]);
serialSendstring(serial_fd, buffer, 6);
if (buffer[2] == 0x43)
{
pwm_write(PWM_RECOVERABLE_GARBAGE);
delay(2000);
pwm_stop(PWM_RECOVERABLE_GARBAGE);
}
else if (buffer[2] != 0x45)
{
printf("start\n");
pwm_write(PWM_GARBAGE);
delay(2000);
pwm_stop(PWM_GARBAGE);
}
buffer[2] = 0x00;
remove(GARBAGE_FILE);
}
}
END:
garbage_final();
return 0;
}
二、项目代码优化
在之前实现的代码中, 主函数是单线程执行的, 导致整个代码的可扩展性非常差,比如想加OLED显示或者添加网络控制变得非常复杂,
而且执行一次识别开关盖的流程非常长。因此,调整下代码架构,增加并发功能、提升代码的可扩展性和执行效率。
- 代码大致流程图如下:
- 修改main.c代码,调整整体main函数的代码架构,利用多线程实现具体的功能(用到了线程里的条件变量控制线程间的数据同步)如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include <pthread.h>
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
int serial_fd = -1; // 串口文件描述符
pthread_cond_t cond; // 条件变量,用于线程之间的条件同步
pthread_mutex_t mutex; // 互斥锁,用于线程之间的互斥访问
// 判断进程是否在运行
static int detect_process(const char * process_name)
{
int n = -1; // 存储进程PID,默认为-1
FILE *strm;
char buf[128] = {0}; // 缓冲区
// 构造命令字符串,通过ps命令查找进程
sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name);
// 使用popen执行命令并读取输出
if ((strm = popen(buf, "r")) != NULL) {
if (fgets(buf, sizeof(buf), strm) != NULL) {
printf("buf = %s\n", buf); //打印缓存区的内容
n = atoi(buf); // 将进程ID字符串转换为整数
printf("n = %d\n", n); // 打印下进程的PID
}
}
else {
return -1; // popen失败
}
pclose(strm); // 关闭popen打开的文件流
return n;
}
// 发送语音线程
void *psend_voice(void *arg)
{
pthread_detach(pthread_self());
unsigned char *buffer = (unsigned char *)arg;
// 串口未打开,退出线程
if (-1 == serial_fd) {
printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);
pthread_exit(0);
}
// buffer不为空时,通过串口发送数据(分类结果)
if (NULL != buffer) {
my_serialSendstring(serial_fd, buffer, 6);
}
pthread_exit(0);
}
// 控制垃圾桶线程
void *popen_trash_can(void *arg)
{
pthread_detach(pthread_self());
unsigned char *buffer = (unsigned char *)arg;
// 根据垃圾类型控制PWM
if (buffer[2] == 0x43|buffer[2] == 0x42|buffer[2] == 0x44) { // 可回收垃圾,湿垃圾,有害垃圾
//printf("%s|%s|%d: buffer[2] = 0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);
pwm_write(PWM_RECOVERABLE_GARBAGE);
delay(2000);
pwm_stop(PWM_RECOVERABLE_GARBAGE);
}
else if (buffer[2] == 0x41) { // 干垃圾
//printf("%s|%s|%d: buffer[2] = 0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);
pwm_write(PWM_GARBAGE);
delay(2000);
pwm_stop(PWM_GARBAGE);
}
pthread_exit(0);
}
// 获取语音线程
void *pget_voice(void *arg)
{
unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};
int len = 0;
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
// 串口未打开,退出线程
if (-1 == serial_fd) {
printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);
pthread_exit(0);
}
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
// 循环读取串口数据
while (1) {
len = my_serialGetstring(serial_fd, buffer);
//printf("%s|%s|%d, len = %d\n", __FILE__, __func__, __LINE__, len);
// 检测到特定数据,发出信号唤醒其他线程
if (len > 0 && buffer[2] == 0x46) {
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
pthread_mutex_lock(&mutex);
buffer[2] = 0x00;
system(WGET_CMD);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
pthread_exit(0);
}
// 阿里云垃圾分类线程
void *pcategory(void *arg)
{
unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};
char *category = NULL;
pthread_t send_voice_tid, trash_tid;
while (1) {
//printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
//printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);
buffer[2] = 0x00;
// 在执行wget命令之前添加调试输出
printf("Executing wget command...\n");
// 使用系统命令拍照
system(WGET_CMD);
// 在执行wget命令之后添加调试输出
printf("Wget command executed.\n");
// 判断垃圾种类
if (0 == access(GARBAGE_FILE, F_OK)) {
category = garbage_category(category);
if (strstr(category, "干垃圾")) {
buffer[2] = 0x41;
}
else if (strstr(category, "湿垃圾")) {
buffer[2] = 0x42;
}
else if (strstr(category, "可回收垃圾")) {
buffer[2] = 0x43;
}
else if (strstr(category, "有害垃圾")) {
buffer[2] = 0x44;
}
else {
buffer[2] = 0x45; // 未识别到垃圾类型
}
}
else {
buffer[2] = 0x45; // 识别失败
}
// 开垃圾桶开关
pthread_create(&trash_tid, NULL, psend_voice, (void *)buffer);
// 开语音播报线程
pthread_create(&send_voice_tid, NULL, popen_trash_can, (void *)buffer);
// 删除拍照文件
remove(GARBAGE_FILE);
}
pthread_exit(0);
}
int main(int argc, char *argv[])
{
int ret = -1;
int len = 0;
char *category = NULL;
pthread_t get_voice_tid, category_tid;
wiringPiSetup();
// 初始化串口和垃圾分类模块
garbage_init ();
// 用于判断mjpg_streamer服务是否已经启动
ret = detect_process ("mjpg_streamer");
if (-1 == ret) {
printf("detect process failed\n");
goto END;
}
// 打开串口
serial_fd = my_serialOpen (SERIAL_DEV, BAUD);
if (-1 == serial_fd) {
printf("open serial failed\n");
goto END;
}
// 开语音线程
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
pthread_create(&get_voice_tid, NULL, pget_voice, NULL);
// 开阿里云交互线程
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
pthread_create(&category_tid, NULL, pcategory, NULL);
//等待线程终止并获取返回值
pthread_join(get_voice_tid, NULL);
pthread_join(category_tid, NULL);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
END:
// 关闭串口
close(serial_fd);
// 释放垃圾分类资源
garbage_final();
return 0;
}
编译运行
编译
gcc -o test *.c *.h -I /usr/include/python3.10 -l python3.10 -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt
执行
sudo -E ./test
查看进程
ps -ax | grep mjpg_streamer | grep -v grep
ps -ax | grep ./test | grep -v grep
ps aux | grep './test' | grep -v grep | awk '{print $2}'
三、增加OLED 屏幕显示功能
详细可参照《官方外设开发》一节:
在之前请确保已经配置好环境:查看是否已经加上i2c-3,如果没有的话自己加上
cat /boot/orangepiEnv.txt
ls -a /dev/i2c-3
myoled.h
#ifndef __MYOLED__H
#define __MYOLED__H
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include "oled.h"
#include "font.h"
#define FILENAME "/dev/i2c-3"
int myoled_init(void);
int oled_show(void *arg);
#endif
myoled.c
#include <myoled.h>
struct display_info disp;
int myoled_init(void)
{
int e;
memset(&disp, 0, sizeof(disp));
disp.address = OLED_I2C_ADDR;
disp.font = font2;
oled_open(&disp, FILENAME);
e = oled_init(&disp);
return e;
}
int oled_show(void *arg)
{
unsigned char *buffer = (unsigned char *)arg;
// 在 OLED 上显示提示信息
oled_putstrto(&disp, 0, 9+1, "THis garbage is:");
disp.font = font2;
// 根据垃圾类型显示相应信息
switch(buffer[2])
{
case 0x41:
oled_putstrto(&disp, 0, 20, "Dry_garbage");
break;
case 0x42:
oled_putstrto(&disp, 0, 20, "Wet_garbage");
break;
case 0x43:
oled_putstrto(&disp, 0, 20, "Recycle_garbage");
break;
case 0x44:
oled_putstrto(&disp, 0, 20, "Hazardous_garbage");
break;
case 0x45:
oled_putstrto(&disp, 0, 20, "recognition failed");
break;
}
disp.font = font2;
// 发送显示缓冲区到 OLED
oled_send_buffer(&disp);
//oled_putpixel(disp, 60, 45);
//oled_putstr(disp, 1, "hello");
return 0;
}
main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include <pthread.h>
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.h"
int serial_fd = -1; // 串口文件描述符
pthread_cond_t cond; // 条件变量,用于线程之间的条件同步
pthread_mutex_t mutex; // 互斥锁,用于线程之间的互斥访问
// 判断进程是否在运行
static int detect_process(const char * process_name)
{
int n = -1; // 存储进程PID,默认为-1
FILE *strm;
char buf[128] = {0}; // 缓冲区
// 构造命令字符串,通过ps命令查找进程
sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name);
// 使用popen执行命令并读取输出
if ((strm = popen(buf, "r")) != NULL) {
if (fgets(buf, sizeof(buf), strm) != NULL) {
printf("buf = %s\n", buf); //打印缓存区的内容
n = atoi(buf); // 将进程ID字符串转换为整数
printf("n = %d\n", n); // 打印下进程的PID
}
}
else {
return -1; // popen失败
}
pclose(strm); // 关闭popen打开的文件流
return n;
}
// 发送语音线程
void *psend_voice(void *arg)
{
pthread_detach(pthread_self());
unsigned char *buffer = (unsigned char *)arg;
// 串口未打开,退出线程
if (-1 == serial_fd) {
printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);
pthread_exit(0);
}
// buffer不为空时,通过串口发送数据(分类结果)
if (NULL != buffer) {
my_serialSendstring(serial_fd, buffer, 6);
}
pthread_exit(0);
}
// 控制垃圾桶线程
void *popen_trash_can(void *arg)
{
pthread_detach(pthread_self());
unsigned char *buffer = (unsigned char *)arg;
// 根据垃圾类型控制PWM
if (buffer[2] == 0x43|buffer[2] == 0x42|buffer[2] == 0x44) { // 可回收垃圾,湿垃圾,有害垃圾
//printf("%s|%s|%d: buffer[2] = 0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);
pwm_write(PWM_RECOVERABLE_GARBAGE);
delay(2000);
pwm_stop(PWM_RECOVERABLE_GARBAGE);
}
else if (buffer[2] == 0x41) { // 干垃圾
//printf("%s|%s|%d: buffer[2] = 0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);
pwm_write(PWM_GARBAGE);
delay(2000);
pwm_stop(PWM_GARBAGE);
}
pthread_exit(0);
}
void *popen_trash_can(void *arg)
{
pthread_detach(pthread_self());
// 初始化 OLED
myoled_init();
// 在 OLED 上显示垃圾分类结果
oled_show(arg);
pthread_exit(0);
}
// 获取语音线程
void *pget_voice(void *arg)
{
unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};
int len = 0;
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
// 串口未打开,退出线程
if (-1 == serial_fd) {
printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);
pthread_exit(0);
}
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
// 循环读取串口数据
while (1) {
len = my_serialGetstring(serial_fd, buffer);
//printf("%s|%s|%d, len = %d\n", __FILE__, __func__, __LINE__, len);
// 检测到特定数据,发出信号唤醒其他线程
if (len > 0 && buffer[2] == 0x46) {
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
pthread_mutex_lock(&mutex);
buffer[2] = 0x00;
system(WGET_CMD);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
pthread_exit(0);
}
// 阿里云垃圾分类线程
void *pcategory(void *arg)
{
unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};
char *category = NULL;
pthread_t send_voice_tid, trash_tid,oled_tid;
while (1) {
//printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
//printf("%s|%s|%d: \n", __FILE__, __func__, __LINE__);
buffer[2] = 0x00;
// 在执行wget命令之前添加调试输出
printf("Executing wget command...\n");
// 使用系统命令拍照
system(WGET_CMD);
// 在执行wget命令之后添加调试输出
printf("Wget command executed.\n");
// 判断垃圾种类
if (0 == access(GARBAGE_FILE, F_OK)) {
category = garbage_category(category);
if (strstr(category, "干垃圾")) {
buffer[2] = 0x41;
}
else if (strstr(category, "湿垃圾")) {
buffer[2] = 0x42;
}
else if (strstr(category, "可回收垃圾")) {
buffer[2] = 0x43;
}
else if (strstr(category, "有害垃圾")) {
buffer[2] = 0x44;
}
else {
buffer[2] = 0x45; // 未识别到垃圾类型
}
}
else {
buffer[2] = 0x45; // 识别失败
}
// 开垃圾桶开关
pthread_create(&trash_tid, NULL, psend_voice, (void *)buffer);
// 开语音播报线程
pthread_create(&send_voice_tid, NULL, popen_trash_can, (void *)buffer);
//开OLED屏幕线程
pthread_create(&oled_tid, NULL, popen_trash_can, (void *)buffer);
// 删除拍照文件
remove(GARBAGE_FILE);
}
pthread_exit(0);
}
int main(int argc, char *argv[])
{
int ret = -1;
int len = 0;
char *category = NULL;
pthread_t get_voice_tid, category_tid;
wiringPiSetup();
// 初始化串口和垃圾分类模块
garbage_init ();
// 用于判断mjpg_streamer服务是否已经启动
ret = detect_process ("mjpg_streamer");
if (-1 == ret) {
printf("detect process failed\n");
goto END;
}
// 打开串口
serial_fd = my_serialOpen (SERIAL_DEV, BAUD);
if (-1 == serial_fd) {
printf("open serial failed\n");
goto END;
}
// 开语音线程
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
pthread_create(&get_voice_tid, NULL, pget_voice, NULL);
// 开阿里云交互线程
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
pthread_create(&category_tid, NULL, pcategory, NULL);
//等待线程终止并获取返回值
pthread_join(get_voice_tid, NULL);
pthread_join(category_tid, NULL);
// 销毁互斥锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
END:
// 关闭串口
close(serial_fd);
// 释放垃圾分类资源
garbage_final();
return 0;
}
编译
gcc -o test *.c *.h -I /usr/include/python3.10 -l python3.10 -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt
执行
sudo -E ./test
查看进程
ps -ax | grep mjpg_streamer | grep -v grep
ps -ax | grep ./test | grep -v grep
ps aux | grep './test' | grep -v grep | awk '{print $2}'
附录:
1.pthread_detach
pthread_detach
是 POSIX 线程库(pthreads)中的一个函数,用于控制线程的分离状态。在 POSIX 线程编程中,线程可以是分离的或非分离的。线程的分离状态决定了当线程终止时其资源是否自动释放。
这种机制对于那些主线程不关心其返回值,也不需要等待其结束的辅助线程是非常有用的。这样,主线程和辅助线程可以并行执行,提高了程序的性能。
1.函数原型
int pthread_detach(pthread_t thread);
thread
:要分离的线程标识符。
2.功能
pthread_detach
函数将指定的线程设置为分离状态。- 如果线程已经终止,
pthread_detach
会自动释放该线程的资源,包括线程的返回值和任何关联的线程特定数据(thread-specific data, TSD)。
3.返回值
- 成功时返回 0。
- 失败时返回错误码,常见的错误码包括:
ESRCH
:指定的线程标识符无效或找不到。EINVAL
:线程标识符无效或线程已经被分离。
4.使用场景
- 当你希望线程终止后自动释放资源时,可以调用
pthread_detach
。 - 在线程创建后立即分离线程是一种常见的做法,这样可以避免在线程终止时需要显式地调用
pthread_join
。
5.示例代码
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void* thread_function(void* arg) {
printf("Thread is running.\n");
return NULL;
}
int main() {
pthread_t tid;
int result;
// 创建线程
result = pthread_create(&tid, NULL, thread_function, NULL);
if (result != 0) {
perror("Failed to create thread");
return 1;
}
// 分离线程
result = pthread_detach(tid);
if (result != 0) {
perror("Failed to detach thread");
return 1;
}
printf("Thread is detached.\n");
return 0;
}
在这个示例中:
- 创建一个线程并立即调用
pthread_detach
将其分离。 - 这样,当线程终止时,其资源会自动释放,不需要调用
pthread_join
。
6.注意事项
- 一旦线程被分离,就不能再次分离或通过
pthread_join
等待其终止。 - 如果线程在分离之前已经终止,
pthread_detach
会立即释放线程的资源。 - 在多线程环境中,确保线程的资源在适当的时候被释放是非常重要的,以避免资源泄漏。
pthread_detach
是管理线程生命周期和资源的有用工具,特别是在需要创建大量短命线程的应用程序中。
2.pthread_detach 和 pthread_join的区别
pthread_detach
和 pthread_join
都是 POSIX 线程库(pthreads)中的函数,它们在线程管理中扮演着不同的角色,尤其是在处理线程终止和资源回收方面。以下是这两个函数的主要区别及其在线程管理中的角色:
特性/函数 | pthread_detach | pthread_join |
---|---|---|
功能 | 将线程设置为分离状态 | 等待线程终止并获取返回值 |
资源回收 | 自动回收资源,无需 pthread_join | 必须显式调用以回收资源 |
返回值 | 不保留,无法通过 pthread_join 获取 | 可以获取线程的返回值。 |
使用场景 | - 不需要线程返回值 - 希望线程终止后立即释放资源 - 创建大量短命线程,如并行处理任务 | - 需要获取线程返回值 - 需要同步线程终止 - 确保数据一致性和完整性 - 需要精确控制线程生命周期和资源回收的场景。 |
线程状态 | 将线程设置为分离状态,无法再次分离或加入 | 可以等待线程终止,但不能将线程设置为分离状态。 |
3.舵机测试代码
pwmtest.c
#include <wiringPi.h>
#include <softPwm.h>
#include <stdio.h>
//根据公式:PWMfreq = 1 x 10^6 / (100 x range) ,要得到PWM频率为50Hz,则range为200,即周期分为200步,控制精度相比硬件PWM较低。
void pwm_write(int pwm_pin)
{
pinMode(pwm_pin, OUTPUT);
softPwmCreate(pwm_pin,0,200);// 起始值为0,周期范围设置为200步。, 周期20ms
softPwmWrite(pwm_pin,10);//1ms 45度,这将产生一个占空比为5%的PWM信号(因为10是200步中的一小部分)。
delay(1000);
softPwmStop(pwm_pin);
}
void pwm_stop(int pwm_pin)
{
pinMode(pwm_pin, OUTPUT);
softPwmCreate(pwm_pin,0,200);// range设置周期分为200步, 周期20ms
softPwmWrite(pwm_pin,22);//2.2ms 140度大概角度
delay(1000);
softPwmStop(pwm_pin);
}
int main()
{
wiringPiSetup();
pwm_write(7);
delay(2000);
pwm_stop(7);
return 0;
}
编译
gcc pwmtest.c -o pwm -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt
sudo ./pwm
4.wget-log打印日志文件
wget-log 文件名通常是 wget 命令行工具的默认日志文件名,用于记录 wget 下载命令执行过程中的信息、警告和错误。wget 是一个用于在命令行中下载文件的工具,而 wget-log 文件则用于记录执行 wget 命令时产生的输出。
如果你在使用类似如下的 wget 命令:wget [URL]
wget
默认会将日志输出到 wget-log
文件中。如果你希望更改日志文件的名称,可以使用 -o
选项,例如:
wget -o mylog.txt [URL]
上述命令将日志输出到名为 mylog.txt 的文件中。因此,wget-log 文件的生成通常取决于 wget 命令的使用方式。
阿里云的相关操作(比如通过 wget 下载文件)也可能产生 wget-log 文件,具体情况可能取决于你执行的命令和阿里云环境的设置。如果有特定的 wget 命令或阿里云操作,你可以提供更多的上下文,以便我更好地理解你的问题。