Rt-Thread学习笔记-----信号量(五)

2023-05-16

线程间同步

1、什么是线程间同步?
同步是指按预定的先后次序进行运行,线程同步是指多个线程通过特定的机制来控制线程之间的执行顺序,也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间将是无序的。

每一个服务进程的运行,都包含若干进程(Thread),线程是调度的基本单位,进程则是资源拥有的基本单位。线程有自己的私有数据,比如栈和寄存器,同时与其它线程共享相同的虚拟内存和全局变量等资源,当多个线程同时读写同一份共享资源的时候,会引起冲突,这时候就需要引入线程同步机制使各个线程排队一个一个的对共享资源进行操作,而不是同时进行。
在这里插入图片描述
1.线程同步其实实现的是线程排队

2.防止线程同步访问共享资源造成冲突。

3.变量需要同步,常量不需要(常量存放于方法区)。

4.多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。

2、为什么要进行线程间同步?
例如一项工作中的两个线程:一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递:
在这里插入图片描述
如果对共享内存的访问不是排他性的,那么各个线程间可能同时访问它,这将引起数据一致性的问题。例如,在显示线程试图显示数据之前,接收线程还未完成数据的写入,那么显示将包含不同时间采样的数据,造成显示数据的错乱。将传感器数据写入到共享内存块的接收线程 #1 和将传感器数据从共享内存块中读出的线程 #2 都会访问同一块内存。为了防止出现数据的差错,两个线程访问的动作必须是互斥进行的,应该是在一个线程对共享内存块操作完成后,才允许另一个线程去操作,这样,接收线程 #1 与显示线程 #2 才能正常配合,使此项工作正确地执行。

在多线程实时系统中,一项工作的完成往往可以通过多个线程协调的方式共同来完成,那么多个线程之间如何 “默契” 协作才能使这项工作无差错执行?下面举个例子说明。

例如一项工作中的两个线程:一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递:

信号量基本概念

信号量(Semaphore)是一种实现线程间通信的机制,实现线程之间同步或临界资源的互斥访问,常用于协助一组相互竞争的线程来访问临界资源。在多线程系统中,各线程之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
在操作系统中,我们使用信号量的目的是为了给临界资源建立一个标志,信号量表示了该临界资源被占用情况。这样,当一个线程在访问临界资源的时候,就会先对这个资源信息进行查询,从而在了解资源被占用的情况之后,再做处理,从而使得临界资源得到有效的保护。

**PS:**相当于在裸机编程中这样使用过一个变量:用于标记某个事件是否发生,或者标志一下某个东西是否正在被使用,如果是被占用了的或者没发生,我们就不对它进行操作。(类似Flag标志位)

信号量分为二值及计数型。

二值信号量:
在嵌入式操作系统中二值信号量是线程间、线程与中断间同步的重要手段。为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0和 1 两种情况的信号量称之为二值信号量。

比如某个线程需要等待一个标记,那么线程可以在轮询中查询这个标记有没有被置位,这样子做,就会很消耗 CPU 资源,其实根本不需要在轮询中查询这个标记,只需要使用二值信号量即可,当二值信号量没有的时候,线程进入阻塞态等待二值信号量到来即可,当得到了这个信号量(标记)之后,在进行线程的处理即可,这样子么就不会消耗太多资源了,而且实时响应也是最快的。

再比如某个线程使用信号量在等中断的标记的发生,在这之前线程已经进入了阻塞态,在等待着中断的发生,当在中断发生之后,释放一个信号量,也就是我们常说的标记,当它退出中断之后,操作系统进行线程的调度,如果这个线程能够运行,系统就会把等待这个线程运行起来,这样子就大大提高了我们的效率。

举例说明来理解二值信号量:
1、线程与线程之间同步
有两个线程任务T1、T2。T1为温度传感器1S采集一次外界温度。T2为LCD显示采集到的温度,理论上也是1S变化一次温度值。如果LCD的屏幕刷新率为10ms,那么此时的温湿度的数据还没更新,液晶屏根本无需刷新,只需要在 1s 后温湿度数据更新的时候刷新即可。那么在裸机中,经常会用到轮询方式,即一直在刷新LCD屏幕,即使没有到下一个温度值到来,再不需要更新数值时,也要刷新。这样CPU资源会被LCD刷新占有,影响资源的合理使用。

