建设公司网站费用怎么做账,影视公司招聘,免费网上商城网站建设,公司网站建设总结报告目录
1. 信号量简介
2. 二值信号量
2.1 二值信号量简介
2.1.1 二值信号量无效
2.1.2 中断释放信号量
2.1.3 任务获取信号量成功
2.1.4 任务再次进入阻塞态
2.2 创建二值信号量
2.2.1 vSemaphoreCreateBinary()
2.2.2 xSemaphoreCreateBinary()
2.2.3 xSemaphoreCrea…目录
1. 信号量简介
2. 二值信号量
2.1 二值信号量简介
2.1.1 二值信号量无效
2.1.2 中断释放信号量
2.1.3 任务获取信号量成功
2.1.4 任务再次进入阻塞态
2.2 创建二值信号量
2.2.1 vSemaphoreCreateBinary()
2.2.2 xSemaphoreCreateBinary()
2.2.3 xSemaphoreCreateBinaryStatic()
2.3 二值信号量创建过程分析
2.4 释放信号量
2.4.1 函数 xSemaphoreGive()
2.4.2 函数 xSemaphoreGiveFromISR()
2.5 获取信号量
2.5.1 函数 xSemaphoreTake()
2.5.2 函数 xSemaphoreTakeFromISR()
3. 二值信号量操作实验
3.1 实验目的
3.2 实验设计
3.3 实验程序
3.3.1 USART.c
3.3.2 main.c 信号量是操作系统中重要的一部分信号量一般用于进行资源管理和任务同步在 FreeRTOS 中信号量可以分为二值信号量、计数型信号量、互斥信号量和递归互斥信号量。不同的信号量其应用场景不同但是有些应用场景是可以互换着使用的从这里开始我们进行信号量的学习
1. 信号量简介 信号量常常用于控制对共享资源的访问和任务同步。举个很常见的例子某个停车场有 100 个停车位这 100 个停车位大家都可以使用对于大家来说这 100 个停车位就是共享资源。现在假设这个停车场正常运行我们要把车停到这个停车场首先肯定要先看一下现在已经停了多少车还有没有停车位当前的停车数量就是一个信号量具体的停车数量就是这个信号量值当这个值到达 100 的时候就说明停车场满了。停车场满的时候我们可以等一会看看有没有其他的车开出停车场当有车开出停车场的时候停车数量就会加一也就是信号量加一。 这是一个典型的使用信号量进行共享资源管理的案例在这个案例中使用的就是计数型信号量。我们再看另外一个使用公共电话我们知道一次只能一个人使用电话这个时候公共电话就只可能有两个状态使用或未使用如果用电话的这两个状态作为信号量的话那么这个就是二值信号量。 信号量的另一个重要的应用场合是任务同步用于任务与任务或中断与任务之间的同步。在执行中断服务函数的时候可以通过向任务发送信号量来通知任务它所期待的事件发生了当退出中断服务函数以后在任务调度器的调度下同步的任务就会执行。在编写中断服务函数的时候我们都知道一定要快进快出中断服务函数里面不能放太多的代码否则的话就会影响中断的实时性。裸机编写中断服务函数的时候一般都只是在中断服务函数中打个标记然后在其他地方根据标记来做具体的处理过程。在使用 RTOS 系统的时候我们就可以借助信号量完成此功能当中断发生的时候就释放信号量中断服务函数不作具体的处理。具体的处理过程做成一个任务这个任务会获取信号量如果获取到信号量就说明中断发生了那么就开始完成相应的处理这么做的好处就是中断执行的时间非常短。 这里区别信号量和队列 队列是用来传输数据的而信号量是用来传输状态的 信号量表示一种状态 如果信号量为 0 和 1那么此时称为二值信号量 如果信号量为 1 2 3 4 5 6 7 8 9 10那么此时称为计数型信号量 队列 可以容纳多个数据 创建队列有两部分内存队列结构体 队列项存储空间 写入队列当队列满时可阻塞 读取队列当队列为空时可阻塞信号量 仅存放计数值无法存放其他数据 创建信号量只需分配信号量结构体 释放信号量不可阻塞计数值当计数值为最大值时返回失败 获取信号量计数值--当没有资源时可阻塞 信号量是一种解决同步问题的机制可以实现对共享资源的有序访问 在计算机中为了合理的进行内存管理引入共享资源的概念。同步问题就是说比如我让计算机去算 A B*C我们肯定知道要先算 B*C然后再去算 A B*C 的结果但是如果我们不加以限制计算机不会这样计算机会先去计算 A B然后再去计算乘法因此同步就是为了保证任务与任务之间的运行次序比如我先给乘法运行的任务一个信号量乘法运算的任务收到这个信号量开始在任务中执行乘法运算然后同样的过程进行加法运算这样就可以保证乘法运算是在加法运算之前进行的共享资源就是计算机中的所有任务都可以使用的资源但是同一时刻只能有一个任务进行使用你能想象打印机第一页打印 A 文档第二页打印 B 文档吗所以同步机制对于操作系统而言是至关重要的 2. 二值信号量
2.1 二值信号量简介 二值信号量通常用于互斥访问或同步。二值信号量和互斥信号量非常类似但是也是存在一些细微的差别互斥信号量拥有优先级继承机制二值信号量没有优先级继承。因此二值信号量更加适用于同步任务与任务或任务与中断的同步而互斥信号量适合于简单的互斥访问。 和队列一样信号量 API 函数允许设置一个阻塞时间。阻塞时间是当任务获取信号量的时候由于信号量无效从而导致任务进入阻塞态的最大时钟节拍数。如果多个任务同时阻塞在同一个信号量上的话那么优先级最高的那个任务优先获得信号量这样当信号量有效的时候高优先级的任务就会解除阻塞状态。 二值信号量其实就是一个只有一个队列项的队列队列长度为 1该队列就只有空 0 和满 1 两种情况这就是二值就像硬币的正反面一样。这个特殊的队列要么是满的要么是空的正如其名二值任务与中断使用这个特殊的队列不用在乎队列中存的是什么消息只需要知道这个队列是满的还是空的即可。可以利用这个机制完成任务与中断之间的同步。 在实际应用中通常会使用一个任务来处理 MCU 的某个外设比如网络应用中一般最简单的方法就是使用一个任务去轮询的查询 MCU 的 ETH网络相关外设如 STM32 的以太网 MAC外设是否有数据当有数据的时候就处理这个网络数据。这样的使用轮询的方式是很浪费 CPU 资源的而且也阻止了其他任务的运行。最理想的方法就是当没有网络数据的时候网络任务就进入阻塞态把 CPU 让给其他任务当有数据的时候网络任务才去执行。现在使用二值信号量就可以实现这样的功能任务通过获取信号量来判断是否有网络数据没有的话就进入阻塞态而网络中断服务函数大多数的网络外设都有中断功能比如 STM32 的 MAC 专用 DMA 中断通过中断可以判断是否接收到了数据通过释放信号量来通知任务以太网外设接收到了网络数据网络任务可以去提取处理了。网络任务只是在一直的获取二值信号量它不会释放信号量而中断服务函数是一直在释放信号量它不会获取信号量。在中断服务函数中发送信号量可以使用 xSemaphoreGiveFromISR()也可以使用任务通知功能来代替二值信号量而且使用任务通知的话速度更快代码量更少。 使用二值信号量来完成中断与任务同步的这个机制中任务优先级确保了外设能够得到及时的处理这样做相当于推迟了中断处理过程。也可以使用队列来代替二值信号量在外设事件的中断服务函数中获取相关数据并将相关的数据通过队列发送给任务。如果队列无效的话任务就进入阻塞态直至队列中有数据任务接收到数据以后就开始相关的处理过程。
2.1.1 二值信号量无效 上图中任务 Task 通过函数 xSemaphoreTake() 获取信号量但是此时二值信号量无效所以任务 Task 进入阻塞态。
2.1.2 中断释放信号量 此时中断发生了在中断服务函数中通过函数 xSemaphoreGiveFromISR() 释放信号量因此信号量变为有效。
2.1.3 任务获取信号量成功 由于信号量已经有效了所以任务 Task 获取信号量成功任务从阻塞态解除开始执行相关的处理过程。
2.1.4 任务再次进入阻塞态 由于任务函数一般都是一个大循环所以在任务做完相关的处理以后就会再次调用函数 xSemaphoreTake() 获取信号量。在执行完第三步以后二值信号量就已经变为无效的了所以任务将再次进入阻塞态和第一步一样直至中断再次发生并且调用函数 xSemaphoreGiveFromISR() 释放信号量。
2.2 创建二值信号量 和队列一样要想使用二值信号量就必须先创建二值信号量二值信号量创建函数如下表所示
vSemaphoreCreateBinary() 动态创建二值信号量(老版本的 FreeRTOS 创建信号量的 API 函数)
xSemaphoreCreateBinary() 动态创建二值信号量(新版本的 FreeRTOS 创建信号量的 API 函数)
xSemaphoreCreateBinaryStatic() 静态创建二值信号量
2.2.1 vSemaphoreCreateBinary() 此函数是老版本的 FreeRTOS 中的创建二值信号量的函数新版本已经不再使用了这里还保留这个函数是为了兼容那些基于老版本 FreeRTOS 而做的应用层代码。 此函数是一个宏具体创建过程由函数 xQueueGenericCreate() 来完成
void vSemaphoreCreateBinarySemaphoreHandle_t xSemaphore
参数 xSemaphore保存创建成功的二值信号量句柄
返回值 NULL二值信号量创建失败。 其他值二值信号量创建成功。
#define xSemaphoreCreateBinary()xQueueGenericCreate(1,semSEMAPHORE_QUEUE_ITEM_LENGTH,queueQUEUE_TYPE_BINARY_SEMAPHORE)
#define semSEMAPHORE_QUEUE_ITEM_LENGTH ((uint8_t)0U)宏定义为 semSEMAPHORE_QUEUE_ITEM_LENGTH 为 0所以创建的二值信号量队列长度为 0队列项 1 个 2.2.2 xSemaphoreCreateBinary() 此函数是创建二值信号量的新版本函数使用此函数来创建二值信号量的话信号量所需的 RAM 是由 FreeRTOS 的内存管理部分来动态分配的。此函数创建好的二值信号量默认是空的也就是说刚创建好的二值信号量使用函数 xSemaphoreTake() 是获取不到的因为获取二值信号量必须保证整个队列不为空此函数也是一个宏具体创建过程是由函数 xQueueGenericCreate() 来完成的函数原型如下
SemaphoreHandle_t xSemaphoreCreateBinary(void)
参数 无
返回值 NULL二值信号量创建失败。 其他值创建成功的二值信号量的句柄。
2.2.3 xSemaphoreCreateBinaryStatic() 此函数也是用来创建二值信号量的只不过使用此函数创建二值信号量的话信号量所需要的 RAM 需要由用户来分配此函数也是一个宏具体创建过程是通过函数 xQueueGenericCreateStatic() 来完成的函数原型如下
SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffe)
参数 pxSemaphoreBuffer此参数指向一个 StaticSemaphore_t 类型的变量用来保存信号量结构体。
返回值 NULL二值信号量创建失败。 其他值创建成功的二值信号量句柄。
2.3 二值信号量创建过程分析 上面我们学习二值信号量的创建函数两个动态创建函数和一个静态创建函数。 这里我们着重的看两个动态的创建函数
#if( configSUPPORT_DYNAMIC_ALLOCATION 1 ) #define vSemaphoreCreateBinary( xSemaphore ) \ { \ ( xSemaphore ) xQueueGenericCreate( ( UBaseType_t ) 1, \ (1) semSEMAPHORE_QUEUE_ITEM_LENGTH, \ queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ if( ( xSemaphore ) ! NULL ) \ { \ ( void ) xSemaphoreGive( ( xSemaphore ) ); \ (2) } \ }
#endif 1在上面我们提到了二值信号量是在队列的基础上实现的所以创建二值信号量其实就是创建队列的过程。这里使用函数 xQueueGenericCreate() 创建了一个队列队列长度为 1 队列项长度为 0队列类型为 queueQUEUE_TYPE_BINARY_SEMAPHORE也就是二值信号量。 2当二值信号量创建成功以后立即调用函数 xSemaphoreGive() 释放二值信号量此时新创建的二值信号量有效。 紧接着 我们来看新版本的二值信号量创建函数 xSemaphoreCreateBinary()函数代码如下
#if( configSUPPORT_DYNAMIC_ALLOCATION 1 ) #define xSemaphoreCreateBinary() \ xQueueGenericCreate( ( UBaseType_t ) 1, \ semSEMAPHORE_QUEUE_ITEM_LENGTH, \ queueQUEUE_TYPE_BINARY_SEMAPHORE ) \
#endif 通过观察可以发现新版本和旧版本的创建二值信号量的函数具有相同点也具有不同点新版本的二值信号量创建函数也是使用函数 xQueueGenericCreate() 来创建一个类型为 queueQUEUE_TYPE_BINARY_SEMAPHORE、长度为 1 、队列项长度为 0 的队列。这一步和老版本的创建二值信号量的函数一样唯一不同的是新版本的函数在成功创建二值信号量以后不会立即调用函数 xSemaphoreGive() 释放二值信号量。也就是说新版本函数创建二值信号量默认是无效的而老版本默认是有效的。 注意看创建的队列的队列项长度为 0 也就是说创建的队列是个没有存储区的队列前面说了使用队列是否为空来表示二值信号量而队列是否为空可以通过队列结构体的成员变量 uxMessagesWaiting 来判断。
2.4 释放信号量 释放信号量的函数有两个
xSemaphoreGive() 任务级信号量释放函数
xSemaphoreGiveFromISR() 中断级信号量释放函数 和队列一样释放信号量也分为任务级和中断级 注意 不管是二值信号量、计数型信号量还是互斥信号量它们都使用上述的函数释放信号量但是递归互斥信号量有专用的释放函数 2.4.1 函数 xSemaphoreGive() 此函数用于释放二值信号量、计数型信号量或互斥信号量此函数是一个宏真正释放信号量的过程是由函数 xQueueGenericSend() 来完成的函数原型如下
BaseType_t xSemaphoreGive(xSemaphore)
参数 xSemaphore 要释放的任务量句柄。
返回值 pdPASS 释放信号量成功。 errQUEUE_FULL 释放信号量失败。 紧接着我们来看一下函数 xSemaphoreGive() 的具体内容此函数在文件 semaphr.h 中有如下定义
#define xSemaphoreGive( xSemaphore ) \ xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \ NULL, \ semGIVE_BLOCK_TIME, \ queueSEND_TO_BACK ) \ //该函数总共4个参数
//第一个参数信号量句柄
//第二个参数发送的消息在信号量中都是针对的计数值所以为NULL
//第三个参数是阻塞时间这里设置为0也就是释放信号量计数值永远不会阻塞
//第四个参数表示队列的入队方式这里是后向入队 可以看出任务级释放信号量就是向队列发送消息的过程只是这里并没有发送具体的消息阻塞时间为 0 宏 semGIVE_BLOCK_TIME入队方式采用的后向入队。入队的时候队列结构体成员变量 uxMessagesWaiting 会加一对于二值信号量通过判断 uxMessagesWaiting 就可以知道信号量是否有效了当 uxMessagesWaiting 为 1 的话说明二值信号量有效为 0 就无效。如果队列满的话就返回错误值 errQUEUE_FULL提示队列满入队失败
2.4.2 函数 xSemaphoreGiveFromISR() 此函数用于在中断中释放信号量此函数只能用来释放二值信号量和计数型信号量绝对不能用来在中断服务函数中释放互斥信号量此函数是一个宏真正执行的是函数 xQueueGiveFromISR() 此函数原型如下
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphoreBaseType_t* pxHigherPriorityTaskWoken)
参数 xSemaphore 要释放的信号量句柄 pxHigherPriorityTaskWoken 标记退出此函数以后是否进行任务切换这个变量的值由这三个函数来设置用户不用进 行设置用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退 出中断服务函数之前一定要进行一次任务切换。
返回值 pdPASS 释放信号量成功。 errQUEUE_FULL 释放信号量失败。 在中断中释放信号量真正使用的是函数 xQueueGiveFromISR()此函数和中断级通用入队函数 xQueueGenericSendFromISR() 极其类似只是针对信号量做了微小的改动。函数 xSemaphoreGiveFromISR() 不能用于中断中释放互斥信号量因为互斥信号量涉及到优先级继承的问题而中断不属于任务没法处理中断优先级继承。
2.5 获取信号量 获取信号量也有两个函数如下表所示
xSemaphoreTake() 任务级获取信号量函数
xSemaphoreTakeFromISR() 中断级获取信号量函数 同释放信号量的 API 函数一样不管是二值信号量、计数型信号量还是互斥信号量它们都使用上述的函数获取信号量。
2.5.1 函数 xSemaphoreTake() 此函数用于获取二值信号量、计数型信号量或互斥信号量此函数是一个宏真正获取信号量的过程是由函数 xQueueGenericReceive() 来完成的函数原型如下
BaseType_t xSemaphoreTakeSemaphoreHandle_t xSemaphoreTickType_t xBlockTime
参数 xSemaphore要获取的信号量句柄 xBlockTime阻塞时间。
返回值 pdTRUE获取信号量成功。 pdFALSE超时获取信号量失败。 紧接着来看函数 xSemaphoreTake() 的具体内容此函数在文件 semaphr.h 中如下定义
#define xSemaphoreTake( xSemaphore, xBlockTime ) \ xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), \ NULL, \ ( xBlockTime ), \ pdFALSE ) \ 获取信号量的过程其实就是读取队列的过程只是这里并不是为了读取队列中的消息。在对队列学习时我们知道函数 xQueueGenericReceive() 的时候说过如果队列为空并且阻塞时间为 0 的话就立即返回 errQUEUE_EMPTY表示队列满。如果队列为空并且阻塞时间不为 0 的话就将任务添加到延时列表中。如果队列不为空的话就从队列中读取数据数据读取完成以后还需将队列结构体成员变量 uxMessagesWaiting 减一然后解除某些因为入队而阻塞的任务最后返回 pdPASS 表示出队成功。
2.5.2 函数 xSemaphoreTakeFromISR() 此函数用于在中断服务函数中获取信号量此函数用于获取二值信号量和计数型信号量绝对不能使用此函数来获取互斥信号量此函数是一个宏真正执行的是函数 xQueueReceiveFromISR()此函数原型如下
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphoreBaseType_t* pxHigherPriorityTaskWoken)
参数 xSemaphore要获取的信号量句柄。 pxHigherPriorityTaskWoken 标记退出此函数以后是否进行任务切换这个变量的值由这三个函数来设置用户不用进 行设置用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退 出中断服务函数之前一定要进行一次任务切换。
返回值 pdPASS获取信号量成功。 pdFALSE获取信号量失败。 在中断中获取信号量真正使用的是函数 xQueueReceiveFromISR()这个函数就是中断级出队函数当队列不为空的时候就拷贝队列中的数据然后将队列结构体中的成员变量 uxMessagesWaiting 减一如果有任务因为入队而阻塞的话就解除阻塞态当解除阻塞的任务拥有更高优先级的话就将参数 pxHigherPriorityTaskWoken 设置为 pdTRUE最后返回 pdPASS 表示出队成功。如果队列为空的话就直接返回 pdFAIL 表示出队失败
3. 二值信号量操作实验
3.1 实验目的 二值信号量的使命就是同步完成任务与任务或中断与任务之间的同步。大多数情况下都是中断与任务之间的同步。
3.2 实验设计 这里我们设置一个通过串口发送指定的指令来控制开发板上的的 LED1 和 BEEP 开关的实验指令如下不区分大小写 LED1ON打开 LED1。 LED1OFF关闭 LED1。 BEEPON打开蜂鸣器。 BEEPOFF关闭蜂鸣器。 这些指令通过串口发送给开发板指令是不区分大小写的开发板使用中断接收当接收到数据以后就释放二值信号量。 任务 DataProcess_task() 用于处理这些指令任务会一直尝试获取二值信号量当获取到信号量就会从串口接收缓冲区中提取这些指令然后根据指令控制相应的外设。简单来说就是串口将指令发送给开发板开发板通过中断来接收在中断中释放二值信号量然后任务 DataProcess_task 处理这些指令并且一直获取中断释放的二值信号量一旦获取到信号量就从串口接收缓冲区中提取这些指令然后根据指令控制相关的外设 本实验设计三个任务start_task、task1_task、DataProcess_task 这三个任务的任务功能如下 start_task用于创建其他两个任务。 task1_task控制 LED0 闪烁提示系统正在运行。 DataProcess_task指令处理任务根据接收到的指令来控制不同的外设。 实验中还创建了一个二值信号量 BinarySemaphore 用于完成串口中断和任务 DataProcess_task 之间的同步。
3.3 实验程序
3.3.1 USART.c
extern SemaphoreHandle_t BinarySemaphore; //信息队列句柄
//QueueHandle_t queue.h 中定义void USART1_IRQHandler(void) //串口1中断服务程序
{u8 Res;//xHigherPriorityTaskWoken用来标记退出此函数以后是否进行任务切换这个变量的值由三个函数来设置用户不再进行设置//用户只需要提供一个变量来保存这个值就可以了。//但是切记要注意当此值为 pdTURE 的时候在退出中断服务函数之前一定要进行一次任务切换。BaseType_t xHigherPriorityTaskWoken; //BaseType_t 也在 queue.h 中定义if(USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾){Res USART_ReceiveData(USART1);//(USART1-DR); //读取接收到的数据if((USART_RX_STA0x8000)0)//接收未完成{if(USART_RX_STA0x4000)//接收到了0x0d{if(Res!0x0a)USART_RX_STA0;//接收错误,重新开始else USART_RX_STA|0x8000; //接收完成了 }else //还没收到0X0D{ if(Res0x0d)USART_RX_STA|0x4000;else{USART_RX_BUF[USART_RX_STA0X3FFF]Res;USART_RX_STA;if(USART_RX_STA(USART_REC_LEN-1))USART_RX_STA0;//接收数据错误,重新开始接收 } }} }//释放二值信号量//指令通过串口发送给开发板串口中断用来释放二值信号量任务用来不断获取信号量//任务一旦获取到信号量就会从串口接收缓冲区中提取这些指令然后根据这些指令控制相应的外设if((USART_RX_STA0x8000)(BinarySemaphore!NULL)) //串口接收到数据并且二值信号量不为空也就表示二值信号量是有效的{xSemaphoreGiveFromISR(BinarySemaphore,xHigherPriorityTaskWoken); //调用在中断中释放二值信号量函数//函数第一个参数释放二值信号量句柄//函数第二个参数标记是否需要进行任务切换portYIELD_FROM_ISR(xHigherPriorityTaskWoken); //如果需要的话进行一次任务切换} //二值信号量用来实现同步的意思就是说保证中断先释放信号量然后任务在获取信号量
}
3.3.2 main.c
#include stm32f4xx.h
#include FreeRTOS.h //这里注意必须先引用FreeRTOS的头文件然后再引用task.h
#include task.h //存在一个先后的关系
#include LED.h
#include LCD.h
#include Key.h
#include usart.h
#include delay.h
#include string.h
#include beep.h
#include malloc.h
#include timer.h
#include queue.h
#include semphr.h//任务优先级
#define START_TASK_PRIO 1 //用于创建其他两个任务
//任务堆栈大小
#define START_STK_SIZE 256
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);//任务优先级
#define TASK1_TASK_PRIO 2 //控制 LED0 闪烁提示系统正在运行
//任务堆栈大小
#define TASK1_STK_SIZE 256
//任务句柄
TaskHandle_t Task1Task_Handler;
//任务函数
void task1_task(void *pvParameters);//任务优先级
#define DATAPROCESS_TASK_PRIO 3 //指令处理函数
//任务堆栈大小
#define DATAPROCESS_STK_SIZE 256
//任务句柄
TaskHandle_t DataProcess_Handler;
//任务函数
void DataProcess_task(void *pvParameters);//二值信号量句柄
SemaphoreHandle_t BinarySemaphore; //二值信号量句柄//用于命令解析用的命令值
#define LED1ON 1
#define LED1OFF 2
#define BEEPON 3
#define BEEPOFF 4
#define COMMANDERR 0xFF//函数 LowerToCap 用于将串口发送过来的命令中的小写字母统一转换成大写字母
//这样就可以在发送命令的时候不用区分大小写因为开发板会统一转换成大写。
//将字符串中的小写字母转换为大写
//str要转换的字符串
//len字符串长度
void LowerToCap(u8 *str,u8 len)
{u8 i;for(i0;ilen;i){//判断字符串的ASCII码是否位于96到123之间if((96str[i])(str[i]123)) //小写字母{//ASCII码是一种用于表示字符的编码系统。在ASCII码中每个字符都被赋予一个唯一的整数值。//大写字母的ASCII码值是65到90//小写字母的ASCII码值是97到122 所以一旦确定ASCII码值位于小写字母的范畴内只需要将ASCII码值减去32即可转换为大写str[i] str[i] - 32; //转换为大写}}
}//函数 CommandProcess 用于将接收到的命令字符串转换成命令值比如说命令“LED1ON”转换成命令值就是 0(宏LED1ON为 0)
//命令处理函数将字符串命令转换成命令值
//str命令
//返回值0xFF命令错误其他值命令值
u8 CommandProcess(u8 *str)
{u8 CommandValue COMMANDERR;if(strcmp((char*)str,LED1ON)0) //strcmp 字符串比较函数//这个函数会比较两个参数比较时会以字符的ASCII值进行比较//如果str1的ASCII码值小于str2返回一个负数反之返回一个正数//如果str1的ASCII码值等于str2返回 0此时if判断语句成立CommandValue LED1ON; //设置的LED1ON的宏为1也就是在串口输入1if判断语句成立else if(strcmp((char*)str,LED1OFF)0)CommandValue LED1OFF; //在串口输入2if判断语句成立else if(strcmp((char*)str,BEEPON)0)CommandValue BEEPON; //在串口输入3if判断语句成立else if(strcmp((char*)str,BEEPOFF)0)CommandValue BEEPOFF; //在串口输入4if判断语句成立return CommandValue;
}int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //设置系统中断优先级delay_init(168);uart_init(115200);LED_Init();KEY_Init();BEEP_Init();LCD_Init();my_mem_init(SRAMIN); //初始化内部内存池POINT_COLORRED;LCD_ShowString(10,10,200,16,16,ATK STM32F407);LCD_ShowString(10,30,200,16,16,FreeRTOS Example);LCD_ShowString(10,50,200,16,16,Binary Semaphore);LCD_ShowString(10,70,200,16,16,Command Data:);//创建开始任务xTaskCreate((TaskFunction_t )start_task, //任务函数(const char* )start_task, //任务名称(uint16_t )START_STK_SIZE, //任务堆栈大小(void* )NULL, //传递给任务函数的参数(UBaseType_t )START_TASK_PRIO, //任务优先级(TaskHandle_t* )StartTask_Handler); //任务句柄 vTaskStartScheduler(); //开启任务调度
}//开始任务任务函数
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); //进入临界区//创建二值信号量,也就是创建一个长度为1的队列BinarySemaphore xSemaphoreCreateBinary(); //xSemaphoreCreateBinary函数为动态创建二值信号量函数//返回 NULL二值信号量创建失败返回其他值表示创建成功的二值信号量的句柄//所以BinarySemaphore表示创建成功的二值信号量的句柄//创建Task1任务xTaskCreate((TaskFunction_t )task1_task, //任务函数(const char* )task1_task, //任务名称(uint16_t )TASK1_STK_SIZE, //任务堆栈大小(void* )NULL, //传递给任务函数的参数(UBaseType_t )TASK1_TASK_PRIO, //任务优先级(TaskHandle_t* )Task1Task_Handler); //任务句柄 //创建Task2任务xTaskCreate((TaskFunction_t )DataProcess_task, //任务函数(const char* )DataProcess_task, //任务名称(uint16_t )DATAPROCESS_STK_SIZE, //任务堆栈大小(void* )NULL, //传递给任务函数的参数(UBaseType_t )DATAPROCESS_TASK_PRIO, //任务优先级(TaskHandle_t* )DataProcess_Handler); //任务句柄 vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL(); //退出临界区
}//Task1任务
//控制 LED0 闪烁提示系统正在运行
void task1_task(void *pvParameters)
{while(1){LED0!LED0;vTaskDelay(500); //延时500ms也就是500个时钟节拍}
}//DataProcess_task函数
//指令处理任务根据接收到的指令来控制不同的外设
void DataProcess_task(void *pvParameters)
{u8 len0;u8 CommandValueCOMMANDERR;BaseType_t errpdFALSE;u8 *CommandStr;POINT_COLORBLUE;while(1){if(BinarySemaphore!NULL) //二值信号量不为空表明接收到的数据是有效的{errxSemaphoreTake(BinarySemaphore,portMAX_DELAY); //获取信号量函数返回值pdTURE获取信号量成功pdFALSE获取信号量失败//第一个参数要获取的信号量句柄//第二个参数阻塞时间这里设置为portMAX_DEALY译为无限等待直至获得信号量if(errpdTRUE) //获取信号量成功{lenUSART_RX_STA0x3fff; //得到此次接收到的数据长度//接收状态//bit15 接收完成标志//bit14 接收到0x0d//bit13~0 接收到的有效字节数目CommandStrmymalloc(SRAMIN,len1); //申请内存 指针指向申请内存的首地址sprintf((char*)CommandStr,%s,USART_RX_BUF); //打印接收缓存区把接收缓存区的数据保存到CommandStr中CommandStr[len]\0; //加上字符串结尾符号//CommandStr 是个指针长度为len数组是从下角标 0 开始的所以len就表示数组的最后一个LowerToCap(CommandStr,len); //将字符串转换成大写CommandValueCommandProcess(CommandStr); //命令解析也就是获取上面定义的宏 1 2 3 4if(CommandValue!COMMANDERR)//if判断语句成立表示CommandValue不等于0xFF那也就是 LED1ON、LED1OFF、BEEPON、BEEPOFF 其中一个指令{LCD_Fill(10,90,210,110,WHITE); //清除显示区域LCD_ShowString(10,90,200,16,16,CommandStr); //在LCD上显示命令printf(命令为%s\r\n,CommandStr); switch(CommandValue){case LED1ON:LED10;break;case LED1OFF:LED11;break;case BEEPON:BEEP1;break;case BEEPOFF:BEEP0;break;}}else{//当命令错误的时候开发板会向串口调试助手发送命令错误的提示信息//比如我们发送 LED1_off 这个命令串口助手会显示无效的命令请重新输入!!printf(无效的命令请重新输入!!\r\n);}USART_RX_STA 0;memset(USART_RX_BUF,0,USART_REC_LEN); //串口接收缓冲区清零myfree(SRAMIN,CommandStr); //释放内存}}else if(errpdFALSE){vTaskDelay(10); //延时10ms也就是10个时钟节拍}}
}