MX6ULL学习笔记 (七) 中断实验

2023-12-05

前言:

本章我们就来学习一 下如何在 Linux 下使用中断。在linux内核里面使用中断,不同于我们以往在别的裸机开发一样,需要进行各种寄存器的配置,中断使能之类的,而在Linux 内核中,提供了完善的中断框架,我们只需要申请中断,然后注 册中断处理函数即可,使用非常方便。

一、Linux 中断 API 函数

1、中断号

每个中断都有一个中断号,通过中断号即可区分不同的中断,有的资料也把中断号叫做中断线。在 Linux 内核中使用一个 int 变量表示中断号。

2、request_irq函数:

函数原型:

int request_irq(unsigned int irq, 
                irq_handler_t handler, 
                unsigned long flags, 
                const char *name, 
                void *dev);

参数分析:

  • irq :要申请中断的中断号。
  • handler :中断处理函数,当中断发生以后就会执行此中断处理函数。
  • flags :中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志
  • name :中断名字,设置以后可以在 /proc/interrupts 文件中看到对应的中断名字。
  • dev 如果将 flags 设置为 IRQF_SHARED 的话, dev 用来区分不同的中断,一般情况下将 dev 设置为设备结构体, dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
  • 返回值: 0 中断申请成功,其他负值 中断申请失败,如果返回 -EBUSY 的话表示中断已经 被申请了。

顺便介绍几个常用的中断标志,如表所示:

3.free_irq 函数:

函数原型:

void free_irq(unsigned int irq, void *dev);

函数参数和返回值含义如下:

  • irq 要释放的中断。
  • dev :如果中断设置为共享 (IRQF_SHARED) 的话,此参数用来区分具体的中断。共享中断 只有在释放最后中断处理函数的时候才会被禁止掉。
  • 返回值:无。

4.中断处理函数:

使用 request_irq 函数申请中断的时候需要设置中断处理函数,中断处理函数格式如下所示:

irqreturn_t (*irq_handler_t) (int, void *);
  • 第一个参数是要中断处理函数要相应的中断号。
  • 第二个参数是一个指向 void 的指针,也就是个通用指针,需要与 request_irq 函数的 dev 参数保持一致。用于区分共享中断的不同设备, dev 也可以指向设备数据结构。
  • 中断处理函数的返回值为 irqreturn_t 类型

二、了解中断初始化驱动的步骤。

1 .修改设备树

这里是基于之前按键的程序修改的,也就是在之前设备树的基础上修改就好。这里是在根节点下添加的。

key {
        #address-cells = <1>; 
        #size-cells = <1>; 
        compatible = "atkalpha-key"; 
        pinctrl-names = "default"; 
        pinctrl-0 = <&pinctrl_key>; 
        key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */ 
        status = "okay"; 
 }; 

在上面基础上添加如下:

(1) 设置 interrupt-parent 属性值,例如按键的中断,设置属性值为” gpio1 ,因为 KEY0 所使用的 GPIO GPIO1_IO18 ,也就是设置 KEY0 GPIO 中断控制器为 gpio1

(2) 设置 interrupts 属性,也就是设置中断源。

2.编写字符型驱动框架。

  1. (1)直接选取之前的字符设备驱动框架,在那份代码的基础上来进行修改。

3.定义按键信息结构体

  1. (1)首先定义一个结构体,来保存有关key中断的一些信息。
/* 中断IO描述结构体 */
struct irq_keydesc {
	int gpio;								/* gpio *
	int irqnum;								/* 中断号     */
	unsigned char value;					/* 按键对应的键值 */
	char name[10];							/* 名字 */
	irqreturn_t (*handler)(int, void *);	/* 中断服务函数 */
};
  1. 4.编写按键中断初始化程序。

  2. 步骤如下:
  • ①使用of_find_node_by_path函数通过环境名获取节点。
  • ②使用of_get_named_gpio函数通过节点得到gpio的name编号。
  • ③使用gpio_request函数通过gpio的name编号去注册gpio
  • ④使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。
  • ⑤使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号
  • ⑥使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。
  • int request_irq(unsigned int irq, 
                    irq_handler_t handler, 
                    unsigned long flags, 
                    const char *name, 
                    void *dev);
    
  • ⑦编写中断处理函数,函数原型:
  • static irqreturn_t key0_handler(int irq, void *dev_id);