如果液晶屏刷新的周期是 10s 更新一次,那么温湿度的数据都变化了 10 次,液晶屏才来更新数据,那拿这个产品有啥用,根本就是不准确的,所以,还是需要同步协调工作,在温湿度采集完毕之后,进行液晶屏数据的刷新,这样子,才是最准确的,并且不会浪费 CPU的资源。

2、线程与中断之间同步
二值信号量在线程与中断同步的应用场景:我们在串口接收中,我们不知道啥时候有数据发送过来,有一个线程是做接收这些数据处理,总不能在线程中每时每刻都在线程查询有没有数据到来,那样会浪费 CPU 资源,所以在这种情况下使用二值信号量是很好的办法,当没有数据到来的时候,线程就进入阻塞态,不参与线程的调度,等到数据到来了,释放一个二值信号量,线程就立即从阻塞态中解除,进入就绪态,然后运行的时候处理数据,这样子系统的资源就会很好的被利用起来。

信号量接口函数

1、创建和删除信号量

//创建
 rt_sem_t rt_sem_create(const char *name,
                        rt_uint32_t value,
                        rt_uint8_t flag);
//删除
rt_err_t rt_sem_delete(rt_sem_t sem);

当调用信号量创建函数时,系统将先从对象管理器中分配一个 semaphore 对象,并初始化这个对象,然后初始化父类 IPC 对象以及与 semaphore 相关的部分。在创建信号量指定的参数中,信号量标志参数决定了当信号量不可用时,多个线程等待的排队方式。当选择 RT_IPC_FLAG_FIFO(先进先出)方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择 RT_IPC_FLAG_PRIO(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。下表描述了该函数的输入参数与返回值:
在这里插入图片描述
2、初始化和脱离信号量
初始化
对于静态信号量对象,它的内存空间在编译时期就被编译器分配出来,放在读写数据段或未初始化数据段上,此时使用信号量就不再需要使用 rt_sem_create 接口来创建它,而只需在使用前对它进行初始化即可。初始化信号量对象可使用下面的函数接口:

rt_err_t rt_sem_init(rt_sem_t       sem,
                    const char     *name,
                    rt_uint32_t    value,
                    rt_uint8_t     flag)

当调用这个函数时,系统将对这个 semaphore 对象进行初始化,然后初始化 IPC 对象以及与 semaphore 相关的部分。信号量标志可用上面创建信号量函数里提到的标志。下表描述了该函数的输入参数与返回值:
在这里插入图片描述
脱离信号量就是让信号量对象从内核对象管理器中脱离,适用于静态初始化的信号量。脱离信号量使用下面的函数接口:

rt_err_t rt_sem_detach(rt_sem_t sem);

使用该函数后,内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离。原来挂起在信号量上的等待线程将获得 - RT_ERROR 的返回值。下表描述了该函数的输入参数与返回值:
在这里插入图片描述
3、获取信号量
线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1,获取信号量使用下面的函数接口:

rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);

在调用这个函数时,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数 time 指定的时间内依然得不到信号量,线程将超时返回,返回值是 - RT_ETIMEOUT。下表描述了该函数的输入参数与返回值:
在这里插入图片描述
4、释放信号量
释放信号量可以唤醒挂起在该信号量上的线程。释放信号量使用下面的函数接口:

rt_err_t rt_sem_release(rt_sem_t sem);

例如当信号量的值等于零时,并且有线程等待这个信号量时,释放信号量将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量;否则将把信号量的值加 1。下表描述了该函数的输入参数与返回值:
在这里插入图片描述

二值信号量的运作机制

创建二值信号量,为创建的信号量对象分配内存,并把可用信号量初始化为用户自定义的个数, 二值信号量的最大可用信号量个数为 1。

