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