5.编写按键释放程序

①使用free_irq函数去释放中断。


三、驱动代码的编写。

1.修改设备树

key {
    #address-cells = <1>; 
    #size-cells = <1>; 
    compatible = "atkalpha-key"; 
    pinctrl-names = "default"; 
    pinctrl-0 = <&pinctrl_key>; 
    key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */ 
    interrupt-parent = <&gpio1>; 
    interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */ 
    status = "okay"; 
 }; 

2.编写字符型驱动框架。

这里就直接使用之前的字符设备驱动的框架就可以了,把那些函数名,还有结构体名字之类的改一改就好,后面也会贴出完整的代码。

3.首先定义一个结构体,来保存有关key中断的一些信息。

struct irq_keydesc
{
	int gpio_num;                           /* gpio号 	 */  	
	int irq_num;							/*  中断号    */
	char gpio_name[10];						/* gpio名字   */
	unsigned char key_value;				/*按键对应键值*/
	irqreturn_t (*handle)(int ,void *);		/*终端服务函数  */
};
  1. 4.编写按键中断初始化程序。

  2. 步骤如下:
  • ①使用of_find_node_by_path函数通过环境名获取节点。
  • 	/*使用of_find_node_by_path函数通过环境名获取节点。*/
    	dev->nd = of_find_node_by_path("/key");
    	if(dev->nd == NULL)
    	{
            printk("key node not find!\r\n");
    		return -EINVAL;
        }
  • ②使用of_get_named_gpio函数通过节点得到gpio的name编号。
  • 	/*使用of_get_named_gpio函数通过节点得到gpio的name编号。*/
    	dev->irqkeydesc[0].gpio_num = of_get_named_gpio(dev->nd,"gpios",0);
    	if(dev->irqkeydesc[0].gpio_num < 0){
    			printk("can't get key0\r\n");
    	}
  • ③使用gpio_request函数通过gpio的name编号去注册gpio
  • 	/*使用gpio_request函数通过gpio的name编号,以及设置中断名去注册gpio*/
        memset(dev->irqkeydesc[0].gpio_name,0,sizeof(dev->irqkeydesc[0].gpio_name));
        sprintf(dev->irqkeydesc[0].gpio_name, "KEY0");
    	gpio_request(dev->irqkeydesc[0].gpio_num,dev->irqkeydesc[0].gpio_name);
  • ④使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。
  • 	/*使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。*/
    	gpio_direction_input(dev->irqkeydesc[0].gpio_num);
  • ⑤使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号。
  • 	/*使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号*/
    	dev->irqkeydesc[0].irq_num=irq_of_parse_and_map(dev->nd,0);
    	printk("key0:gpio=%d, irqnum=%d\r\n", dev->irqkeydesc[0].gpio_num, 
                                             dev->irqkeydesc[0].irq_num);
  • ⑥使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。
  • 	/*使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。*/
    	dev->irqkeydesc[0].handle    = key0_handler;
    	dev->irqkeydesc[0].key_value = KEY0_VALUE;
    	ret = request_irq(dev->irqkeydesc[0].irq_num,
    					  dev->irqkeydesc[0].handle,
    					  IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
    					  dev->irqkeydesc[0].gpio_name, &imx6uirq);
    	if(ret < 0){
    		printk("irq %d request failed!\r\n", dev->irqkeydesc[0].irq_num);
    		return -EFAULT;
    	}

5.编写中断释放程序

①使用free_irq函数去释放中断。

	free_irq(imx6uirq.irqkeydesc[0].irq_num,&imx6uirq);
	gpio_free(imx6uirq.irqkeydesc[0].gpio_num);

6.完整驱动代码:

/**************头文件区域*********************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/major.h>
#include <linux/gfp.h>

#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/device.h>

#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/string.h>
/**********************************************************************************/


/************************宏定义-begin***********************************************/
#define DEVICE_CNT			1		  	/* 设备号个数 */
#define DEVICE_NAME			"imx6uirq"	/* 名字 */
#define KEY_CNT				1	        /* 按键数量*/
#define KEY0_VALUE          0X01		/*按键0对应键值*/
#define INVAKEY_VALUE       0XFF		/*无按键对应键值*/
/************************宏定义-end********************************************/