信号量获取,从创建的信号量资源中获取一个信号量,获取成功返回正确。否则线程会等待其它线程释放该信号量,超时时间由用户设定。当线程获取信号量失败时,线程将进入阻塞态,系统将线程挂到该信号量的阻塞列表中。在二值信号量无效的时候,假如此时有线程获取该信号量的话,那么线程将进入阻塞状态,具体见图
在这里插入图片描述

假如某个时间中断/线程释放了信号量,其过程具体见图 2,那么,由于获取无效信号量而进入阻塞态的线程将获得信号量并且恢复为就绪态,其过程具体见图3
在这里插入图片描述

二值信号量同步实验

信号量同步实验是在 RT-Thread 中创建了两个线程,一个是获取信号量线程,一个是释放互斥量线程,两个线程独立运行,获取信号量线程是一直在等待信号量,其等待时间是 RT_WAITING_FOREVER,等到获取到信号量之后,线程处理完毕时它又马上释放信号量。

编写thread1和thread2的入口函数。Thread1以RT_WAITING_FOREVER的方式获取一个信号量,操作完后进程内容释放信号量。此时while循环应该继续运行的,但是由于thread1释放信号量之后,thread2以RT_WAITING_FOREVER的方式获取到了信号量,使信号量的value为0,thread1便停止在rt_sem_take(dynamic_sem,RT_WAITING_FOREVER)处。Thread2操作完进程内容之后,释放了一个信号量,Thread1获取到该信号量,继续运行。

释放互斥量线程利用延时模拟占用信号量,延时的这段时间,获取线程无法获得信号量,等到释放线程使用完信号量,然后释放信号量,此时释放信号量会唤醒获取线程,获取线程开始运行,然后形成两个线程间的同步,若是线程正常同步,则在串口打印出信息

/*
******************************************************************
*                           二值信号量部分
******************************************************************
*/
/*
******************************************************************
*                               变量
******************************************************************
*/
/* 定义线程控制块 */
static rt_thread_t receive_thread = RT_NULL;
static rt_thread_t send_thread = RT_NULL;
/* 定义信号量控制块 */
static rt_sem_t test_sem = RT_NULL;

/************************* 全局变量声明 ****************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */
uint8_t ucValue [ 2 ] = { 0x00, 0x00 };
/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void receive_thread_entry(void* parameter);
static void send_thread_entry(void* parameter);
/*
*************************************************************************
*                             线程定义
*************************************************************************
*/

static void receive_thread_entry(void* parameter)
{	
  /* 任务都是一个无限循环,不能返回 */
  while(1)
	{
		rt_sem_take(test_sem,	/* 获取信号量 */
                RT_WAITING_FOREVER); 	/* 等待时间:一直等 */
		if ( ucValue [ 0 ] == ucValue [ 1 ] )
		{ 			
			rt_kprintf ( "Successful\n" );			
		}
		else
		{
			rt_kprintf ( "Fail\n" );			
		}
		rt_sem_release(	test_sem	);   //释放二值信号量 
		
		rt_thread_delay ( 1000 );  					      //每1s读一次		
  }
  
  
}

static void send_thread_entry(void* parameter)
{	
    /* 任务都是一个无限循环,不能返回 */
    while (1)
    {
			rt_sem_take(test_sem,				/* 获取信号量 */
                  RT_WAITING_FOREVER);	 		/* 等待时间:一直等 */		
		ucValue [ 0 ] ++;		
		rt_thread_delay ( 100 );        	 	/* 延时100ms */		
		ucValue [ 1 ] ++;		
		rt_sem_release(	test_sem	);			//释放二值信号量
		rt_thread_yield();  					//放弃剩余时间片,进行一次任务切换	
    }
}


