nRF52840开发板现在可供参考的例子只有SDK中的实例,而且实例也很少,在SDK中有一个温度数据采集的实例,但是采集的是芯片温度;twi也提供了一个通过I2C获取温度传感器数据的例子,但是温度传感器是LM75B,我手头上没有这个传感器,而且这两个例子都是通过while(true)实现一个死循环来不断地采集温度,这样如果需要集成其他功能的话会导致程序无法正常运行下去,所以决定使用app_timer来定时获取DHT11温湿度传感器的数据。
1.定义DHT11.h
#ifndef __DHT11_H__
#define __DHT11_H__
#include <stdint.h>
#define DATA_PIN NRF_GPIO_PIN_MAP(1,7) //设定P1.07为温度传感器out接口
#define PIN_DATA_OUT (nrf_gpio_cfg_output(DATA_PIN));
#define PIN_DATA_IN (nrf_gpio_cfg_input(DATA_PIN,NRF_GPIO_PIN_PULLUP));
#define PIN_DATA_SET (nrf_gpio_pin_set(DATA_PIN)); //DATA_PINê?3???μ???
#define PIN_DATA_CLEAR (nrf_gpio_pin_clear(DATA_PIN));
#define DHT11_SUCCESS NRF_SUCCESS
#define DHT11_DATA_ERR 0xFD
#define DHT11_NACK 0xFE
typedef struct
{
uint8_t h_int;
uint8_t h_deci;
uint8_t t_int;
uint8_t t_deci;
uint8_t check_sum;
}DHT11_Data_t;
uint32_t Read_DHT11(DHT11_Data_t *DHT11_Data);
#endif
我用的是Segger Embeded Studio,所以需要将新建的dht11.h的文件放入工程的同一文件夹下,或者在#include的时候写明dht11.h文件的路径。
2.main.c中完成DHT11驱动定义、数据采集的功能
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "nrf.h"
#include "nrf_delay.h"
#include "nrf_temp.h"
#include "app_error.h"
#include "bsp.h"
#include "dht11.h"
#include "app_timer.h"
#include "nrf_drv_clock.h"
///** @brief Function for main application entry.
// */
//
DHT11_Data_t DHT11_Data;
APP_TIMER_DEF(m_repeated_timer_id);
/**@brief Timeout handler for the repeated timer.
*/
static void lfclk_request(void)
{
ret_code_t err_code = nrf_drv_clock_init();
APP_ERROR_CHECK(err_code);
nrf_drv_clock_lfclk_request(NULL);
}
static uint32_t waitfor_state(bool pin_state)
{
uint8_t delay_us = 100;
do
{
if(nrf_gpio_pin_read(DATA_PIN)==pin_state)
{
return DHT11_SUCCESS;
}
nrf_delay_us(1);
delay_us--;
}while(delay_us);
return DHT11_NACK;
}
static uint8_t Read_Byte(void)
{
uint8_t i, temp=0;
for(i=0;i<8;i++)
{
if(waitfor_state(1)!=DHT11_SUCCESS)return DHT11_NACK;
nrf_delay_us(40);
if(nrf_gpio_pin_read(DATA_PIN)==1)
{
if(waitfor_state(0)!=DHT11_SUCCESS)return DHT11_NACK;
temp|=(uint8_t)(0x01<<(7-i));
}
else
{
temp&=(uint8_t)~(0x01<<(7-i));
}
}
return temp;
}
uint32_t Read_DHT11(DHT11_Data_t *DHT11_Data)
{
PIN_DATA_OUT
PIN_DATA_CLEAR;
nrf_delay_ms(19);
PIN_DATA_SET;
PIN_DATA_IN;
nrf_delay_us(30);
if(nrf_gpio_pin_read(DATA_PIN) == 0)
{
if(waitfor_state(1)!=DHT11_SUCCESS)return DHT11_NACK;
if(waitfor_state(0)!=DHT11_SUCCESS)return DHT11_NACK;
DHT11_Data->h_int= Read_Byte();
DHT11_Data->h_deci= Read_Byte();
DHT11_Data->t_int= Read_Byte();
DHT11_Data->t_deci= Read_Byte();
DHT11_Data->check_sum= Read_Byte();
PIN_DATA_OUT;
PIN_DATA_SET;
if(DHT11_Data->check_sum == DHT11_Data->h_int + DHT11_Data->h_deci + DHT11_Data->t_int+ DHT11_Data->t_deci)
return DHT11_SUCCESS;
else
return DHT11_DATA_ERR;
}
else
{
return DHT11_NACK;
}
}
static void gpio_output_voltage_setup_3v3(void)
{
if ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) !=
(UICR_REGOUT0_VOUT_3V3 << UICR_REGOUT0_VOUT_Pos))
{
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
NRF_UICR->REGOUT0 = (NRF_UICR->REGOUT0 & ~((uint32_t)UICR_REGOUT0_VOUT_Msk)) |
(UICR_REGOUT0_VOUT_3V3 << UICR_REGOUT0_VOUT_Pos);
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy){}
NVIC_SystemReset();
}
}
static void repeated_timer_handler(void * p_context)
{
uint32_t err_code = Read_DHT11(&DHT11_Data);
if(err_code==DHT11_SUCCESS)
{
nrf_gpio_pin_toggle(LED_2);
//NRF_LOG_INFO("Temperature: %d.%d C Humidity: %d.%d H \r\n",DHT11_Data.t_int,DHT11_Data.t_deci,DHT11_Data.h_int,DHT11_Data.h_deci);
//以上为采集到的温度,可以通过访问DHT11_Data数据结构得到温度及湿度数据,但是因为LOG没办法正常工作所以注释了这段
}
else if(err_code==DHT11_DATA_ERR)
{
//Do nothing
}
else if(err_code==DHT11_NACK)
{
//Do nothing
}
else
{
//Do nothing
}
}
/**@brief Create timers.
*/
static void create_timers()
{
// Create timers
ret_code_t err_code = app_timer_create(&m_repeated_timer_id,
APP_TIMER_MODE_REPEATED,
repeated_timer_handler);
APP_ERROR_CHECK(err_code);
}
int main(void)
{
log_init();
app_timer_init();
lfclk_request();
bsp_board_init(BSP_INIT_LEDS);
create_timers();
ret_code_t err_code = app_timer_start(m_repeated_timer_id, APP_TIMER_TICKS(1000), NULL);
APP_ERROR_CHECK(err_code);
}
3.配置sdk_config.h
首先配置app_timer:
// <e> APP_TIMER_ENABLED - app_timer - Application timer functionality
//==========================================================
#ifndef APP_TIMER_ENABLED
#define APP_TIMER_ENABLED 1
#endif
// <o> APP_TIMER_CONFIG_RTC_FREQUENCY - Configure RTC prescaler.
// <0=> 32768 Hz
// <1=> 16384 Hz
// <3=> 8192 Hz
// <7=> 4096 Hz
// <15=> 2048 Hz
// <31=> 1024 Hz
#ifndef APP_TIMER_CONFIG_RTC_FREQUENCY
#define APP_TIMER_CONFIG_RTC_FREQUENCY 0
#endif
// <o> APP_TIMER_CONFIG_IRQ_PRIORITY - Interrupt priority
// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest)
// <1=> 1
// <2=> 2
// <3=> 3
// <4=> 4
// <5=> 5
// <6=> 6
// <7=> 7
#ifndef APP_TIMER_CONFIG_IRQ_PRIORITY
#define APP_TIMER_CONFIG_IRQ_PRIORITY 6
#endif
// <o> APP_TIMER_CONFIG_OP_QUEUE_SIZE - Capacity of timer requests queue.
// <i> Size of the queue depends on how many timers are used
// <i> in the system, how often timers are started and overall
// <i> system latency. If queue size is too small app_timer calls
// <i> will fail.
#ifndef APP_TIMER_CONFIG_OP_QUEUE_SIZE
#define APP_TIMER_CONFIG_OP_QUEUE_SIZE 10
#endif
// <q> APP_TIMER_CONFIG_USE_SCHEDULER - Enable scheduling app_timer events to app_scheduler
#ifndef APP_TIMER_CONFIG_USE_SCHEDULER
#define APP_TIMER_CONFIG_USE_SCHEDULER 0
#endif
// <q> APP_TIMER_KEEPS_RTC_ACTIVE - Enable RTC always on
// <i> If option is enabled RTC is kept running even if there is no active timers.
// <i> This option can be used when app_timer is used for timestamping.
#ifndef APP_TIMER_KEEPS_RTC_ACTIVE
#define APP_TIMER_KEEPS_RTC_ACTIVE 0
#endif
// <o> APP_TIMER_SAFE_WINDOW_MS - Maximum possible latency (in milliseconds) of handling app_timer event.
// <i> Maximum possible timeout that can be set is reduced by safe window.
// <i> Example: RTC frequency 16384 Hz, maximum possible timeout 1024 seconds - APP_TIMER_SAFE_WINDOW_MS.
// <i> Since RTC is not stopped when processor is halted in debugging session, this value
// <i> must cover it if debugging is needed. It is possible to halt processor for APP_TIMER_SAFE_WINDOW_MS
// <i> without corrupting app_timer behavior.
#ifndef APP_TIMER_SAFE_WINDOW_MS
#define APP_TIMER_SAFE_WINDOW_MS 300000
#endif
// <h> App Timer Legacy configuration - Legacy configuration.
//==========================================================
// <q> APP_TIMER_WITH_PROFILER - Enable app_timer profiling
#ifndef APP_TIMER_WITH_PROFILER
#define APP_TIMER_WITH_PROFILER 0
#endif
// <q> APP_TIMER_CONFIG_SWI_NUMBER - Configure SWI instance used.
#ifndef APP_TIMER_CONFIG_SWI_NUMBER
#define APP_TIMER_CONFIG_SWI_NUMBER 0
#endif
// </h>
//==========================================================
// </e>
还有clock配置:
// <e> NRF_CLOCK_ENABLED - nrf_drv_clock - CLOCK peripheral driver - legacy layer
//==========================================================
#ifndef NRF_CLOCK_ENABLED
#define NRF_CLOCK_ENABLED 1
#endif
// <o> CLOCK_CONFIG_LF_SRC - LF Clock Source
// <0=> RC
// <1=> XTAL
// <2=> Synth
// <131073=> External Low Swing
// <196609=> External Full Swing
#ifndef CLOCK_CONFIG_LF_SRC
#define CLOCK_CONFIG_LF_SRC 1
#endif
// <o> CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority
// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
// <0=> 0 (highest)
// <1=> 1
// <2=> 2
// <3=> 3
// <4=> 4
// <5=> 5
// <6=> 6
// <7=> 7
#ifndef CLOCK_CONFIG_IRQ_PRIORITY
#define CLOCK_CONFIG_IRQ_PRIORITY 6
#endif
// </e>
其实nordic提供了NRF_LOG调试器供用户使用,可以直接通过putty连接COM端口去看打印出来的DHT11采集到的温度及湿度,但是我在调试过程中发现LOG和我定义的TIMER冲突,为了程序正常运行我取消了所有的LOG,所以采集到的温湿度不能打印出来,但是我标志成功采集到温度的响应为LED2闪烁一次,所以大家可以看到每采集成功一次LED2就会闪烁一次。温湿度输保存在DHT11_Data数据结构中,大家可以访问DHT11_Data数据结构获得温度及湿度数据:
温度DHT11_Data.t_int,DHT11_Data.t_deci
湿度DHT11_Data.h_int,DHT11_Data.h_deci
因为我后续的工作不需要打印出数据,只需要判断数据大小是否超过阈值所以就不准备继续探究为什么TIMER和LOG冲突。如果有人知道为什么可以评论一下,方便大家交流,博文写的不多,如果哪里有问题还请指正。另外附加app_timer官方使用教程,方便大家学习如何写app_timer:https://devzone.nordicsemi.com/tutorials/b/software-development-kit/posts/application-timer-tutorial