/************************结构体定义-begin***********************************************/

struct irq_keydesc
{
	int gpio_num;                           /* gpio号 	 */  	
	int irq_num;							/*  中断号    */
	char gpio_name[10];						/* gpio名字   */
	unsigned char key_value;				/*按键对应键值*/
	irqreturn_t (*handle)(int ,void *);		/*终端服务函数  */
};

/* imx6uirql设备信息结构体 */
struct char_dev{
	dev_t devid;							/* 设备号 	 */
	struct cdev cdev;						/* cdev 	*/
	struct class *class;					/* 类 		*/
	struct device *device;					/* 设备 	*/
	int major;								/* 主设备号	 */
	int minor;								/* 次设备号  */
	struct device_node *nd; 				/* 设备节点  */
	struct irq_keydesc irqkeydesc[KEY_CNT];	/*按键信息结构体数组*/
	struct timer_list timer;				/*timer结构体*/
	atomic_t keyvalue;      				/* 有效的按键键值 */
    atomic_t releasekey;    				/* 标记是否完成一次完成的按键,包括按下和释放 */
};
struct char_dev imx6uirq;
/************************结构体定义-end***********************************************/


/*----------------------------------------------------------------------------------------------------*/
/************************按键中断服务函数***********************************************/
static irqreturn_t key0_handler(int irq, void *dev_id)
{
	struct char_dev *dev = (struct char_dev *)dev_id;
	dev->timer.data = (volatile long)dev_id;
	mod_timer(&dev->timer,jiffies + msecs_to_jiffies(10));
	return IRQ_RETVAL(IRQ_HANDLED);

}

/************************定时器中断服务函数***********************************************/
void timer_function(unsigned long arg)
{
	unsigned char value;
	struct char_dev *dev=(struct char_dev *)arg;
	struct irq_keydesc*keydesc;

	keydesc = &dev->irqkeydesc[0];

	value=gpio_get_value(keydesc->gpio_num);
	if(value == 0){
		atomic_set(&dev->keyvalue,keydesc->key_value);
		}
	else{
		atomic_set(&dev->keyvalue,0x80 | keydesc->key_value);
		atomic_set(&dev->releasekey, 1);	/* 标记松开按键,即完成一次完整的按键过程 */			

	}
}

/*************按键gpio初始化函数**************************/
static int keyio_init(struct char_dev *dev)
{
	int ret;
	/*使用of_find_node_by_path函数通过环境名获取节点。*/
	dev->nd = of_find_node_by_path("/key");
	if(dev->nd == NULL)
	{
        printk("key node not find!\r\n");
		return -EINVAL;
    }

	/*使用of_get_named_gpio函数通过节点得到gpio的name编号。*/
	dev->irqkeydesc[0].gpio_num = of_get_named_gpio(dev->nd,"gpios",0);
	if(dev->irqkeydesc[0].gpio_num < 0){
			printk("can't get key0\r\n");
	}

	/*使用gpio_request函数通过gpio的name编号,以及设置中断名去注册gpio*/
    memset(dev->irqkeydesc[0].gpio_name,0,sizeof(dev->irqkeydesc[0].gpio_name));
    sprintf(dev->irqkeydesc[0].gpio_name, "KEY0");
	gpio_request(dev->irqkeydesc[0].gpio_num,dev->irqkeydesc[0].gpio_name);

	/*使用gpio_direction_input函数去设置gpio的方向,是输入还是输出啊。*/
	gpio_direction_input(dev->irqkeydesc[0].gpio_num);

	/*使用irq_of_parse_and_map函数从 interupts 属性中提取到对应的中断号*/
	dev->irqkeydesc[0].irq_num=irq_of_parse_and_map(dev->nd,0);
	printk("key0:gpio=%d, irqnum=%d\r\n", dev->irqkeydesc[0].gpio_num, 
                                         dev->irqkeydesc[0].irq_num);

	/*使用request_irq函数去申请注册中断。同时指定我们的中断处理函数。*/
	dev->irqkeydesc[0].handle    = key0_handler;
	dev->irqkeydesc[0].key_value = KEY0_VALUE;
	ret = request_irq(dev->irqkeydesc[0].irq_num,
					  dev->irqkeydesc[0].handle,
					  IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
					  dev->irqkeydesc[0].gpio_name, &imx6uirq);
	if(ret < 0){
		printk("irq %d request failed!\r\n", dev->irqkeydesc[0].irq_num);
		return -EFAULT;
	}

	return 0;
}