static int Binary_Semaphore(void)
{
    /* 
	 * 开发板硬件初始化,RTT系统初始化已经在main函数之前完成,
	 * 即在component.c文件中的rtthread_startup()函数中完成了。
	 * 所以在main函数中,只需要创建线程和启动线程即可。
	 */
	rt_kprintf("这是一个RTT二值信号量同步实验!\n");
  rt_kprintf("同步成功则输出Successful,反之输出Fail\n");
   /* 创建一个信号量 */
	test_sem = rt_sem_create("test_sem",/* 信号量名字 */
                     1,     /* 信号量初始值,默认有一个信号量 */
                     RT_IPC_FLAG_FIFO); /* 信号量模式 FIFO(0x00)*/
  if (test_sem != RT_NULL)
    rt_kprintf("信号量创建成功!\n\n");
    
	receive_thread =                          /* 线程控制块指针 */
    rt_thread_create( "receive",            /* 线程名字 */
                      receive_thread_entry, /* 线程入口函数 */
                      RT_NULL,              /* 线程入口函数参数 */
                      512,                  /* 线程栈大小 */
                      3,                    /* 线程的优先级 */
                      20);                  /* 线程时间片 */
                   
    /* 启动线程,开启调度 */
   if (receive_thread != RT_NULL)
        rt_thread_startup(receive_thread);
    else
        return -1;
    
  send_thread =                            /* 线程控制块指针 */
    rt_thread_create( "send",              /* 线程名字 */
                      send_thread_entry,   /* 线程入口函数 */
                      RT_NULL,             /* 线程入口函数参数 */
                      512,                 /* 线程栈大小 */
                      2,                   /* 线程的优先级 */
                      20);                 /* 线程时间片 */
                   
    /* 启动线程,开启调度 */
   if (send_thread != RT_NULL)
        rt_thread_startup(send_thread);
    else
        return -1;		
}

下载验证:
在这里插入图片描述

计数型信号量的运作机制

计数型信号量与二值信号量其实都是差不多的,一样用于资源保护,不过计数信号量则允许多个线程获取信号量访问共享资源,但会限制线程的最大数目。访问的线程数达到信号量可支持的最大数目时,会阻塞其他试图获取该信号量的线程,直到有线程释放了信号量。这就是计数型信号量的运作机制,虽然计数信号量允许多个线程访问同一个资源,但是也有限定,比如某个资源限定只能有 3 个线程访问,那么第 4 个线程访问的时候,会因为获取不到信号量而进入阻塞,等到有线程(比如线程 1)释放掉该资源的时候,第 4个线程才能获取到信号量从而进行资源的访问,其运作的机制具体见图 19-4。
在这里插入图片描述
计数信号量实验
计数型信号量实验是模拟停车场工作运行。在创建信号量的时候初始化 5 个可用的信号量,并且创建了两个线程:一个是获取信号量线程,一个是释放信号量线程,两个线程独立运行,获取信号量线程是通过按下 K1 按键进行信号量的获取,模拟停车场停车操作,其等待时间是 0,在串口调试助手输出相应信息。释放信号量线程则是信号量的释放,释放信号量线程也是通过按下 K2 按键进行信号量的释放,模拟停车场取车操作,在串口调试助手输出相应信息

/*
******************************************************************
*                           计数信号量部分
******************************************************************
*/
/*
******************************************************************
*                               变量
******************************************************************
*/
/* 定义线程控制块 */
static rt_thread_t count_receive_thread = RT_NULL;
static rt_thread_t count_send_thread = RT_NULL;
/* 定义消息队列控制块 */
static rt_sem_t count_test_sem = RT_NULL;

/************************* 全局变量声明 ****************************/
/*
 * 当我们在写应用程序的时候,可能需要用到一些全局变量。
 */
/*
*************************************************************************
*                             函数声明
*************************************************************************
*/
static void count_receive_thread_entry(void* parameter);
static void count_send_thread_entry(void* parameter);

/*
*************************************************************************
*                             线程定义
*************************************************************************
*/

static void count_receive_thread_entry(void* parameter)
{		
  rt_err_t uwRet = RT_EOK;
  /* 任务都是一个无限循环,不能返回 */
  while(1)
	{
		if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )       //如果KEY2被单击
		{
			/* 获取一个计数信号量 */
      uwRet = rt_sem_take(count_test_sem,	
                          0); 	/* 等待时间:0 */
			if ( RT_EOK == uwRet ) 
				rt_kprintf( "KEY1被单击:成功申请到停车位。\n" );
			else
				rt_kprintf( "KEY1被单击:不好意思,现在停车场已满!\n" );							
		}
		rt_thread_delay(20);     //每20ms扫描一次		
  }
}

