【RP2物联网实战(一)】C/C++&FreeRTOS版

2023-05-16

写在前面

  树莓派pico,又称为RP2,其开发方式多种多样,有C语言和micropython语言,工具有cmake,vscode,arduino,thonny等,本版主要使用的工具链是VsCode结合cmake、官方提供的SDK——pico-sdk和移植的FreeRTOS。环境搭建见这四章:交叉编译踩坑指北
  现在默认你已经完成了环境的搭建,让我们来概括性的认识一下我们所搭建的工具链。

1、PICO-SDK

  首先是树莓派pico官方提供的pico-sdk。参考资料为github pico-example和pico-sdk的C开发手册。pico-sdk是树莓派官方给RP2提供的一个API,主要包含了GPIO,ADC,TIMER,INTERRUPT等函数封装,并且提供了丰富的例子,甚至包含了诸如DHT11,7段数码管的简单驱动。
在这里插入图片描述

1.1 PICO-SDK的CMake组织方式

在这里插入图片描述

  如pico-sdk文档中的话所示, 其官方文件夹是用Cmake组织链接起来的。那么,pico-sdk是如何被使用的呢?我们可以在CMakeLists中一窥究竟。
在这里插入图片描述

  在最外层的CMake文件中,我们可以看到它使用include指令,将pico_sdk_import.cmake文件加入进来(该文件是pico-sdk官方提供的)。这是cmake链接的一种方式,接下来就可以具体看到它是如何把我们的项目工程和pico-sdk链接起来的。
在这里插入图片描述

  可以看到,在这里有一个变量PICO_SDK_PATH,指定的就是你存放官方提供的pico-sdk的位置。你在编译时指定或者在CMakeLists中指定该变量的值就可以了。这样就可以让CMake在顶层的CMakeLists的指引下找到pico-sdk这个文件夹。
在这里插入图片描述
  而这个文件夹也是官方直接提供的,可以看到这个文件夹也有其顶层的CMakeLists,CMakeLists就像一个指引器,指引CMake进入到各种子文件夹中,而子文件夹中也有其各自的CMakeLists,向一棵树一样发散开去。但是因为这个pico-sdk文件夹是官方提供的,这个文件夹内部是怎么编写和放置CMakeLists来指引CMake的我们不关心,已经被官方的人做好了。我们要做的就是把我们工程的CMake指向这个文件夹就好了,之后的工作已经被封装好了。

1.2 PICO-SDK的使用方式

  那么在代码中我们该如何调用pico-sdk呢?首先我们需要知道pico-sdk提供了许多库。包括了高层的库和底层的库,这些都在pico-sdk官方文档中说的很详细。当你需要使用哪个库时,只需要查询官方文档,将其名字以库的形式添加在指导main.c文件编译的CMakeLists中即可。具体而言如下所示,比方说要使用adc功能,在官方文档中查找到库名字的声明,这是第四节,在hardware分类下。
在这里插入图片描述

  所以我们在直接编译main.c文件的CMakeLists中找到链接库的语句,加入hardware_adc即可
在这里插入图片描述
  在main.c等文件中应用时,遵循其分类,因为再硬件库分类下,使用hardware/adc.h来表示,这一块可以参考文档中给出的示例就可以,如下所示。
在这里插入图片描述
  因为pico-sdk提供的库已经内部链接了头文件,因此我们无需在CMake中声明include路径,直接在main.c文件里使用就行了。(下一节FreeRTOS编译的过程中将会看到这种为库内部链接头文件是怎么用CMake实现的)。
此外还有一个常用的主库pico/stdlib,这个库包含了许多常用子库如时间库,gpio库等等。
在这里插入图片描述

1.3 PICO-SDK常见函数的简单介绍

  因为文档比较长,秉承着用到哪学哪的理念,我使用较多的为gpio、time还有adc功能。

函数功能
gpio_init(PIN)初始化管脚
gpio_set_dir(PIN,GPIO_OUT/GPIO_IN)设置gpio方向
gpio_put(PIN,1/0)输出高/低电平
x=gpio_get(PIN)获取引脚电平
adc_init()初始化ADC
adc_set_temp_sensor_enabled(true)开启内置温度计
adc_select_input(channel)选择ADC通道,4通道是温度计
gpio_set_mask(mask)gpio数据掩码赋值
gpio_clr_mask(mask)gpio清除掩码

2、 FreeRTOS

  前面介绍了Pico-SDK,实际上它就相当于一个库的集合,一个包,被链接到我们的工程文件中来。这里的FreeRTOS其实也是一样的,也是通过CMake链接进来。只不过我们下载的是FreeRTOS的源代码,需要先用CMake把它转换成库,然后链接进来。

2.1 FreeRTOS的CMake组织方式

  如前所述,既然pico-sdk已经是CMake所组织连接的,因此我们在已知FreeRTOS时也要使用CMake。
  首先去FreeRTOS官网下载V9.0.0版本,虽然不是up-to-dated,但是是比较主流的。我们主要用到的文件在FreeRTOSv9.0.0\FreeRTOS\Source文件夹下,当然,官方也给出了很多移植的demo,在FreeRTOSv9.0.0\FreeRTOS\Demo下有许多芯片型号的移植实例。
  我们先新建一个文件夹freertos,其中包括了FreeRTOSKernel,CMakeLists和FreeRTOSConfig.h这几个文件,如下图所示。