/*************定时器初始化函数********************************************/
void mytimer_init(struct char_dev *dev)
{
	init_timer(&dev->timer);
	dev->timer.function = timer_function;
}

/*****实现对应的open/read/write等函数,填入file_operations结构体************/

/******************实现对应的read函数************************************/
static ssize_t imx6uirq_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int ret = 0;
	unsigned char keyvalue = 0;
	unsigned char releasekey = 0;

	struct char_dev *dev = (struct char_dev *)file->private_data;
	keyvalue   = atomic_read(&dev->keyvalue);
	releasekey = atomic_read(&dev->releasekey);
	
	if(releasekey)
	{
		if(keyvalue & 0x80)
		{
			keyvalue &= ~0x80;
			ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
		}
		else
		{
			goto data_error;
		}
		atomic_set(&dev->releasekey, 0);/* 按下标志清零 */
	}
	else
	{
		goto data_error;
	}
	return 0;
data_error:
	return -EINVAL;

}

/******************实现对应的write函数************************************/
static ssize_t imx6uirq_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	return 0;
}

/******************实现对应的open函数************************************/
static int imx6uirq_drv_open (struct inode *node, struct file *file)
{
	file->private_data = &imx6uirq;	/* 设置私有数据 */
	return 0;
}


/******************实现对应的close函数************************************/
static int imx6uirq_drv_close (struct inode *node, struct file *file)
{
	return 0;
}

/********************定义自己的file_operations结构体****************************/
static struct file_operations imx6uirq_drv = {
	.owner	 = THIS_MODULE,
	.open    = imx6uirq_drv_open,
	.read    = imx6uirq_drv_read,
	.write   = imx6uirq_drv_write,
	.release = imx6uirq_drv_close,
};

/**************************注册驱动程序******************************************/
/* 把file_operations结构体告诉内核:注册驱动程序                        */
/* 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init imx6uirq_init(void)
{
	/* 动态注册字符设备的流程一般如下:
	1.调用 alloc_chrdev_region() 函数申请设备编号。
	2.使用 cdev_init() 函数初始化设备描述结构体。
	3.使用 cdev_add() 函数将设备号与设备描述结构体关联起来,注册字符设备驱动。
	4.使用 class_create() 函数创建一个设备类.
	5.使用 device_create() 函数创建一个设备
	*/
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	/*1 创建设备号
	   根据是否定义了设备号,通过条件判断选择不同的创建方式。
	   如果定义了设备号,则使用MKDEV宏将主设备号和次设备号合成为设备号,并调用register_chrdev_region()函数注册字符设备号。
	   如果没有定义设备号,则使用alloc_chrdev_region()函数动态分配设备号,并通过MAJOR和MINOR宏获取分配得到的主设备号和次设备号。*/
	if (imx6uirq.major) 
	{		/*  定义了设备号 */
		imx6uirq.devid = MKDEV(imx6uirq.major, 0);
		/*register_chrdev_region() 是 Linux 内核中用于向系统申请指定范围内的字符设备编号的函数*/
		register_chrdev_region(imx6uirq.devid, DEVICE_CNT, DEVICE_NAME);
	} 
	else 
	{						/* 没有定义设备号 */
		alloc_chrdev_region(&imx6uirq.devid, 0, DEVICE_CNT, DEVICE_NAME);	/* 申请设备号 */
		imx6uirq.major = MAJOR(imx6uirq.devid);	/* 获取分配号的主设备号 */
		imx6uirq.minor = MINOR(imx6uirq.devid);	/* 获取分配号的次设备号 */
	}
	
	/* 2 初始化cdev
	   设置cdev结构体的拥有者为当前模块(THIS_MODULE),然后使用 cdev_init() 函数初始化cdev结构体。
	   参数包括待初始化的cdev结构体和用于操作该设备的file_operations结构体(imx6uirq_drv) */
	imx6uirq.cdev.owner = THIS_MODULE;
	cdev_init(&imx6uirq.cdev, &imx6uirq_drv);

	/* 3、添加一个cdev */
	cdev_add(&imx6uirq.cdev, imx6uirq.devid, DEVICE_CNT);

	/*4 创建设备类
	   使用 class_create() 函数创建一个设备类,设备类用于在/sys/class目录下创建子目录,以组织同一类设备的相关信息。
	   该函数的参数包括所属的模块(THIS_MODULE)和设备类的名称(imx6uirq_NAME)。
	   如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */
	imx6uirq.class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(imx6uirq.class)) {
		return PTR_ERR(imx6uirq.class);
	}

	/*5 创建设备
	   使用 device_create() 函数创建一个设备,并在/dev目录下创建相应的设备节点。
	   参数包括设备所属的类(imx6uirq.class)、父设备(NULL,如果没有父设备)、设备号(imx6uirq.devid)、设备私有数据(NULL,一般为设备驱动程序提供一个指针)和设备名称(imx6uirq_NAME)。
	   如果创建失败,IS_ERR() 函数将返回true,表示出错,此时使用 PTR_ERR() 函数返回错误码。 */
	imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, DEVICE_NAME);
	if (IS_ERR(imx6uirq.device)) {
		return PTR_ERR(imx6uirq.device);
	}
	atomic_set(&imx6uirq.keyvalue, 0XFF);
	atomic_set(&imx6uirq.releasekey, 0);
	keyio_init(&imx6uirq);
	mytimer_init(&imx6uirq);
	printk("imx6uirq init!\r\n");
	return 0;
}