static void count_send_thread_entry(void* parameter)
{	
	rt_err_t uwRet = RT_EOK;
    /* 任务都是一个无限循环,不能返回 */
  while (1)
  {
		if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )       //如果KEY2被单击
		{
			/* 释放一个计数信号量 */
			uwRet = rt_sem_release(count_test_sem);    
			if ( RT_EOK == uwRet ) 
				rt_kprintf ( "KEY2被单击:释放1个停车位。\n" );	
			else
				rt_kprintf ( "KEY2被单击:但已无车位可以释放!\n" );					
		}
		rt_thread_delay(20);     //每20ms扫描一次		
  }
}


static int Count_semaphore(void)
{
    /* 
	 * 开发板硬件初始化,RTT系统初始化已经在main函数之前完成,
	 * 即在component.c文件中的rtthread_startup()函数中完成了。
	 * 所以在main函数中,只需要创建线程和启动线程即可。
	 */
	rt_kprintf("这是一个RTT计数信号量实验!\n");
  rt_kprintf("车位默认值为5个,按下K1申请车位,按下K2释放车位!\n\n");
   /* 创建一个信号量 */
	count_test_sem = rt_sem_create("count_test_sem",		/* 计数信号量名字 */
                     5,     								/* 信号量初始值,默认有5个信号量 */
                     RT_IPC_FLAG_FIFO); 					/* 信号量模式 FIFO(0x00)*/
  if (count_test_sem != RT_NULL)
    rt_kprintf("计数信号量创建成功!\n\n");
    
	count_receive_thread =                          /* 线程控制块指针 */
    rt_thread_create( "count_receive",              /* 线程名字 */
                      count_receive_thread_entry,   /* 线程入口函数 */
                      RT_NULL,             			/* 线程入口函数参数 */
                      512,                 			/* 线程栈大小 */
                      3,                   			/* 线程的优先级 */
                      20);                 			/* 线程时间片 */
                   
    /* 启动线程,开启调度 */
   if (count_receive_thread != RT_NULL)
        rt_thread_startup(count_receive_thread);
    else
        return -1;
    
  count_send_thread =                          	  /* 线程控制块指针 */
    rt_thread_create( "count_send",               /* 线程名字 */
                      count_send_thread_entry,    /* 线程入口函数 */
                      RT_NULL,             		  /* 线程入口函数参数 */
                      512,                 		  /* 线程栈大小 */
                      2,                   		  /* 线程的优先级 */
                      20);                	      /* 线程时间片 */
                   
    /* 启动线程,开启调度 */
   if (count_send_thread != RT_NULL)
        rt_thread_startup(count_send_thread);
    else
        return -1;	
}

在这里插入图片描述
rtthread信号量工程

总结

信号量:信号量是用来解决线程同步和互斥的通用工具,和互斥量类似,信号量也可用作资源互斥访问,但信号量没有所有者的概念,在应用上比互斥量更广泛。信号量比较简单,不能解决优先级翻转问题,但信号量是一种轻量级的对象,比互斥量小巧、灵活。因此在很多对互斥要求不严格的系统中(或者不会造成优先级翻转的情况下),经常使用信号量来管理互斥资源。简而言之,信号量就是一个信号,类似于我们平常自己设定的标志位。通过这个信号的状态(0或者非0)来表征当前线程的状态(是否可以运行)。每次线程申请一次信号量,信号量变量的数值会减一,反之,释放一个信号量,信号量变量的数值加一。

邮箱:邮箱服务是实时操作系统中一种典型的任务间通信方法,通常开销比较低,效率较高,每一封邮件只能容纳固定的4字节内容(针对32位处理系统,刚好能够容纳一个指针)。这里需要注意的是,邮箱中每一封邮件的容量是有限的,因此,如果信号量小于4个字节,那么可以直接利用邮件传达信息,否则,需要利用邮件传送保存信息的变量指针。

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