在这里插入图片描述

  其中,FreeRTOS-Kernel文件夹中主要存放的是之前所说的FreeRTOS Source文件夹提供的源文件。
在这里插入图片描述
  我们可以在此基础上对不需要的功能进行剪裁,或者可以在FreeRTOS Plus文件中加入额外的功能比如UDP通讯等等。总之就是根据需要对源代码进行相应的取舍。但是在这里不删减也没关系,因为后面讲到的配置文件会利用条件编译的方法来决定是否加入某种功能。
  CMakeLists的内容如下,
在这里插入图片描述

#提取文件夹中的源文件生成静态库freertos.a在build/freertos下
#将头文件添加到目标头文件路径中,可能是为了辅助生成freertos库
set(PICO_SDK_FREERTOS_SOURCE FreeRTOS-Kernel)#添加文件夹位置变量

add_library(freertos#生成库
    ${PICO_SDK_FREERTOS_SOURCE}/event_groups.c#事件功能
    ${PICO_SDK_FREERTOS_SOURCE}/list.c
    ${PICO_SDK_FREERTOS_SOURCE}/queue.c#队列功能(队列消息,信号量,互斥量)
    ${PICO_SDK_FREERTOS_SOURCE}/stream_buffer.c
    ${PICO_SDK_FREERTOS_SOURCE}/tasks.c#任务功能
    ${PICO_SDK_FREERTOS_SOURCE}/timers.c#定时器功能
    ${PICO_SDK_FREERTOS_SOURCE}/portable/MemMang/heap_3.c#内存管理模块
    ${PICO_SDK_FREERTOS_SOURCE}/portable/GCC/ARM_CM0/port.c
)

target_include_directories(freertos PUBLIC#为库连接头文件
    .
    ${PICO_SDK_FREERTOS_SOURCE}/include
    ${PICO_SDK_FREERTOS_SOURCE}/portable/GCC/ARM_CM0
)

  add_library()是CMake中生成库的指令.默认是静态库(.a),生成库 add_library(libname [SHARED| STATIC] source1 …sourceN) 指令用于将一系列源文件指定身成为静态库或者动态库。例如本例中为静态库,就会在指定的libname前面添加lib作为生成库文件的名字,后缀为.a
在这里插入图片描述
  将头文件位置加入了freertos的include搜索路径,使用指令**target_include_directories**,cmake引入头文件有2条指令分别是 include_directories()target_include_directories() 区别是,前者是当前CMakeLists.txt中所有目标的搜索路径及其之后子目录目标的搜索路径后者是指定目标所包含的头文件路径(一般是先生成文件,才能指定,因此是出现在后面的)后者的好处是,具体化,对于不同文件夹下相同名称的文件也具有区分性

  target_include_directories( [SYSTEM] [AFTER|BEFORE] <INTERFACE|PUBLIC|PRIVATE> [items1…])
  其中指定的参数代表含义如下
  INTERFACE:target对应的对头文件使用
  PRIVATE:target对应的源文件使用
  PUBLIC:target对应的头文件/源文件都可以用

  以源文件构建库文件之一的源文件list.c为例,其中就有include
在这里插入图片描述
  因此使用target_include_directories可以为库中的每个文件引入头文件的路径进行具体化,使其可以找到特定的头文件而不会出错。

  FreeRTOSConig.h头文件在FreeRTOS.h中被引用,如下图所示
在这里插入图片描述

  当我们在主程序中调用FreeRTOS核心库FreeRTOS.h时,会自动调用配置文件FreeRTOSConfig.h文件。下面简单讲一下该文件中的一些常用功能与配置。
在这里插入图片描述
  其实在这本书中除了对FreeRTOS有详细讲解外,也穿插着对其配置文件有阐述。
  例如
在这里插入图片描述

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* Use Pico SDK ISR handlers */
#define vPortSVCHandler         isr_svcall
#define xPortPendSVHandler      isr_pendsv
#define xPortSysTickHandler     isr_systick