/************有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数****************/
static void __exit imx6uirq_exit(void)
{
	del_timer_sync(&imx6uirq.timer);
	free_irq(imx6uirq.irqkeydesc[0].irq_num,&imx6uirq);
	gpio_free(imx6uirq.irqkeydesc[0].gpio_num);

	/*在模块卸载时,使用 cdev_del() 函数注销字符设备驱动,并使用 unregister_chrdev_region() 函数释放设备号资源。*/
	/* 注销字符设备驱动 */
	cdev_del(&imx6uirq.cdev);/*  删除cdev */
	unregister_chrdev_region(imx6uirq.devid, DEVICE_CNT); /* 注销设备号 */

	device_destroy(imx6uirq.class, imx6uirq.devid);// 销毁设备,删除相应的设备节点
	class_destroy(imx6uirq.class);// 销毁设备类,释放相关资源
	printk("imx6uirq_drv exit!\r\n");
}

/*****************其他完善:提供设备信息**********************/
module_init(imx6uirq_init);
module_exit(imx6uirq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("oudafa");
MODULE_LICENSE("GPL");

四、应用程序的编写.

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"
/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd;
	int ret = 0;
	char *filename;
	unsigned char data;
	
	if (argc != 2) {
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];
	fd = open(filename, O_RDWR);
	if (fd < 0) {
		printf("Can't open file %s\r\n", filename);
		return -1;
	}

	while (1) {
		ret = read(fd, &data, sizeof(data));
		if (ret < 0) {  /* 数据读取错误或者无效 */
			
		} else {		/* 数据读取正确 */
			if (data)	/* 读取到数据 */
				printf("key value = %#X\r\n", data);
		}
	}
	close(fd);
	return ret;
}

五、实验测试

测试流程和之前一样,先使用make命令生成.ko文件还有APP这 两 个 文 件,然后拷贝到nfs挂载的rootfs/lib/modules/4.1.15 目录中,重启开发板。

先进入/lib/modules/4.1.15 目录,输入如下命令加载驱动模块:

insmod imx6uirq.ko

驱动加载成功以后可以通过查看/proc/interrupts 文件来检查一下对应的中断有没有被注册 上,输入如下命令:

 cat /proc/interrupts

可以看到如下:

从图可以看出 imx6uirq.c 驱动文件里面的 KEY0 中断已经存在了,触发方式为跳边沿(Edge),中断号为 49。

接下来使用如下命令来测试中断:

./imx6uirqApp /dev/imx6uirq