Rt-Thread学习笔记-----信号量(五) 的相关文章

  • ubuntu20.04双系统分区

    一 分四个区 xff0c 记录下 主分区和逻辑分区目前感觉没啥影响 但是有的电脑可能数量会有限制 boot引导分区 500M 交换分区 xff0c 16G及以下与内存大小一致 根目录 xff0c 如果要安装cuda之类的尽量50G以上吧 其
  • Vins-Fusion运行kitti,euroc和tum数据集并使用evo评估

    基于ubuntu18 04 VIns Fusion 1 修改程序输出位姿信息修改为TUM格式 为了方便评估 xff0c 先将程序的输出位姿信息修改为tum格式 xff0c 需要做如下改动 1 1 回环输出位姿文件pose graph cpp
  • JETSON ORIN NX 烧录系统镜像

    所需环境 xff1a Ubuntu 操作系统 本方法适用于SDKManager 找不到套件的情况 xff01 解决方法如下 xff1a 进入NVIDIA官网 xff08 Jetson Linux 35 2 1 NVIDIA Develope
  • 解决Komodo上自带D435可以打开而外接D435i打不开的原因

    解决Komodo上自带D435可以打开而外接D435i打不开的原因 因为Komodo上装的realsense SDK 和realsense ros版本不对应以及版本太低的原因 xff0c 所以各种问题 按照realsense和realsen
  • C++泛型编程,标准模板库(STL)

    C 43 43 的特点就是添加了面向对象和泛型 面向对象是用类实现的 xff0c 泛型是用模板实现的 xff0c C 43 43 的标准模板库 STL 是泛型一个实例 xff0c 已经被集成到C 43 43 xff0c STL是一些常用的数
  • [工程编写]CMakeLists.txt 如何编写

    最近开始写一些SLAM工程 xff0c 所以对涉及到的内容做一些整理 xff0c 那么要做的第一件事情就是编写一个CMakeLists txt 本文那任乾大佬在知乎关于雷达自动驾驶SLAM的工程距离 1 信息指定 cmake版本确定 xff
  • 在执行 pip install 时遇到错误: ERROR: Complete output from command python setup.py egg_info:

    在执行 pip install 时遇到错误 xff1a pip install U docker compose ERROR Complete output from command python setup py egg info ERR
  • 树莓派登录问题

    在使用树莓派的过程中 xff0c 多次遇到了开机后输入正确密码却被重复多次要求输入的情况 xff0c 在查看他人经验后 xff0c 总结出以下两种情况及可行的方法 不确定密码是否正确 参考博客 纸末 树莓派忘记密码的解决方法 1 xff0c
  • 什么是SDK和API?

    SDK xff1a 在搭建环境的时候知道的sdk xff0c 但并不知道有什么用 SDK Software Development Kit 翻译成中文就是 34 软体开发工具组 34 是用来帮一个 产品 或 平台 开发应用程式的工具组 xf
  • 华为开发者大会总结——个人总结

    方舟编译器 xff08 开源 xff09 xff1a 干掉Java虚拟机 将java代码直接编译成机器码 xff0c 静态语义好编译 xff0c 核心是静态编译出动态语义 xff08 通过华为编译实验室的核心专利 xff09 xff0c 代
  • LoRa关键参数 1、扩频因子(SF) 2、编码率(CR) 3、信号带宽(BW) 4、LoRa信号带宽BW、符号速率Rs和数据速率DR的关系 5、

    LoRa 学习 xff1a LoRa 关键参数 xff08 扩频因子 xff0c 编码率 xff0c 带宽 xff09 的设定及解释 1 扩频因子 xff08 SF xff09 2 编码率 xff08 CR xff09 3 信号带宽 xff
  • MarkDown的语法

    使用MarkDoown时应该注意些什么呢 目录 一 添加标题 二 引用代码块 三 嵌入图片 1 本地图片 2 互联网图片 四 快捷键使用 五 分界线的使用 一 添加标题 在文字前加入相应数量的 一级标题 二级标题 三级标题 四级标题 五级标
  • python试爬李毅吧贴子标题,爬虫最初级

    注 xff1a 以下所有python代码均运行于2 7 0 最近想抓点数据存起来 xff0c 开始捣鼓python 爬虫技术以前没接触过 xff0c 这一回就当练手 xff0c 从零开始 xff0c 从最原始的方式开始 先定个小目标 xff
  • Activity的生命周期

    图来自百度百科 onCreate 启动activity时被调用 xff0c 用于进行初始化操作 xff08 加载布局 绑定事件等 xff09 xff0c 不应在onCreate 中做过多的不必要操作 xff0c 避免造成打开activity
  • Activity的启动模式

    以下为读书笔记 xff1a 实际项目中 xff0c 我们要通过特定的需求 xff0c 为每个活动指定恰当的启动模式 Android一共有4种启动模式 xff1a standard singleTop singleTask singleIns
  • PID参数 Ziegler-Nichols基于时域响应曲线的整定 反应曲线法

    PID控制器是工业过程控制中广泛采用的一种控制器 xff0c 其中 xff0c P I D分别为比例 xff08 Proportion xff09 积分 xff08 Integral xff09 微分 xff08 Differential
  • 郑学坚《微型计算机原理及应用》考点精讲 36讲

    链接 xff1a https pan baidu com s 12 vGBRrjOd UtO8P4e9bow 提取码 xff1a tqmp 当初考研时买的网课 xff0c 现在也用不着了 xff0c 赠与有缘人 书籍记录着别人多年时光总结出
  • 几种常用排序算法

    排序算法代码如下 xff1a void Sort Algorithm Bubble Sort int amp nums const int len bool haschange 61 true for int i 61 0 i lt len
  • 四旋翼姿态解算

    代码思路如下 xff1a 姿态解算 void IMU update float dT imu state st state float gyr VEC XYZ s32 acc VEC XYZ s16 mag val VEC XYZ imu
  • 树莓派4B-基于MCP2515的CAN通信过程记录篇

    本文主要记录使用树莓派4B xff0c 通过外接MCP2515模块来实现CAN通信 xff0c 使用基于Can utils实现CAN消息的收发 准备工作 xff1a 树莓派MCP2515模块杜邦线若干 知乎上一篇较为详细的参考 xff0c