#define configUSE_PREEMPTION                    1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0//使能它时任务优先级可以支持32级
#define configUSE_TICKLESS_IDLE                 0//低功耗使能,置0系统节拍中断一直运行
#define configCPU_CLOCK_HZ                      133000000//设置CPU频率133M,用于在port.c文件中进行时钟配置
#define configTICK_RATE_HZ                      1000//设置FREERTOS系统时钟节拍频率HZ,系统滴答定时器的中断频率,设为1000则系统时钟节拍周期1ms,软件定时器的值必须是该周期的整数倍;pdMS_TO_TICKS()可以将毫秒转换为tick,前提是该宏小于1000
#define configMAX_PRIORITIES                    5//设置任务优先级数量从0-该数-1,就绪列表数组大小,最大支持255,数值越大优先级越高
#define configMINIMAL_STACK_SIZE                128//设置空闲任务的最小任务堆栈大小(字)
#define configMAX_TASK_NAME_LEN                 16//设置任务名最大长度
#define configUSE_16_BIT_TICKS                  0//1:8位存储事件组  0:24位存储事件组,*推荐:0
#define configIDLE_SHOULD_YIELD                 1//空闲任务与处于同等优先级的其他用户任务的行为.为1时空闲任务会为同等优先级的用户任务让出cpu使用权
#define configUSE_TASK_NOTIFICATIONS            1
#define configTASK_NOTIFICATION_ARRAY_ENTRIES   3
#define configUSE_MUTEXES                       1//使用互斥量
#define configUSE_RECURSIVE_MUTEXES             1//使用递归互斥量
#define configUSE_COUNTING_SEMAPHORES           1//使用计数信号量
#define configQUEUE_REGISTRY_SIZE               10//设置可以注册的信号量和消息队列个数
#define configUSE_QUEUE_SETS                    0//启用队列集
#define configUSE_TIME_SLICING                  1//使能时,处于就绪态的多个相同优先级任务会以时间分片方式共享CPU
#define configUSE_NEWLIB_REENTRANT              0
#define configENABLE_BACKWARD_COMPATIBILITY     0//支持RTOS8.0之前的数据类型
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
#define configSTACK_DEPTH_TYPE                  uint16_t
#define configMESSAGE_BUFFER_LENGTH_TYPE        size_t

/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION         0//SRAM静态分配任务栈和任务控制块的内存,需要用户自定义两个函数设定空任务和定时器任务的堆栈大小,建议使用动态
#define configSUPPORT_DYNAMIC_ALLOCATION        1//SRAM动态分配任务栈和任务控制块的内存,分配到堆。堆在heap_x.c文件中定义.支持动态分配任务、队列、信号等
#define configAPPLICATION_ALLOCATED_HEAP        1//配置为用户自行设置堆内存而不是编译器自动分配,设置configTOTAL_HEAP_SIZE
#define configTOTAL_HEAP_SIZE                   ((size_t)(36*1024))//定义堆内存大小
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK                     0
#define configUSE_TICK_HOOK                     0
#define configCHECK_FOR_STACK_OVERFLOW          0//检测任务栈溢出
#define configUSE_MALLOC_FAILED_HOOK            0
#define configUSE_DAEMON_TASK_STARTUP_HOOK      0

/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS           0//开启时间统计功能,需要再定义portCONFIGURE_TIMER_FOR_RUN_TIME_STATUS()等宏
#define configUSE_TRACE_FACILITY                0
#define configUSE_STATS_FORMATTING_FUNCTIONS    0

/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES                   0//为1时可以启动协程
#define configMAX_CO_ROUTINE_PRIORITIES         1//协程的最大优先级数,协程优先级可以从0到该值-1

/* Software timer related definitions. */
#define configUSE_TIMERS                        1//使能了定时器,会自动创建定时器任务,ISR中设置事件需要,软件定时器需要
#define configTIMER_TASK_PRIORITY               4//软件定时器 任务 优先级,应当设置为最高(原来是3),守护进程的优先级
#define configTIMER_QUEUE_LENGTH                10//软件定时器命令队列长度,定时器通信不能直接访问,被API封装
#define configTIMER_TASK_STACK_DEPTH            configMINIMAL_STACK_SIZE//定时器任务堆栈大小128

/* Define to trap errors during development. */
#define configASSERT( x )//定义断言函数,具体内容需要用户自定义

/* Optional functions - most linkers will remove unused functions anyway. */
//使能或这解除使能对应的API函数
#define INCLUDE_vTaskPrioritySet                1
#define INCLUDE_uxTaskPriorityGet               1
#define INCLUDE_vTaskDelete                     1//启用任务删除函数
#define INCLUDE_vTaskSuspend                    1//启用任务挂起功能函数和任务恢复函数vTaskSuspend()和vTaskResume,会使得信号和消息的阻塞等待时间可以为无穷大portMAX_Delay
#define INCLUDE_xResumeFromISR                  1
#define INCLUDE_vTaskDelayUntil                 1//启用绝对阻塞延时函数
#define INCLUDE_vTaskDelay                      1//启用相对阻塞延时函数
#define INCLUDE_xTaskGetSchedulerState          1
#define INCLUDE_xTaskGetCurrentTaskHandle       1
#define INCLUDE_uxTaskGetStackHighWaterMark     0
#define INCLUDE_xTaskGetIdleTaskHandle          0
#define INCLUDE_eTaskGetState                   0
#define INCLUDE_xEventGroupSetBitFromISR        1
#define INCLUDE_xTimerPendFunctionCall          1//原来是0;支持xEventGroupSetBitsFromISR()事件组中断置位函数
#define INCLUDE_xTaskAbortDelay                 0
#define INCLUDE_xTaskGetHandle                  0
#define INCLUDE_xTaskResumeFromISR              1//在中断中恢复挂起任务

/* A header file that defines trace macro can be included here. */

#endif /* FREERTOS_CONFIG_H */

3、FreeRTOS功能介绍(部分)

