DHT11 humidity and temperature sensor sampling.

This commit is contained in:
Renaud 2020-12-15 09:41:12 +08:00
parent 9e080f1740
commit 788c5c5b3b
6 changed files with 447 additions and 1 deletions

View File

@ -36,7 +36,8 @@ PROJECT = f030f4
#SRCS = startup.c uplow.2.c uptime.c
#SRCS = startup.c uplow.2.c hello.c
#SRCS = startup.c clocks.c uptime.c
SRCS = startup.txeie.c txeie.c uptime.c
#SRCS = startup.txeie.c txeie.c uptime.c
SRCS = startup.txeie.c gpioa.c dht11main.c dht11.c
OBJS = $(SRCS:.c=.o)
LIBOBJS = printf.o putchar.o puts.o
CPU = -mthumb -mcpu=cortex-m0

93
dht11.c Normal file
View File

@ -0,0 +1,93 @@
/* dht11.c -- DHT11 humidity and temperature sensor reading */
/* Copyright (c) 2020 Renaud Fivet */
#include "dht11.h" /* implements DHT11 API */
#include "system.h" /* usleep(), gpioa_*() */
#define DIO 13
#define dht11_input() gpioa_input( DIO)
#define dht11_output() gpioa_output( DIO)
#define dht11_bread() gpioa_read( DIO)
#define MAX_RETRIES 200 /* at 48 MHz, 160 retries for 80 us HIGH */
#define is_not_LOW( a) a != LOW
#define is_not_HIGH( a) a == LOW
#define wait_level( lvl) \
retries = MAX_RETRIES ; \
while( is_not_##lvl( dht11_bread())) \
if( retries-- == 0) \
return DHT11_FAIL_TOUT
/* 5 .. 95 %RH, -20 .. 60 C */
unsigned char dht11_humid ; /* 5 .. 95 %RH */
signed char dht11_tempc ; /* -20 .. 60 C */
unsigned char dht11_tempf ; /* .0 .. .9 C */
void dht11_init( void) {
/* At startup A13 is ALT DIO with Pull Up enabled */
dht11_input() ;
}
dht11_retv_t dht11_read( void) {
unsigned char values[ 5] ;
/* Host START: pulls line down for > 18ms then release line, pull-up raises to HIGH */
dht11_output() ;
usleep( 18000) ;
dht11_input() ;
/* DHT START: takes line, 80us low then 80us high */
int retries ; /* retry counter */
wait_level( LOW) ; /* HIGH -> LOW, starts 80us low */
wait_level( HIGH) ; /* LOW -> HIGH, ends 80us low, starts 80us high */
/* DHT transmits 40 bits, high bit first
* 0 coded as 50us low then 26~28us high
* 1 coded as 50us low then 70us high
*/
wait_level( LOW) ; /* HIGH -> LOW, ends, 80us high, starts 50us low */
int threshold = (MAX_RETRIES + retries) / 2 ;
unsigned char sum = 0 ;
unsigned char v = 0 ;
for( int idx = 0 ; idx <= 4 ; idx += 1) {
sum += v ;
v = 0 ;
for( unsigned char curbit = 128 ; curbit ; curbit >>= 1) {
/* Measure duration of HIGH level */
wait_level( HIGH) ; /* LOW -> HIGH, ends 50us low, starts timed high */
wait_level( LOW) ; /* HIGH -> LOW, timed high ends, starts 50us low */
/* Set bit based on measured HIGH duration */
if( retries < threshold) /* 0 == 26~28us, 1 == 70us */
v |= curbit ;
}
values[ idx] = v ;
}
/* DHT STOP: releases line after 50us, pull-up raises to HIGH */
wait_level( HIGH) ; /* LOW -> HIGH, ends 50us low, DHT has released the line */
if( sum != values[ 4])
return DHT11_FAIL_CKSUM ;
dht11_humid = values[ 0] ;
dht11_tempc = values[ 2] ;
dht11_tempf = values[ 3] ;
if( dht11_tempf & 0x80) {
dht11_tempc *= -1 ;
dht11_tempf = 10 - ( dht11_tempf & 0x7F) ;
if( dht11_tempf == 10) {
dht11_tempc -= 1 ;
dht11_tempf = 0 ;
}
}
return DHT11_SUCCESS ;
}
/* end of file dht11.c */

20
dht11.h Normal file
View File

@ -0,0 +1,20 @@
/* dht11.h -- DHT11 API */
/* Copyright (c) 2020 Renaud Fivet */
typedef enum {
DHT11_SUCCESS,
DHT11_FAIL_TOUT,
DHT11_FAIL_CKSUM
} dht11_retv_t ;
/* 5 .. 95 %RH, -20 .. 60 C */
extern unsigned char dht11_humid ; /* 5 .. 95 %RH */
extern signed char dht11_tempc ; /* -20 .. 60 C */
extern unsigned char dht11_tempf ; /* .0 .. .9 C */
void dht11_init( void) ;
dht11_retv_t dht11_read( void) ;
/* end of dht11.h */

31
dht11main.c Normal file
View File

@ -0,0 +1,31 @@
/* dht11main.c -- sample DHT11 sensor */
/* Copyright (c) 2020 Renaud Fivet */
#include <stdio.h>
#include "system.h"
#include "dht11.h"
int main() {
static unsigned last ;
dht11_init() ;
for( ;;)
if( last == uptime)
yield() ;
else {
last = uptime ;
if( 2 == (last % 5)) /* every 5 seconds starting 2s after boot */
switch( dht11_read()) {
case DHT11_SUCCESS:
printf( "%u%%RH, %d.%uC\n", dht11_humid, dht11_tempc, dht11_tempf) ;
break ;
case DHT11_FAIL_TOUT:
puts( "Timeout") ;
break ;
case DHT11_FAIL_CKSUM:
puts( "Cksum error") ;
}
}
}
/* end of dht11main.c */

288
gpioa.c Normal file
View File

@ -0,0 +1,288 @@
/* gpioa.c -- system layer
** Copyright (c) 2020 Renaud Fivet
**
** gpioa low level API and usleep()
** interrupt based serial transmission
** clocks configuration: HSI, HSE, PLL HSI, PLL HSE
** implements system.h interface: uptime, init(), kputc(), kputs(), yield()
** uptime = seconds elapsed since boot
** Serial tx, SysClck 8MHz HSI based, baudrate 9600, Busy wait transmission
** user LED toggled every second
** SysTick interrupt every second
*/
#include "system.h" /* implements system.h */
/** CORE **********************************************************************/
#define SYSTICK ((volatile unsigned long *) 0xE000E010)
#define SYSTICK_CSR SYSTICK[ 0]
#define SYSTICK_RVR SYSTICK[ 1]
#define SYSTICK_CVR SYSTICK[ 2]
#define NVIC ((volatile long *) 0xE000E100)
#define NVIC_ISER NVIC[ 0]
#define unmask_irq( idx) NVIC_ISER = 1 << idx
#define USART1_IRQ_IDX 27
/** PERIPH ********************************************************************/
#define CAT( a, b) a##b
#define RCC ((volatile long *) 0x40021000)
#define RCC_CR RCC[ 0]
#define RCC_CR_HSION 0x00000001 /* 1: Internal High Speed clock enable */
#define RCC_CR_HSEON 0x00010000 /* 16: External High Speed clock enable */
#define RCC_CR_HSERDY 0x00020000 /* 17: External High Speed clock ready flag */
#define RCC_CR_PLLON 0x01000000 /* 24: PLL enable */
#define RCC_CR_PLLRDY 0x02000000 /* 25: PLL clock ready flag */
#define RCC_CFGR RCC[ 1]
#define RCC_CFGR_SW_MSK 0x00000003 /* 1-0: System clock SWitch Mask */
#define RCC_CFGR_SW_HSE 0x00000001 /* 1-0: Switch to HSE as system clock */
#define RCC_CFGR_SW_PLL 0x00000002 /* 1-0: Switch to PLL as system clock */
#define RCC_CFGR_SWS_MSK 0x0000000C /* 3-2: System clock SWitch Status Mask */
#define RCC_CFGR_SWS_HSE 0x00000004 /* 3-2: HSE used as system clock */
#define RCC_CFGR_SWS_PLL 0x00000008 /* 3-2: PLL used as system clock */
#define RCC_CFGR_PLLSRC 0x00010000
#define RCC_CFGR_PLLSRC_HSI 0x00000000 /* HSI / 2 */
#define RCC_CFGR_PLLSRC_HSE 0x00010000 /* HSE */
#define RCC_CFGR_PLLXTPRE 0x00020000
#define RCC_CFGR_PLLXTPRE_DIV1 0x00000000 /* HSE */
#define RCC_CFGR_PLLXTPRE_DIV2 0x00020000 /* HSE / 2 */
#define RCC_CFGR_PLLMUL_MSK (0x00F << 18)
#define RCC_CFGR_PLLMUL( v) ((v - 2) << 18)
#define RCC_AHBENR RCC[ 5]
#define RCC_AHBENR_IOPn( n) (1 << (17 + n))
#define RCC_AHBENR_IOPh( h) RCC_AHBENR_IOPn( CAT( 0x, h) - 0xA)
#define RCC_APB2ENR RCC[ 6]
#define RCC_APB2ENR_USART1EN 0x00004000 /* 14: USART1 clock enable */
#define GPIOA ((volatile long *) 0x48000000)
#define GPIOB ((volatile long *) 0x48000400)
#define GPIO( x) CAT( GPIO, x)
#define MODER 0
#define IDR 4
#define ODR 5
#define AFRH 9
#define USART1 ((volatile long *) 0x40013800)
#define CR1 0 /* Config Register */
#define BRR 3 /* BaudRate Register */
#define ISR 7 /* Interrupt and Status Register */
#define TDR 10 /* Transmit Data Register*/
#define USART_CR1_TXEIE (1 << 7) /* 7: TDR Empty Interrupt Enable */
#define USART_CR1_TE 8 /* 3: Transmit Enable */
#define USART_CR1_RE 4 /* 2: Receive Enable */
#define USART_CR1_UE 1 /* 0: USART Enable */
#define USART_ISR_TXE (1 << 7) /* 7: Transmit Data Register Empty */
/* user LED ON when PA4 is low */
#define LED_IOP A
#define LED_PIN 4
#define LED_ON 0
/* 8MHz quartz, configure PLL at 24MHz */
#define HSE 8000000
#define PLL 6
#define BAUD 9600
#ifdef PLL
# ifdef HSE
# define CLOCK HSE / 2 * PLL
# else /* HSI */
# define CLOCK 8000000 / 2 * PLL
# endif
# if CLOCK < 16000000
# error PLL output below 16MHz
# endif
#elif defined( HSE)
# define CLOCK HSE
#else /* HSI */
# define CLOCK 8000000
#endif
#if CLOCK > 48000000
# error clock frequency exceeds 48MHz
#endif
#if CLOCK % BAUD
# warning baud rate not accurate at that clock frequency
#endif
static unsigned char txbuf[ 8] ; // best if size is a power of 2 for cortex-M0
#define TXBUF_SIZE (sizeof txbuf / sizeof txbuf[ 0])
static unsigned char txbufin ;
static volatile unsigned char txbufout ;
void USART1_Handler( void) {
if( txbufout == txbufin) {
/* Empty buffer => Disable TXEIE */
USART1[ CR1] &= ~USART_CR1_TXEIE ;
} else {
static unsigned char lastc ;
unsigned char c ;
c = txbuf[ txbufout] ;
if( c == '\n' && lastc != '\r')
c = '\r' ;
else
txbufout = (txbufout + 1) % TXBUF_SIZE ;
USART1[ TDR] = c ;
lastc = c ;
}
}
void kputc( unsigned char c) { /* character output */
int nextidx ;
/* Wait if buffer full */
nextidx = (txbufin + 1) % TXBUF_SIZE ;
while( nextidx == txbufout)
yield() ;
txbuf[ txbufin] = c ;
txbufin = nextidx ;
/* Trigger transmission by enabling interrupt */
USART1[ CR1] |= USART_CR1_TXEIE ;
}
int kputs( const char s[]) { /* string output */
int cnt = 0 ;
int c ;
while( (c = *s++) != 0) {
kputc( c) ;
cnt += 1 ;
}
return cnt ;
}
void yield( void) { /* give way */
__asm( "WFI") ; /* Wait for System Tick Interrupt */
}
volatile unsigned uptime ; /* seconds elapsed since boot */
#ifdef LED_ON
static void userLEDtoggle( void) {
GPIO( LED_IOP)[ ODR] ^= 1 << LED_PIN ; /* Toggle User LED */
}
#endif
void SysTick_Handler( void) {
uptime += 1 ;
#ifdef LED_ON
userLEDtoggle() ;
#endif
}
void usleep( unsigned usecs) { /* wait at least usec µs */
#if CLOCK / 8000000 < 1
# error HCLK below 8 MHz
#endif
usecs = SYSTICK_CVR - (CLOCK / 8000000 * usecs) ;
while( SYSTICK_CVR > usecs) ;
}
/* GPIOA low level API ********************************************************/
void gpioa_input( int pin) { /* Configure GPIOA pin as input */
GPIOA[ MODER] &= ~(3 << (pin * 2)) ; /* Apin as input [00] */
}
void gpioa_output( int pin) { /* Configure GPIOA pin as output */
GPIOA[ MODER] |= 1 << (pin * 2) ; /* Apin output (over [00]) */
}
iolvl_t gpioa_read( int pin) { /* Read level of GPIOA pin */
return LOW != (GPIOA[ IDR] & (1 << pin)) ;
}
int init( void) {
/* By default SYSCLK == HSI [8MHZ] */
#ifdef HSE
/* Start HSE clock (8 MHz external oscillator) */
RCC_CR |= RCC_CR_HSEON ;
/* Wait for oscillator to stabilize */
do {} while( (RCC_CR & RCC_CR_HSERDY) == 0) ;
#endif
#ifdef PLL
/* Setup PLL HSx/2 * 6 [24MHz] */
/* Default 0: PLL HSI/2 src, PLL MULL * 2 */
RCC_CFGR =
# ifdef HSE
RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLXTPRE_DIV2 |
# endif
RCC_CFGR_PLLMUL( PLL) ;
RCC_CR |= RCC_CR_PLLON ;
do {} while( (RCC_CR & RCC_CR_PLLRDY) == 0) ; /* Wait for PLL */
/* Switch to PLL as system clock SYSCLK == PLL [24MHz] */
RCC_CFGR = (RCC_CFGR & ~RCC_CFGR_SW_MSK) | RCC_CFGR_SW_PLL ;
do {} while( (RCC_CFGR & RCC_CFGR_SWS_MSK) != RCC_CFGR_SWS_PLL) ;
#elif defined( HSE)
/* Switch to HSE as system clock SYSCLK == HSE [8MHz] */
RCC_CFGR = (RCC_CFGR & ~RCC_CFGR_SW_MSK) | RCC_CFGR_SW_HSE ;
do {} while( (RCC_CFGR & RCC_CFGR_SWS_MSK) != RCC_CFGR_SWS_HSE) ;
#endif
#ifdef HSE
/* Switch off HSI */
RCC_CR &= ~RCC_CR_HSION ;
#endif
/* SYSTICK */
SYSTICK_RVR = CLOCK / 8 - 1 ; /* HBA / 8 */
SYSTICK_CVR = 0 ;
SYSTICK_CSR = 3 ; /* HBA / 8, Interrupt ON, Enable */
/* SysTick_Handler will execute every 1s from now on */
#ifdef LED_ON
/* User LED ON */
RCC_AHBENR |= RCC_AHBENR_IOPh( LED_IOP) ; /* Enable IOPx periph */
GPIO( LED_IOP)[ MODER] |= 1 << (LED_PIN * 2) ; /* LED_IO Output [01],
** over default 00 */
/* OTYPER Push-Pull by default */
/* Pxn output default LOW at reset */
# if LED_ON
userLEDtoggle() ;
# endif
#endif
/* USART1 9600 8N1 */
RCC_AHBENR |= RCC_AHBENR_IOPh( A) ; /* Enable GPIOA periph */
GPIOA[ MODER] |= 0x0A << (9 * 2) ; /* PA9-10 ALT 10, over default 00 */
GPIOA[ AFRH] |= 0x110 ; /* PA9-10 AF1 0001, over default 0000 */
RCC_APB2ENR |= RCC_APB2ENR_USART1EN ;
USART1[ BRR] = CLOCK / BAUD ; /* PCLK is default source */
USART1[ CR1] |= USART_CR1_UE | USART_CR1_TE ; /* Enable USART & Tx */
/* Unmask USART1 irq */
unmask_irq( USART1_IRQ_IDX) ;
kputs(
#ifdef PLL
"PLL"
#endif
#ifdef HSE
"HSE"
#else
"HSI"
#endif
"\n") ;
return 0 ;
}
/* end of gpioa.c */

View File

@ -9,4 +9,17 @@ void kputc( unsigned char c) ; /* character output */
int kputs( const char s[]) ; /* string output */
void yield( void) ; /* give way */
/* GPIOA low level API ********************************************************/
typedef enum {
LOW = 0,
HIGH
} iolvl_t ;
void gpioa_input( int pin) ; /* Configure GPIOA pin as input */
void gpioa_output( int pin) ; /* Configure GPIOA pin as output */
iolvl_t gpioa_read( int pin) ; /* Read level of GPIOA pin */
void usleep( unsigned usecs) ; /* wait at least usecs us */
/* end of system.h */