随机推荐

  • nuxt踩坑集

    目录结构 assets 资源文件 用于组织未编译的静态资源如 LESS SASS或 JavaScript components 组件 layouts page 模板页面 xff0c 默认为 default vue可以在这个目录下创建全局页面
  • 记录个人在安装D435i驱动时出现的问题及解决方案

    目录 1 驱动安装 xff1a 2 启动Intel RealSense Viewer后报错No Frames Recevied 3 Record时报错Error opening file 1 驱动安装 xff1a 参考这篇博客 xff1a
  • 深度学习常见的优化算法

    文章目录 1 梯度下降算法 xff08 batch gradient descent BGD 2 随机梯度下降法 xff08 Stochastic gradient descent SGD 3 小批量梯度下降 Mini batch grad
  • vscode如何链接github

    1 首先安装配置好vscode xff1a https code visualstudio com download 然后下载git xff1a https git scm com download 根据自己的电脑选择相应的版本的下载就好了
  • 串口(uart)开发流程

    UART概述 UART全称 xff0c Universal Asynchronous Receiver and Transmitter UART xff0c 一般是每个单片机或开发板必备的一个功能模块 xff0c 可以用来调试 xff0c
  • *33.硬中断和软中断是什么?区别?

    1 硬中断 硬件中断 像磁盘 xff0c 网卡 xff0c 键盘 xff0c 时钟等 2 软中断 软件中断 进程产生 区别 xff1a xff08 产生机制 处理顺序 可否屏蔽 中断信号由来 xff09 1 软中断的发生的时间是由程序控制的
  • gazebo学习时遇到的问题(PX4篇)

    最近在研究如何使用UAV xff0c 由于是初学者 xff0c 不好直接上手实体无人机 xff0c 因此考虑通过在gazebo中模拟的方式进行一下无人机的简单使用模拟 xff0c 于是了解到了PX4这个东西 xff0c 他不仅支持固件写入
  • 阿里云服务器的搭建和部署(小白教程)

    阿里云服务器的搭建和部署 xff08 小白教程 xff09 一 购买服务器二 管理云服务器三 环境配置1 JDK的配置与下载2 tomcat的配置与下载3 MySQL的安装与配置 四 外网访问服务器 个人推荐阿里云 因为里面有一个大学生的云
  • 一篇带你搞懂Python3 中的 def __init__

    在学习python 100 days时 xff0c 在面向对象编程基础的那一块 xff0c 封装一个class时 xff0c 突然出现def init 的方法 xff0c 刚开始时 xff0c 对他的理解很模糊 xff0c 为什么定义一个类
  • 时间序列异常检测综述

    1 Introduction 时序异常检测几个可以运用的点 xff1a 1 欺诈检测 2 工业数据检测 简介一下 xff1a 异常检测很久之前就有了 最早可以追溯到 J Fox 1972 Outliers in Time Series Jo
  • Linux基础----Makefile(1)

    前言 刚开始学习Linux xff0c 这些那真的是新知识 xff0c 由于感觉将来FPGA的学习会越来越靠近软件的方向发展 xff0c 所以觉得有必要好好地学习一下嵌入式的东西 xff0c 因此有必要把学习的过程记录下来 xff0c 方便
  • 【OpenCV】ChArUco标定板角点的检测Detection of ChArUco Corners

    opencv3 4 15源文档链接 link ChArUco标定板角点的检测 GoalSource codeCharuco板创建ChArUco板检测ChArUco姿势估计 ArUco标记和板的快速检测和多功能性是非常有用的 然而 xff0c
  • 错误 MSB3721

    错误 MSB3721 命令 C Program Files NVIDIA GPU Computing Toolkit CUDA v10 2 bin nvcc exe gencode 61 arch 61 compute 30 code 61
  • 倒立摆状态反馈控制——分析、建模与仿真(matlab)

    倒立摆状态反馈控制 引言 倒立摆是机器人学中一个非常重要的模型 xff0c 火箭 导弹 独轮车 双足机器人 四足机器人 xff0c 基本都是倒立摆的变形 问题描述 关于倒立摆的问题描述如下 xff1a 如图所示的倒立摆系统 摆的关节连接在一
  • docker pull rabbitmq:management 怎样指定版本

    docker pull rabbitmq 3 8 1 management
  • git 命令学习

    git 命令学习 git clone https github com zhaji01 notes git 克隆远程仓库 git status 本地仓库状态 git add lt file gt 把修改 xff08 包括创建 修改 删除 解
  • 【实例记录】在ubuntu上运行python实现与单片机多线程串口通信

    文章目录 工具步骤 工具 工具 xff1a 自己电脑双系统的ubuntu18 04 单片机esp8266 一个usb转ttl模块 xff0c ubuntu上已经下载了python的3 6和pip xff0c 还需要pip install s
  • 【过程记录 】windows和ubuntu两台电脑局域网进行socket通信收发数据和传输文件

    实验图片和前期准备 xff1a ubuntu作为服务端 xff0c windows客户端传输数据和文件 xff1a windows作为服务端 xff0c ubuntu作为客户端 xff1a 实验图片和前期准备 xff1a 服务端发送和接受i
  • 过程记录 yolov3目标检测输出目标坐标和原图物体像素坐标比较

    设备 xff1a jetsonnano B01 一个usb广角摄像头 语言为python 库为opencv 框架pytorch 大家都知道yolov3的坐标变换过程比较复杂 xff0c 大概要经过图片缩放 xff0c 对数空间变换等等 xf
  • Rt-Thread学习笔记-----信号量(五)

    线程间同步 1 什么是线程间同步 xff1f 同步是指按预定的先后次序进行运行 xff0c 线程同步是指多个线程通过特定的机制来控制线程之间的执行顺序 xff0c 也可以说是在线程之间通过同步建立起执行顺序的关系 xff0c 如果没有同步