3.1任务管理Task

  从系统的角度看,任务是竞争系统资源的最小运行单元。FreeRTOS 是一个支持多任务的操作系统。在FreeRTOS 中,任务可以使用或等待CPU、使用内存空间等系统资源,并独立于其它任务运行, 任何数量的任务可以共享同一个优先级, 如果宏configUSE_TIME_SLICING 定义为1,处于就绪态的多个相同优先级任务将会以时间片切换的方式共享处理器。
  FreeRTOS 的任务可认为是一系列独立任务的集合。每个任务在自己的环境中运行。在任何时刻,只有一个任务得到运行,FreeRTOS 调度器决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。作为任务,不需要对调度器的活动有所了解,在任务切入切出时保存上下文环境(寄存器值、堆栈内容)是调度器主要的职责
  FreeRTOS 中的任务是抢占式调度机制,高优先级的任务可打断低优先级任务,低优先级任务必须在高优先级任务阻塞或结束后才能得到调度。,当有比当前任务优先级更高的任务就绪时,当前任务将立刻被换出,高优先级任务抢占处理器运行。同时FreeRTOS 也支持时间片轮转调度方式。
  FreeRTOS的任务状态迁移如下图所示

在这里插入图片描述
  挂起和解除挂起任务的函数分别是vTaskSuspend()和vTaskResume(),挂起态任务对任务调度器是不可见的,而阻塞情况比较多,例如vTaskDelay()延时或者等待信号量、资源占用结束、事件发生等。
  xTaskCreate用于创建任务,参数指明任务句柄、优先级、任务名等信息。
  vTaskDelete同于删除任务,vTaskStartScheduler()打开任务调度器,FreeRTOS正式接管。
  空闲任务(idle 任务) 是FreeRTOS 系统中没有其他工作进行时自动进入的系统任务。因为处理器总是需要代码来执行——所以至少要有一个任务处于运行态。FreeRTOS 为了保证这一点,当调用 vTaskStartScheduler()时,调度器会自动创建一个空闲任务,空闲任务是一个非常短小的循环。用户可以通过空闲任务钩子方式,在空闲任务上钩入自己的功能函数。通常这个空闲任务钩子能够完成一些额外的特殊功能,例如系统运行状态的指示,系统省电模式等
  

3.2消息队列Queue

在这里插入图片描述

  消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息,当队列中的消息是空时,读取消息的任务将被阻塞,用户还可以指定阻塞的任务时间xTicksToWait,在这段时间中,如果队列为空,该任务将保持阻塞状态以等待队列数据有效。当队列中有新消息时,被阻塞的任务会被唤醒并处理新消息。消息队列是一种异步的通信方式。(可能类似python多进程中的管道)
  一个任务可以向任何一个队列发送消息,也可以从任何一个队列接收消息。默认是按照FIFO的方式发送和读取,也可以配置为LIFO。
xQueueCreate():消息队列创建函数
vQueueDelete():消息队列删除函数
xQueueSend():向消息队列发送函数
xQueueSendToFront():向消息队列发送紧急消息函数
xQueueReceive():从消息队列读取消息函数
xQueuePeek():从消息队列读取消息函数(不删除消息)

3.3 信号量Semaphore

  基于队列,只不过不关心消息内容,只关心消息的个数。有二值、计数、互斥信号量等。
xSemaphoreCreateBinary()创建二值信号
xSemaphoreCreateCounting()创建计数信号
vSemaphoreDelete()信号量删除函数
xSemaphoreTake() 信号量获取
xSemaphoreGive() 信号量释放

3.4互斥量Mutex

  特殊的信号量,最主要的区别是锁的概念。一个信号只能在同一个任务中进行申请和释放。而普通信号量可以在一个任务中申请而在另一个任务中进行释放。
xSemaphoreCreateMutex()互斥信号量创建
vSemaphoreDelete()(互斥)信号量删除
xSemaphoreTake() (互斥)信号量获取
xSemaphoreGive() (互斥)信号量释放

3.5事件Event

在这里插入图片描述

  事件是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。即一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。
  如果宏configUSE_16_BIT_TICKS 定义为1,那么变量uxEventBits 就是16 位的,其中有8 个位用来存储事件组;而如果宏configUSE_16_BIT_TICKS 定义为0,那么变量uxEventBits 就是32 位的, 其中有24 个位用来存储事件组。
xEventGroupCreate()事件创建函数
vEventGroupDelete()事件删除函数
xEventGroupSetBits()事件组置位函数
xEventGroupWaitBits()等待事件函数
xEventGroupClearBits()事件组清零位函数

3.6软件定时器timer

在这里插入图片描述

  定时器有硬件定时器和软件定时器之分:
  硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
  软件定时器,软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,它实现的功能与硬件定时器也是类似的。使用硬件定时器时,每次在定时时间到达之后就会自动触发一个中断,用户在中断中处理信息;而使用软件定时器时,需要我们在创建软件定时器时指定时间到达后要调用的函数(也称超时函数/回调函数,为了统一,下文均用回调函数描述),在回调函数中处理信息。
  单次模式:当用户创建了定时器并启动了定时器后,定时时间到了,只执行一次回调函数之后就将该定时器进入休眠状态,不再重新执行。
  周期模式:这个定时器会按照设置的定时时间循环执行回调函数,直到用户将定时器删除
  通常软件定时器以系统节拍周期为计时单位。系统节拍是系统的心跳节拍,表示系统时钟的频率,就类似人的心跳,1s 能跳动多少下,系统节拍配置为configTICK_RATE_HZ,该宏在FreeRTOSConfig.h 中有定义,默认是1000。那么系统的时钟节拍周期就为1ms(1s 跳动1000 下,每一下就为1ms)。软件定时器的所定时数值必须是这个节拍周期的整数倍,
