Linux GPIO 通过sysfs 方式进行操控, 进入到/sys/class/gpio 目录下。
export:用于将指定编号的GPIO 引脚导出。在使用GPIO 引脚之前,需要将其导出,导出成功之后才能使用它。
unexport:将导出的GPIO 引脚删除。当使用完GPIO 引脚之后,我们需要将导出的引脚删除。
将指定的编号写入到export 文件中,可以导出指定编号的GPIO 引脚,导出成功之后会在/sys/class/gpio目录下生成对应的gpioX(X 表示GPIO 的编号)。
direction:配置GPIO 引脚为输入或输出模式。该文件可读、可写,读表示查看GPIO 当前是输入还是输出模式,写表示将GPIO 配置为输入或输出模式;读取或写入操作可取的值为"out"(输出模式)和"in"(输入模式)。
value:在GPIO 配置为输出模式下,向value 文件写入"0"控制GPIO 引脚输出低电平,写入"1"则控制GPIO 引脚输出高电平。在输入模式下,读取value 文件获取GPIO 引脚当前的输入电平状态。
active_low:这个属性文件用于控制极性,可读可写。
edge:控制中断的触发模式,该文件可读可写。在配置GPIO 引脚的中断触发模式之前,需将其设置为输入模式:
非中断引脚:echo “none” > edge
上升沿触发:echo “rising” > edge
下降沿触发:echo “falling” > edge
边沿触发:echo “both” > edge
瑞芯微GPIO对应用户空间的gpio编号如下表。
GPIO | GPIO编号 |
---|---|
GPIO0_B0 | 8 |
GPIO2_C0 | 80 |
GPIO4_C3 | 147 |
GPIO0_C5 | 21 |
GPIO4_C4 | 148 |
GPIO4_C5 | 149 |
GPIO2_B2 | 74 |
GPIO2_B1 | 73 |
cd /sys/class/gpio/
# 将文件用于者改为youyeetoo
sudo chown youyeetoo:youyeetoo export
sudo chown youyeetoo:youyeetoo unexport
# 以GPIO0_B0为例操作gpio输出,其他gpio操作只需要根据上一个表格的对应关系导出即可。
# 导出 GPIO0_B0
echo 8 > export
# 将文件用于者改为youyeetoo
sudo chown youyeetoo:youyeetoo gpio8/direction
sudo chown youyeetoo:youyeetoo gpio8/value
cd gpio8
# 设置输出
echo out > direction
# 输出高电平
echo 1 > value
# 输出低电平
echo 0 > value
# 删除导出gpio引脚
cd ..
echo 8 > unexport
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
static char gpio_path[100];
static int gpio_config(const char *attr, const char *val)
{
char file_path[100];
int len;
int fd;
sprintf(file_path, "%s/%s", gpio_path, attr);
chown(file_path, 1000, 1000);
fd = open(file_path, O_WRONLY);
if (fd < 0) {
perror("open error");
return fd;
}
len = strlen(val);
if (len != write(fd, val, len)) {
perror("write error");
close(fd);
return -1;
}
close(fd); //关闭文件
return 0;
}
int main(int argc, char *argv[])
{
/* 校验传参 */
if (3 != argc) {
fprintf(stderr, "usage: %s <gpio> <value>\n", argv[0]);
exit(-1);
}
/* 判断指定编号的GPIO是否导出 */
sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);
if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出
int fd;
int len;
chown("/sys/class/gpio/export", 1000, 1000);
fd = open("/sys/class/gpio/export", O_WRONLY);
if (0 > ()) {
perror("open error");
exit(-1);
}
len = strlen(argv[1]);
if (len != write(fd, argv[1], len)) {//导出gpio
perror("write error");
close(fd);
exit(-1);
}
close(fd); //关闭文件
}
/* 配置为输出模式 */
if (gpio_config("direction", "out"))
exit(-1);
/* 极性设置 */
if (gpio_config("active_low", "0"))
exit(-1);
/* 控制GPIO输出高低电平 */
if (gpio_config("value", argv[2]))
exit(-1);
/* 退出程序 */
exit(0);
}
cd /sys/class/gpio/
# 将文件用于者改为youyeetoo
chown youyeetoo:youyeetoo export
chown youyeetoo:youyeetoo unexport
# 以GPIO0_B0为例操作gpio输出,其他gpio操作只需要根据上一个表格的对应关系导出即可。
# 导出 GPIO0_B0
echo 8 > export
# 将文件用于者改为youyeetoo
sudo chown youyeetoo:youyeetoo gpio8/direction
sudo chown youyeetoo:youyeetoo gpio8/value
cd gpio8
# 设置输出
echo in > direction
# 查看gpio电平
cat value
# 删除导出gpio引脚
cd ..
echo 8 > unexport
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
static char gpio_path[100];
static int gpio_config(const char *attr, const char *val)
{
char file_path[100];
int len;
int fd;
sprintf(file_path, "%s/%s", gpio_path, attr);
if (0 > (fd = open(file_path, O_WRONLY))) {
perror("open error");
return fd;
}
len = strlen(val);
if (len != write(fd, val, len)) {
perror("write error");
close(fd);
return -1;
}
close(fd); //关闭文件
return 0;
}
int main(int argc, char *argv[])
{
char file_path[100];
char val;
int fd;
/* 校验传参 */
if (2 != argc) {
fprintf(stderr, "usage: %s <gpio>\n", argv[0]);
exit(-1);
}
/* 判断指定编号的GPIO是否导出 */
sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);
if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出
int len;
if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) {
perror("open error");
exit(-1);
}
len = strlen(argv[1]);
if (len != write(fd, argv[1], len)) {//导出gpio
perror("write error");
close(fd);
exit(-1);
}
close(fd); //关闭文件
}
/* 配置为输入模式 */
if (gpio_config("direction", "in"))
exit(-1);
/* 极性设置 */
if (gpio_config("active_low", "0"))
exit(-1);
/* 配置为非中断方式 */
if (gpio_config("edge", "none"))
exit(-1);
/* 读取GPIO电平状态 */
sprintf(file_path, "%s/%s", gpio_path, "value");
if (0 > (fd = open(file_path, O_RDONLY))) {
perror("open error");
exit(-1);
}
if (0 > read(fd, &val, 1)) {
perror("read error");
close(fd);
exit(-1);
}
printf("value: %c\n", val);
/* 退出程序 */
close(fd);
exit(0);
}
# 将文件用于者改为youyeetoo
chown youyeetoo:youyeetoo export
chown youyeetoo:youyeetoo unexport
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
static char gpio_path[100];
static int gpio_config(const char *attr, const char *val)
{
char file_path[100];
int len;
int fd;
sprintf(file_path, "%s/%s", gpio_path, attr);
if (0 > (fd = open(file_path, O_WRONLY))) {
perror("open error");
return fd;
}
len = strlen(val);
if (len != write(fd, val, len)) {
perror("write error");
return -1;
}
close(fd); //关闭文件
return 0;
}
int main(int argc, char *argv[])
{
struct pollfd pfd;
char file_path[100];
int ret;
char val;
/* 校验传参 */
if (2 != argc) {
fprintf(stderr, "usage: %s <gpio>\n", argv[0]);
exit(-1);
}
/* 判断指定编号的GPIO是否导出 */
sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]);
if (access(gpio_path, F_OK)) {//如果目录不存在 则需要导出
int len;
int fd;
if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) {
perror("open error");
exit(-1);
}
len = strlen(argv[1]);
if (len != write(fd, argv[1], len)) {//导出gpio
perror("write error");
exit(-1);
}
close(fd); //关闭文件
}
/* 配置为输入模式 */
if (gpio_config("direction", "in"))
exit(-1);
/* 极性设置 */
if (gpio_config("active_low", "0"))
exit(-1);
/* 配置中断触发方式: 上升沿和下降沿 */
if (gpio_config("edge", "both"))
exit(-1);
/* 打开value属性文件 */
sprintf(file_path, "%s/%s", gpio_path, "value");
if (0 > (pfd.fd = open(file_path, O_RDONLY))) {
perror("open error");
exit(-1);
}
/* 调用poll */
pfd.events = POLLPRI; //只关心高优先级数据可读(中断)
read(pfd.fd, &val, 1);//先读取一次清除状态
for ( ; ; ) {
ret = poll(&pfd, 1, -1); //调用poll
if (0 > ret) {
perror("poll error");
exit(-1);
}
else if (0 == ret) {
fprintf(stderr, "poll timeout.\n");
continue;
}
/* 校验高优先级数据是否可读 */
if(pfd.revents & POLLPRI) {
if (0 > lseek(pfd.fd, 0, SEEK_SET)) {//将读位置移动到头部
perror("lseek error");
exit(-1);
}
if (0 > read(pfd.fd, &val, 1)) {
perror("read error");
exit(-1);
}
printf("GPIO中断触发<value=%c>\n", val);
}
}
/* 退出程序 */
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
int set_opt(int,int,int,char,int);
void main(){
int fd,nByte;
char *uart3 = "/dev/ttyS2";
char bufferR[2];
char bufferW[2];
memset(bufferR, '\0', 2);
memset(bufferW, '\0', 2);
if((fd=open(uart3,O_RDWR,0777))<0)
{
printf("failed\n");
}
else{
printf("success\n");
set_opt(fd, 115200, 8, 'N', 1);
}
while(1){
nByte = 0;
memset(bufferR, '\0', 2);
memset(bufferW, '\0', 2);
// printf("hello\n");
if((nByte = read(fd, bufferR, 1)) == 1){ //MCU串口发送接收都是单字节(单字符)函数
printf("receive:%c\n",bufferR[0]);
bufferW[0] = 'A';
write(fd,bufferW,1); //串口发送单字节(单字符) buffer[0] = data
memset(bufferR, '\0', 2);
memset(bufferW, '\0', 2);
nByte = 0;
}
}
close(fd);
}
//串口通用初始化函数
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;//定义结构体newtio和oldtio
//将原串口的数据取到oldtio
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
//将newio清零和设置c_cflag
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;//使能接收和忽略控制线
newtio.c_cflag &= ~CSIZE;
//设置数据位
switch( nBits )
{
case 7:
newtio.c_cflag |= CS7;
break;
case 8:
newtio.c_cflag |= CS8;
break;
}
//设置校验位
switch( nEvent )
{
//偶校验
case 'O':
newtio.c_cflag |= PARENB;//使能奇偶校验
newtio.c_cflag |= PARODD;//偶校验
newtio.c_iflag |= (INPCK | ISTRIP);//输入校验并忽略第八位
break;
case 'E':
newtio.c_iflag |= (INPCK | ISTRIP);
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;//取消偶校验(置零偶校验位),开启奇校验
break;
case 'N':
newtio.c_cflag &= ~PARENB;//不进行奇偶校验
break;
}
//设置波特率
switch( nSpeed )
{
case 2400:
cfsetispeed(&newtio, B2400);
cfsetospeed(&newtio, B2400);
break;
case 4800:
cfsetispeed(&newtio, B4800);
cfsetospeed(&newtio, B4800);
break;
case 9600:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
case 115200:
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
break;
case 460800:
cfsetispeed(&newtio, B460800);
cfsetospeed(&newtio, B460800);
break;
default:
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
break;
}
//设置停止位
if( nStop == 1 )
newtio.c_cflag &= ~CSTOPB;//一位停止位
else if ( nStop == 2 )
newtio.c_cflag |= CSTOPB;//两位停止位
newtio.c_cc[VTIME] = 0;//不设置读取超时
newtio.c_cc[VMIN] = 0;//读取最小字符数为0
tcflush(fd,TCIFLUSH);//清空缓冲区
//使配置生效
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
// printf("set done!\n\r");
return 0;
}
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
/* eeprom所对应的I2C控制器的设备节点 */
#define EEPROM_DEVICE "/dev/i2c-0"
/* eeprom的I2C设备地址 */
#define EEPROM_ADDR 0x50
/* 函数名:eeprom_write
** 功能:向eeprom写数据
**参数:fd:eeprom对应I2C控制器设备节点的文件名
** dev_addr:eeprom的I2C从设备地址
** reg_addr:eeprom的寄存器地址
** data_buf:要向eeprom写数据的数据buf
** len:要写多少个字节。本例中当前最大支持为8个字节
**返回值:负数表示操作失败,其他为成功
*/
int eeprom_write(int fd, unsigned char dev_addr, unsigned char reg_addr, unsigned char * data_buf,int len)
{
int ret;
unsigned char msg_buf[9];
struct i2c_rdwr_ioctl_data data;
struct i2c_msg messages;
/* 1. 构建msg_buf*/
/* 1.1. 将要操作的寄存器首地址赋给要进行I2C数据通信的首字节数据 */
msg_buf[0] = reg_addr;
/* 1.2. 将要向eeprom写数据的数据buf赋在I2C数据通信中eeprom寄存器的后面 */
if (len < 9) { /* 本demo最大支持一次向eeprom写一页大小的8个字节数据 */
memcpy((void *) &msg_buf[1], data_buf, len); //第1位之后是数据
} else {
printf("This function supports up to 8 bytes at a time !!!\n");
return -1;
}
/* 2. 构建 struct i2c_msg messages */
/* 2.1. 赋值eeprom的I2C从设备地址 */
messages.addr = dev_addr;
/* 2.2. 赋值flags为本次I2C通信完成写功能 */
messages.flags = 0;
/* 2.3. 赋值len为数据buf的长度 + eeprom寄存器地址的数据长度 */
messages.len = len+1;
/* 2.4. 构建消息包的数据buf*/
messages.buf = msg_buf;
/* 3. 构建struct i2c_rdwr_ioctl_data data */
/* 3.1. 将准备好的消息包赋值给i2c_rdwr_ioctl_data中的msgs消息*/
data.msgs = &messages;
/* 3.2. 由于本次I2C通信只有写动作,所以消息数为1次 */
data.nmsgs = 1;
/* 4. 调用驱动层的读写组合的I2C数据传输 */
if(ioctl(fd, I2C_RDWR, &data) < 0)
{
printf("I2C_RDWR err \n");
return -1;
}
/* 5. 等待I2C总线写入完成 */
sleep(1);
return 0;
}
/*函数名:eeprom_read
**功能:从eeprom读数据
**参数:fd:eeprom对应I2C控制器设备节点的文件名
** dev_addr:eeprom的I2C从设备地址
** reg_addr:eeprom的寄存器地址
** data_buf:存放从eeprom读数据的buf
** len:要读多少个字节。
**返回值:负数表示操作失败,其他为成功
*/
int eeprom_read(int fd, unsigned char dev_addr, unsigned char reg_addr, unsigned char * data_buf,int len)
{
int ret;
unsigned char msg_buf[9];
struct i2c_rdwr_ioctl_data data;
struct i2c_msg messages[2];
/* 1. 构建 struct i2c_msg messages */
/* 1.1. 构建第一条消息 messages[0] */
/* 1.1.1. 赋值eeprom的I2C从设备地址 */
messages[0].addr = dev_addr;
/* 1.1.2. 赋值flags为本次I2C通信完成写动作 */
messages[0].flags = 0;
/* 1.1.3. 赋值len为eeprom寄存器地址的数据长度是1 */
messages[0].len = 1;
/* 1.1.4. 本次写动作的数据是要读取eeprom的寄存器首地址*/
messages[0].buf = ®_addr;
/* 1.2. 构建第二条消息 messages[1] */
/* 1.2.1. 赋值eeprom的I2C从设备地址 */
messages[1].addr = dev_addr;
/* 1.1.2. 赋值flags为本次I2C通信完成读动作 */
messages[1].flags = I2C_M_RD;
/* 1.1.3. 赋值len为要读取eeprom寄存器数据长度len */
messages[1].len = len;
/* 1.1.4. 本次读动作的数据要存放的buf位置*/
messages[1].buf = data_buf;
/* 2. 构建struct i2c_rdwr_ioctl_data data */
/* 2.1. 将准备好的消息包赋值给i2c_rdwr_ioctl_data中的msgs消息*/
data.msgs = messages;
/* 2.2. 由于本次I2C通信既有写动作也有读动作,所以消息数为2次 */
data.nmsgs = 2;
/* 3. 调用驱动层的读写组合的I2C数据传输 */
if(ioctl(fd, I2C_RDWR, &data) < 0)
{
printf("I2C_RDWR err \n");
return -1;
}
/* 4. 等待I2C总线读取完成 */
sleep(1);
return 0;
}
int main()
{
int fd,i,ret=0;
unsigned char w_add=0x10;
/* 将要读取的数据buf*/
unsigned char rd_buf[8] = {0};
/* 要写的数据buf*/
unsigned char wr_buf[8] = {0};
printf("hello,this is I2C_RDWR i2c test \n");
/* 打开eeprom对应的I2C控制器文件 */
fd =open(EEPROM_DEVICE, O_RDWR);
if (fd< 0)
{
printf("open"EEPROM_DEVICE"failed \n");
}
/* 把要写入的数据写入到后面的buf中 */
for(i=0;i<8;i++)
wr_buf[i]=i;
/* 通过I2C_RDWR完成向eeprom读数据的功能 */
eeprom_write(fd,EEPROM_ADDR,w_add,wr_buf,8);
/* 通过I2C_RDWR完成向eeprom写数据的功能 */
eeprom_read(fd,EEPROM_ADDR,w_add,rd_buf,8);
for(i=0;i<8;i++)
{
printf("rd_buf is :%d\n",rd_buf[i]);
}
/* 完成操作后,关闭eeprom对应的I2C控制器的设备文件 */
close(fd);
return 0;
}
CAN发送应用程序示例
/* 1. 报文发送程序 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main()
{
int s, nbytes;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame[2] = {{0}};
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);//创建套接字
strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr); //指定 can0 设备
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr));//将套接字与 can0 绑定
//禁用过滤规则,本进程不接收报文,只负责发送
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
//生成两个报文
frame[0].can_id = 0x11;
frame[0]. can_dlc = 1;
frame[0].data[0] = 'Y';
frame[1].can_id = 0x22;
frame[1]. can_dlc = 1;
frame[1].data[0] = 'N';
//循环发送两个报文
while(1)
{
nbytes = write(s, &frame[0], sizeof(frame[0])); //发送 frame[0]
if(nbytes != sizeof(frame[0]))
{
printf("Send Error frame[0]\n!");
break; //发送错误,退出
}
sleep(1);
nbytes = write(s, &frame[1], sizeof(frame[1])); //发送 frame[1]
if(nbytes != sizeof(frame[0]))
{
printf("Send Error frame[1]\n!");
break;
}
sleep(1);
}
close(s);
return 0;
}
CAN接收程序示例
/* 2. 报文过滤接收程序 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int main()
{
int s, nbytes;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
struct can_filter rfilter[1];
s = socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字
strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr); //指定 can0 设备
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与 can0 绑定
//定义接收规则,只接收表示符等于 0x11 的报文
rfilter[0].can_id = 0x11;
rfilter[0].can_mask = CAN_SFF_MASK;
//设置过滤规则
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
while(1)
{
nbytes = read(s, &frame, sizeof(frame)); //接收报文
//显示报文
if(nbytes > 0)
{
printf(“ID=0x%X DLC=%d data[0]=0x%X\n”, frame.can_id,
frame.can_dlc, frame.data[0]);
}
}
close(s);
return 0;
}
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。由一系列 C函数和少量 C++ 类构成,同时提供了 Python、Java、MATLAB 等语言的接口。
pkg-config --modversion opencv4
本系统默认内置版本为4.13.0。
mkdir OpenCV_Demo
cd OpenCV_Demo
mkdir build image src
cmake_minimum_required(VERSION 3.8)
project( OpenCV_Demo )
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( image_demo src/image_demo.cpp )
add_executable( video_demo src/video_demo.cpp )
add_executable( camera_demo src/camera_demo.cpp )
target_link_libraries( image_demo ${OpenCV_LIBS} )
target_link_libraries( video_demo ${OpenCV_LIBS} )
target_link_libraries( camera_demo ${OpenCV_LIBS} )
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char** argv) {
// 读取图片
cv::Mat image = cv::imread("../image/image.png");
// 确认图片读取成功
if(image.empty()) {
std::cerr << "Failed to open image file." << std::endl;
return -1;
}
//控制照片比例
resize(image, image, cv::Size(1280, 720));
// 显示图片
cv::imshow("Image with Box", image);
// 等待按键
cv::waitKey(0);
return 0;
}
#include <opencv2/opencv.hpp>
using namespace cv;
int main() {
// 打开视频文件
VideoCapture cap("../image/video.mp4");
// 检查视频是否成功打开
if (!cap.isOpened()) {
std::cout << "Error opening video stream or file" << std::endl;
return -1;
}
// 循环读取视频帧
while (true) {
Mat frame;
// 读取当前帧
cap >> frame;
// 检查是否成功读取帧
if (frame.empty())
break;
// 显示当前帧
imshow("Frame", frame);
// 按下 Esc 键退出循环
if (waitKey(25) == 27)
break;
}
// 释放VideoCapture对象和所有窗口
cap.release();
destroyAllWindows();
return 0;
}
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace cv;
int main() {
// 打开默认摄像头
VideoCapture cap(41);
if (!cap.isOpened()) {
cout << "无法打开摄像头!" << endl;
return -1;
}
namedWindow("摄像头", WINDOW_NORMAL);
while (true) {
Mat frame;
cap >> frame;
// 显示视频帧
imshow("摄像头", frame);
// 按下空格键拍照
if (waitKey(30) == ' ')
{
// 生成文件名
time_t now = time(NULL);
tm *ltm = localtime(&now);
string filename = to_string(ltm->tm_year + 1900) + "-" + to_string(ltm->tm_mon + 1) + "-" + to_string(ltm->tm_mday) + "-" + to_string(ltm->tm_hour) + "-" + to_string(ltm->tm_min) + "-" + to_string(ltm->tm_sec) + ".jpg";
// 保存图片
imwrite(filename, frame);
cout << "已保存照片:" << filename << endl;
}
}
return 0;
}
cd build
cmake ..
make
./image_demo
./video_demo
./camera_demo