按下开发板上的 KEY0 键,终端就会输出按键值,如图所示:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MX6ULL学习笔记 (七) 中断实验 的相关文章

  • linux下安装nginx

    第一步 压缩包 准备压缩包 最好准备一个稳定的版本 下载地址 我这边选用的是1 24 0双版本号 第二步 解压 在相对应的目录下 执行命令 tar zxvf nginx 1 18 0 tar gz 第三步 配置 编译 推荐使用 config
  • [原创][4]探究C#多线程开发细节-“初步体验ManualResetEvent类带来的同步效果“

    简介 常用网名 猪头三 出生日期 1981 XX XX 个人网站 80x86汇编小站 https www x86asm org 编程生涯 2001年 至今 共22年 职业生涯 20年 开发语言 C C 80x86ASM PHP Perl O
  • 得帆云助力容百科技构建CRM系统,实现LTC全流程管理

    宁波容百新能源科技股份有限公司 宁波容百新能源科技股份有限公司 以下简称 容百科技 于2014年9月建立 是高科技新能源材料行业的 跨国型集团公司 专业从事锂电池正极材料的研发 生产和销售 于2019年登陆 上交所科创板 成为科创板 首批
  • Visual C++(VC)6.0-可视化软件编程开发工具 最新安装教程

    今天大成给大家分享的是 Visual C VC 6 0 可视化软件编程开发工具 安全无毒 无广告捆绑 运行稳定 快来下载体验吧 资源介绍 支持 Windows系统 Visual C 6 0是一个功能强大具有集成开发环境的可视化软件编程开发工
  • 如何做好小红书?9条小红书运营起号心得(必读)

    关于小红书运营细节和方法 总结了以下9条起号心得 希望给近期新手们一些经验借鉴 一 出现一条爆文后的策略 当账号新发的一篇笔记流量起飞了 不要急于发布新内容 先让爆文的流量消耗殆尽 等流量开始减少时再发布新笔记 同时 将新笔记置顶 这样访问