xTimerCreate()软件定时器创建函数
xTimerStart()软件定时器启动函数
xTimerStop()软件定时器停止函数
xTimerDelete()软件定时器删除函数

任务通知,内存管理,中断:暂时未看

4、实例测温展示

  硬件RP2-H,DS18B20,4共阴数码管


main.c

//VERSION 1.0.1 TIME 
//加入DS18B20传感器测试模块
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <queue.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
#include "DS18B20.h"
#define FIRST_GPIO 2
#define QUEUE_LEN 4//队列的长度,最大包含多少消息
#define QUEUE_SIZE 4//队列中每个消息的字节
int Temperature_temp=0;
int bits[10] = {//共阴极7段数码管
        0x3f,  // 0
        0x06,  // 1
        0x5b,  // 2
        0x4f,  // 3
        0x66,  // 4
        0x6d,  // 5
        0x7d,  // 6
        0x07,  // 7
        0x7f,  // 8
        0x6f   // 9
};
QueueHandle_t Test_Queue1=NULL;
void task1(void *pvParameters){
    while(true){
        const float conversion_factor=3.3f/(1<<12);
        float adc=(float)adc_read()*conversion_factor;
        float Temperature=27 - (adc - 0.706)/0.001721;
        int Temperature_temp=(int)(Temperature*100);

        printf("发送队列消息Temperature\n");
        xQueueSend(Test_Queue1,&Temperature_temp,0);

        printf("voltage: %f V\nOn board Temperature: %f Degree\n",adc,Temperature);   
        // We are starting with GPIO 2, our bitmap starts at bit 0 so shift to start at 2.
        vTaskDelay(2000);
    }
}
void task2(void *pvParameters){
    while(true){
        int a,b,c,d;
        xQueueReceive(Test_Queue1,&Temperature_temp,1);
        a=Temperature_temp/1000;
        b=Temperature_temp/100-a*10;
        c=Temperature_temp/10-a*100-b*10;
        d=Temperature_temp-a*1000-b*100-c*10;
        int32_t mask = bits[a] << FIRST_GPIO;//掩码
        // Set all our GPIOs in one go!
        // If something else is using GPIO, we might want to use gpio_put_masked()
        gpio_set_mask(mask);
        gpio_put(9,0);
        gpio_put(10,0);
        gpio_put(11,1);
        gpio_put(12,1);
        gpio_put(13,1);
        vTaskDelay(5);
        gpio_clr_mask(mask);  //清除上一次的值
        
        mask = bits[b] << FIRST_GPIO;//掩码
        gpio_set_mask(mask);
        gpio_put(9,1);
        gpio_put(10,1);
        gpio_put(11,0);
        gpio_put(12,1);
        gpio_put(13,1);
        vTaskDelay(5);
        gpio_clr_mask(mask);  //清除上一次的值 
        
        mask = bits[c] << FIRST_GPIO;//掩码
        gpio_set_mask(mask);
        gpio_put(9,0);
        gpio_put(10,1);
        gpio_put(11,1);
        gpio_put(12,0);
        gpio_put(13,1);
        vTaskDelay(5);
        gpio_clr_mask(mask);  //清除上一次的值     

        mask = bits[d] << FIRST_GPIO;//掩码
        gpio_set_mask(mask);
        gpio_put(9,0);
        gpio_put(10,1);
        gpio_put(11,1);
        gpio_put(12,1);
        gpio_put(13,0);
        vTaskDelay(5);
        gpio_clr_mask(mask);  //清除上一次的值 
    }

}
void task3(void *pvParameters){//DS18B20传感器测温任务
  while(true){
        ReadTemperature();
        vTaskDelay(2000);

  }
}
int main(){

    stdio_init_all();
    gpio_init(PICO_DEFAULT_LED_PIN);//初始化板载LED,表征进入主程序
    gpio_set_dir(PICO_DEFAULT_LED_PIN,GPIO_OUT);
    gpio_put(PICO_DEFAULT_LED_PIN,1);
    gpio_init(DQ_PIN);//初始化外设温度器数据引脚GPIO22
    gpio_set_dir(DQ_PIN,GPIO_OUT);

    //初始化8段数码管GPIO引脚
    for (int gpio = FIRST_GPIO; gpio < FIRST_GPIO + 12; gpio++) {
        gpio_init(gpio);
        gpio_set_dir(gpio, GPIO_OUT);
        // Our bitmap above has a bit set where we need an LED on, BUT, we are pulling low to light
        // so invert our output
        //gpio_set_outover(gpio, 1);
    }
     
    printf("ADC Meausring GPIO026\n");
    adc_init();
    adc_set_temp_sensor_enabled(true);
    adc_select_input(4);//temperature
    xTaskCreate(task1, "ADC_Task", 256, NULL, 1, NULL); 
    xTaskCreate(task2, "SEG_Task", 256, NULL, 1, NULL);
    xTaskCreate(task3,"DS18B20_Task",256,NULL,1,NULL);
    Test_Queue1=xQueueCreate(QUEUE_LEN,QUEUE_SIZE);  
    vTaskStartScheduler();

    while(true){};
}


DS18B20.c

#include "DS18B20.h"
#include "pico/stdlib.h"
#include <stdio.h>
/*****初始化DS18B20*****/
unsigned int Init_DS18B20(void)
{
    unsigned int x=0;
    // gpio_put(DQ_PIN,1);//DQ复位
    // sleep_us(100);    //稍做延时
    gpio_set_dir(DQ_PIN, GPIO_OUT);
    gpio_put(DQ_PIN,0);//单片机将DQ拉低
    sleep_us(500);   //精确延时,大于480us
    gpio_put(DQ_PIN,1);//拉高总线15us
    sleep_us(15);
    gpio_set_dir(DQ_PIN,GPIO_IN);  //释放总线
    sleep_us(45);
    x =gpio_get(DQ_PIN);//稍做延时后,如果x=0则初始化成功,x=1则初始化失败
    if(x==0){
        // printf("初始化成功\n");
    }
    else{
        // printf("初始化失败\n");
    }
    return x;
}
/*****写一个字节*****/
void WriteOneChar(unsigned char dat)//先写低字节
{
    unsigned char i=0;
    for (i=8; i>0; i--)
    {
        gpio_set_dir(DQ_PIN,GPIO_OUT); 
        gpio_put(DQ_PIN,0);//单片机将DQ拉低
        sleep_us(1);
        gpio_put(DQ_PIN,dat&0x01);//与1按位与运算,dat最低位为1时DQ总线为1,dat最低位为0时DQ总线为0
        dat>>=1;
        sleep_us(70);//采样时间
        gpio_put(DQ_PIN,1);
        gpio_set_dir(DQ_PIN,GPIO_IN);//释放总线  
        sleep_us(10);         
    }
}

/*****读一个字节*****/
unsigned char ReadOneChar(void)
{
  unsigned char i=0;
  unsigned char dat = 0;
  for (i=8;i>0;i--)
  {
    gpio_set_dir(DQ_PIN,GPIO_OUT); 
    gpio_put(DQ_PIN,0);// 给脉冲信号
    dat>>=1;
    sleep_us(2);
    gpio_put(DQ_PIN,1);// 给脉冲信号
    gpio_set_dir(DQ_PIN,GPIO_IN); //释放总线
    sleep_us(6);
    if(gpio_get(DQ_PIN))
    	dat|=0x80;
    sleep_us(48);
  }
  return(dat);
}

/*****读取温度*****/
int ReadTemperature(void)
{
  	unsigned char a=0;
  	unsigned char b=0;
  	unsigned int t=0;
    float t2=0;
  	Init_DS18B20();
    sleep_ms(1);
  	WriteOneChar(0xCC);  //跳过读序号列号的操作
  	WriteOneChar(0x44);  //启动温度转换
  	Init_DS18B20();
    sleep_ms(1);
  	WriteOneChar(0xCC);  //跳过读序号列号的操作
  	WriteOneChar(0xBE);  //读取温度寄存器
  	a=ReadOneChar();     //读低8位
  	b=ReadOneChar();     //读高8位
  	t=b;
  	t<<=8;
    // printf("t=b并左移8位的值:%d",t);
  	t=t|a;
    // printf("16位数据为%d\n",t);    
	t2=t*0.0625-4.2;//修正
    printf("DS18B20 Temperature: %fDegree\n",t2);
  	return(t);
}



DS18B20.h

#ifndef _DS18B20_H_
#define _DS18B20_H_
#define DQ_PIN 22
unsigned int Init_DS18B20(void);
void WriteOneChar(unsigned char dat);
unsigned char ReadOneChar(void);
int ReadTemperature(void);
#endif

CMakeLists.txt

add_executable(blink
        main.c DS18B20.c
)

target_link_libraries(blink pico_stdlib freertos hardware_adc)

target_include_directories(blink PUBLIC .)#增加的

pico_enable_stdio_usb(blink 1)#add ,parameter1 is the program
pico_enable_stdio_uart(blink 0)#add ,disable uart

pico_add_extra_outputs(blink)

最终效果如下

在这里插入图片描述  

  

  
  Linux开发环境下工具链同样跑通

在这里插入图片描述

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