随机推荐

  • Linux下Redis安装及配置

    首先下载redis安装包 地址 这里我使用的是7 0版本的 将文件上传至linux上 此处不再多叙述 不会操作的 建议使用ftp 第一步 解压压缩包 tar zxvf redis 7 0 14 tar gz 第二步 移动redis存放目录
  • 测试基础知识

    常见测试分类 按测试阶段划分 单元测试 针对程序源码进行测试 国内是开发自测 集成测试 又称接口测试 针对模块间的访问地址进行测试 系统测试 对整个系统进行测试 包括功能 兼容性 文档等 验收测试 分为内测和公测 按代码可见度划分 黑盒测试
  • 智能联动第三方告警中心,完美实现故障响应全闭环

    前言 我们曾讨论完善的告警策略是整个数据监控系统的重要组成部分 参见 机智的告警策略 完善监控系统的重要一环 介绍了如何配置告警通知以及场景示例 帮助用户及时更多潜在的故障和问题 有效地保障系统稳定运行和持续优化 为了实现故障发现 告警 处
  • 最新SOTA!NGEL-SLAM:精度最高、速度超快的SLAM系统

    作者 Deep蓝同学 编辑 深蓝AI 原文链接 https arxiv org abs 2311 09525 点击下方 卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 点击进入 自动驾驶之心 SLAM 技术交流群 本文只做学术
  • 题解 | #Quasi Binary#

    题解 Quasi Binary 这道题只让再可能的数中有0或1出现 那么最少可能方案的数量只可能是每个位上的最大的数字 因为一定要在这个位上减去这个数目的一 才可以将这位变成0 接下来就是按每 题解 奇 妙拆分 这道题思路 很简单 要求最多
  • 【菜谱】鱼香肉丝

    具体步骤 猪里脊肉200克清水洗净 切成薄片再切成细丝 再用清水洗净 食用盐0 5克 少许胡椒粉 料酒5克 生抽酱油3克 鸡蛋清半个 后将肉丝朝一个方向搅拌2分钟 目的吸收调料水分 2分钟后加入适量干淀粉搅拌均匀 黑木耳40克 卷起切丝备用
  • JSON数据交换格式有几种?

    JSON JavaScript Object Notation 是一种常用的数据交换格式 它简洁 易于阅读和编写 并且易于解析和生成 在实际应用中 JSON有以下几种常见的数据交换格式 JSON 这是最常见的JSON数据交换格式 它使用键值
  • Windows命令行系列:网络命令

    ping ipconfig all 显示计算机网络情况 包括IP地址 DNA DHCP MAC地址等信息 release 释放IP地址 renew 重新获取IP地址 arp a 用于查看高速缓存中的所有项目 a IP 如果有多个网卡 那么使
  • 钱越来越难挣?这期程序员兼职干货没有水分!

    钱越来越难挣 程序员找兼职越来越难 结局只能指路美团 文末福利 还没看透职场 高薪 骗局 别人早就把精力放在了做副业上 兼职找不到 多半是经验不够 思路没打开 本篇文章 应该能让你茅塞顿开 收获颇丰 先喝点水 干货满满 下面容我娓娓道来 一
  • DDR详解

    DDR也就是常称的内存在一般使用过程中都是透明的 此文从多方面对DDR进行详解 DDR训练 高可靠性是系统级芯片SoC重要的质量和性能要求之一 SoC的复杂在于各个IP模块都对其产生至关重要的影响 从芯耀辉长期服务客户的经验来看 在客户的S
  • 比亚迪今年的薪资。。。

    综合自网络 网传比亚迪2022 2023 2024校招薪资 2024 届部分网友晒出的薪资 985本华五硕非f类 13k 1 36 12 985f本 9k 1 36 12 c9硕f类 18k 1 36 12 双非硕非f类 10k 1 36
  • 题解 | #0级用户高难度试卷的平均用时和平均得分#

    中煤科工开采研究院 大家有投中煤科工开采研究院的吗 一块交流交流 题解 按照格式输入并交换输出 include
  • Jquery如何获取和设置元素内容?

    在jQuery中 可以使用以下方法来获取和设置元素的内容 获取元素内容 text 获取元素的文本内容 包括其所有子元素的文本 var content div text html 获取元素的HTML内容 包括其所有子元素的HTML标记 var
  • U-BOOT移植的第一天

    编译NXP的UBOOT成功后 我们需要修改LCD 网络 DDR 接下来我们要在u boot添加自己的开发板 1 添加开发板默认配置文件 先在 configs 目录下创建默认配置文件 复制 mx6ull 14x14 evk emmc defc
  • Linux下设置redis临时密码和长期密码

    临时密码 第一步 先启动redis 命令 src redis server redis conf 第二步 进入redis 命令 src redis cli 第三步 查看密码 命令 config get requirepass 如果你redi
  • 基于Python手把手教你实现flappy bird游戏

    目录 前言 开始前的准备工作 进入正题 结束语 前言 想必玩过游戏的都知道 Flappy Bird是一款简单却富有挑战性的经典的小鸟飞行游戏 让许多玩家为之痴迷 而作为开发者 那肯定要通过技术手段来再做一遍这款经典游戏 那么本文就来通过万能
  • 英伟达高薪抢夺中国自动驾驶人才!吴新宙牵头,25大岗位!

    作者 有据无车 编辑 智能车参考 点击下方 卡片 关注 自动驾驶之心 公众号 ADAS巨卷干货 即可获取 点击进入 自动驾驶之心 求职交流 技术交流群 本文只做学术分享 如有侵权 联系删文 英伟达 开始在中国加大自动驾驶布局 官方刚刚发布了
  • Linux下activemq的安装与安装成功确认

    一 下载 apache activemq 5 14 0 bin tar gz 二 安装 将压缩包拷入linux内 进行解压 tar zxvf apache activemq 5 14 0 bin tar gz 与redis nginx不同的
  • CnosDB 科技春晚暨CnosDB 2.4.0 Milky Way发布会|我们程序员也有自己的节目啦

    CnosDB即将举办科技春晚 也是CnosDB 2 4 0版本发布会啦 举办地点就由各位爱码士选在电影院 在此也感谢大家的支持和参与 01 场地剧透 本次发布会正式选择电影院为春晚主办地的现在 就让我们先来一场Venue Tour吧 以上是
  • MX6ULL学习笔记 (七) 中断实验

    前言 本章我们就来学习一 下如何在 Linux 下使用中断 在linux内核里面使用中断 不同于我们以往在别的裸机开发一样 需要进行各种寄存器的配置 中断使能之类的 而在Linux 内核中 提供了完善的中断框架 我们只需要申请中断 然后注