【RP2物联网实战(一)】C/C++&FreeRTOS版 的相关文章

  • git am PATCH 失败的处理方法

    参考 xff1a http www cnblogs com domainfei articles 2433504 html http blog sina com cn s blog 5372b1a301015y0n html 英文原文地址
  • AAC帧格式及编码介绍

    参考资料 xff1a AAC以adts格式封装的分析 xff1a http wenku baidu com view 45c755fd910ef12d2af9e74c html aac编码介绍 xff1a http wenku baidu
  • 基于ubuntu14.04的Mobilenet_SSD环境搭建

    Data 2017 11 22 Author cjh Theme 基于ubuntu14 04的Mobilenet SSD环境搭建 Caffe for SSD xff1a https github com weiliu89 caffe tre
  • [open vSwitch]查看OVS端口ofport编号及对应虚拟机MAC

    在用open vSwitch做实验时 xff0c 我们经常需要知道OVS port对应的ofport编号 xff0c 这个比较容易 xff0c 用 ovs ofctl show bridge 就能得到 如 root 64 vaio ovs
  • 4、基于51单片机智能语音识别小车控制 语音口令说话控制系统设计

    毕设帮助 开题指导 技术解答 xff08 有偿 xff09 见文末 目录 摘要 一 硬件方案 二 设计功能 三 实物图 四 原理图 五 PCB图 六 程序源码 七 资料包括 摘要 随着电子工业的发展 xff0c 具有语音控制功能的小车越来越
  • H264中的SPS、PPS提取与作用

    牛逼的视频会议网站 xff1a http wmnmtm blog 163 com blog m 61 0 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 43 4
  • Linux系统备份与还原

    在 使用Ubuntu之前 xff0c 相信很多人都有过使用Windows系统的经历 如果你备份过Windows系统 xff0c 那么你一定记忆犹新 xff1a 首先需要找到一个备份工 具 通常都是私有软件 xff0c 然后重启电脑进入备份工
  • ip能ping开但是不能远程登陆

    刚刚安装ftp时玩将防火墙开启了 然后退出ssh再登陆时就登陆不上去了 出现 xff1a ip能ping开但是不能远程登陆 xff1b telnet ip 也不通 以为使系统问题 xff0c 但是想想刚刚的操作还是防火墙 解决方法 xff1
  • 如何检索CPCI-S

    1 打开Web of Science www webofknowledge com 2 选择数据库大类 xff1a Web of Science核心合集 xff1b 3 会议论文 xff0c 需要选择web of science数据库中的一
  • ZeroMQ消息传输协议 (v2.0)

    因为项目上在和其他团队联调时需要抓包分析消息正确性的问题 xff0c 因此在网络上查找了一下关于ZeroMQ的协议资料 找到如下文章 原文 另外这里有一篇对ZeroMQ实现讲的比较深的文章 xff0c 有兴趣也可以看看 xff1a 全网仅此
  • mysql左右匹配原则的用法和理解

    重点 xff1a mysql的最左匹配原则其实是和where后面的查询条件顺序是没有关系的只和索引的字段顺序有关 xff1b xff08 这里说的顺序是联合索引的顺序 xff09 这点网上很多地方都说错了 下面我们来用代码模拟一下问题 这里
  • 如何做好项目经理

    我一直赞同这个观点 xff1a 项目经理是干出来的 xff0c 不是学出来的 xff1b 是带出来的 xff0c 不是教出来的 一个人要成长为一名合格的项目经理主要不是靠学 xff0c 而是靠干 xff0c 当然学也很重要 靠干 xff0c
  • Javascript进制转换

    介绍一个简单的用Javascript进行 进制 转换的方式 xff1a 将十进制转换为十六进制 xff1a var i 61 10 alert parseInt 10 10 toString 16 同样 xff0c 将十六进制转换为十进制
  • js实现进制变换 10->16

    进制变换 10 gt 16 function heTransform data var pattern 61 new RegExp 39 1 9 d 0 39 判断是否是10进制数字 if pattern test data var hex
  • 用Visual C#实现局域网点对点通讯

    用Visual C xff03 实现局域网点对点通讯 作者 xff1a 马金虎 日期 xff1a 2003 9 28 出处 xff1a P2P中国 PPcn net 点对点即Peer To Peer xff0c 通常简写为P2P 所谓网络中
  • Makefile学习总结

    Data 2017 12 07 Author cjh Theme Makefile Tutorial 俗话说 xff0c 工欲善其事必先利其器 xff0c 所以我们先来介绍一下Makefile中的特殊字符 64 表示目标文件 表示所有的依赖
  • stmdb和ldmia

    stmdb xff1a db xff08 decrease before xff09 表示先减后存 指令 stmdb sp fp ip lr pc 34 表示sp等于最终被修改的sp的值 假设 sp 61 4096 xff0c 此条指令的执
  • ros之tf经验总结

    1 概念 搞ros都离不开tf xff0c 当建立一个机器人模型时 xff0c 第一步就是要确定机器人的tf结构 以kobuki导航运行为例 xff0c 首先是 map xff1a 地图坐标 xff0c 固定坐标系 odom xff1a 机
  • setInterval和setTimeout的缺陷和优势分析

    先把问题摆出来 xff1a 使用定时器的setInterval xff08 xff09 方法会出现程序并不是按照我们设定的精确时间而调用的问题 xff01 定时器 xff1a 在JavaScript中经常会使用定时器来进行延时或者是重复调用
  • 关于KEIL调试时CortexJLink中SW Device检测不到芯片解决办法

    使用Jlink第一次下载成功后 xff0c 第二次检测不到设备 xff0c 极大原因是因为软件配置了SWDIO和SWCLK的状态导致的 解决办法 xff1a 软件中将配置两个引脚状态程序注释 xff0c 将芯片的BOOT0引脚接高电平 xf

随机推荐

  • Web大规模高并发请求和抢购的解决方案

    电商的秒杀和抢购 xff0c 对我们来说 xff0c 都不是一个陌生的东西 然而 xff0c 从技术的角度来说 xff0c 这对于Web系统是一个巨大的考验 当一个Web系统 xff0c 在一秒钟内收到数以万计甚至更多请求时 xff0c 系
  • Java用自定义的类作为HashMap的key值

    这是Java中很经典的问题 xff0c 在面试中也经常被问起 其实很多书或者文章都提到过要重载hashCode 和equals 两个方法才能实现自定义键在HashMap中的查找 xff0c 但是为什么要这样以及如果不这样做会产生什么后果 x
  • Maven类包冲突终极三大解决技巧 mvn dependency:tree

    举例 A依赖于B及C xff0c 而B又依赖于X Y xff0c 而C依赖于X M xff0c 则A除引B及C的依赖包下 xff0c 还会引入X xff0c Y xff0c M的依赖包 xff08 一般情况下了 xff0c Maven可通过
  • idea中的实现接口时@Override注解问题

    摘要 用IntelliJ 15打开一个以前的工程 xff0c 发现代码出现很多关于 64 Override的错误 xff0c 编辑器提示 xff1a 64 Override is not allowed when implementing
  • mac下idea的使用之java工程打包生成jar篇

    我们先选中工程 xff0c 然后点如下的图标 xff0c 打开配置文件 xff0c 再选中artifacts选项 xff0c 再点加号选jar 起一个名字 xff0c 然后再填写好要打到的路径 xff0c 下面就是我们要选择我们需要的包或着
  • Memory Dependencies-内存依赖

    Data 2017 12 28 Author cjh Theme Memory Dependencies 本篇内容是基于TI TMS320C6000Programmer s Guide手册第2 2 2 Memory Dependencies
  • Python中获取异常(Exception)信息

    异常信息的获取对于程序的调试非常重要 xff0c 可以有助于快速定位有错误程序语句的位置 下面介绍几种python中获取异常信息的方法 xff0c 这里获取异常 xff08 Exception xff09 信息采用try except 程序
  • 聚类、K-Means、例子、细节

    聚类 今天说聚类 xff0c 但是必须要先理解聚类和分类的区别 xff0c 很多业务人员在日常分析时候不是很严谨 xff0c 混为一谈 xff0c 其实二者有本质的区别 分类其实是从特定的数据中挖掘模式 xff0c 作出判断的过程 比如Gm
  • CTR中的特征哈希

    在CTR预估中 xff0c 一种做法是采用人工来做feature engineering xff0c 将一些非线性的feature转换为线性的feature xff0c 然后喂给LR之类的线性model来做在线学习 xff0c 在这个过程中
  • go的一些基本知识

    go 编译 xff0c 依赖 GOPATH 找到src https blog csdn net u012210379 article details 50443636 go logging http www yeolar com note
  • 论文精读-DeepFM

    转载一篇博客文章 https blog csdn net zynash2 article details 79348540 DNN部分的详细推导见 xff1a https blog csdn net zynash2 article deta
  • 《玲音》

    这年头时兴谈互联网思维 xff0c 最近看了二十年前的一部动漫 玲音 xff0c 里面还真是有不少互联网思维 xff0c 以下剧透 xff1a 玲音是一个中学女生 xff0c 性格内向自闭 xff0c 一直过着平淡的生活 xff0c 与家庭
  • idea中安装activity插件actibpm,文章中有下载链接

    我的个人网站 等不见天亮等时光 插件的安装路径有这么几种 从idea setting plugin 选择商店 在商店中直接输入actibpm 可以直接进行安装 由于网站的地址在国外 如果你没得梯子的话 中间肯定会安装失败 所以我建议在安装时
  • unrecognized command line option '-V' 解决方法

    xgcc error unrecognized command line option 39 V 39 xgcc fatal error no input files 真正的错误并不在这里 xff0c 而是在最后一个错误出现的地方 我遇到的
  • ROS教程译文1———TF转换树的设置和使用

    本文属于ROS翻译文章 xff0c 来自WIKI http wiki ros org navigation Tutorials RobotSetup TF 由于水平有限 xff0c 翻译错误在所难免 xff0c 发现错误欢迎评论或联系作者
  • TI Openmp clacc编译器讲解

    Data 2017 12 29 Author cjh Theme TI Openmp clacc编译器讲解 本章内容主要分四讲来讲 xff1a 一 clacc编译器的讲解 二 makefile的讲解 三 openmp target语法讲解
  • 第二讲 STM32相关底层配置(一)

    本文是针对stm32F1系列的一些基础部分的底层配置的流程的总结 主要包括了GPIO普通配置 GPIO复用配置 GPIO重映射配置及中断优先级管理 其中紫色部分为调用正点原子的SYSTEM文件夹里的函数来实现相应步骤 GPIO普通配置 1
  • RCP开发中,对当前字体名字的获得

    Display getCurrent getSystemFont getFontData 0 getName 这句话获得的是当前字体的名字
  • sql server 中OPENROWSET与OPENDATASOURCE的用法

    SELECT FROM OPENROWSET 39 sqloledb 1 39 39 mrp server 39 39 sa 39 39 123 39 DBdata dbo tbG gzzxzl SELECT a FROM OPENROWS
  • 【RP2物联网实战(一)】C/C++&FreeRTOS版

    写在前面 树莓派pico xff0c 又称为RP2 xff0c 其开发方式多种多样 xff0c 有C语言和micropython语言 xff0c 工具有cmake xff0c vscode xff0c arduino xff0c thonn