From 8c9c4e6d3eb18b6e4a9d68b453389207ccdaadf5 Mon Sep 17 00:00:00 2001 From: Renaud Fivet Date: Tue, 29 Dec 2020 14:26:39 +0800 Subject: [PATCH] ADC conversion of temperature and voltage internal sensors --- Makefile | 3 +- adc.c | 394 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ adcmain.c | 39 ++++++ system.h | 9 ++ 4 files changed, 444 insertions(+), 1 deletion(-) create mode 100644 adc.c create mode 100644 adcmain.c diff --git a/Makefile b/Makefile index dab2fe8..895a054 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,8 @@ PROJECT = f030f4 #SRCS = startup.c clocks.c uptime.c #SRCS = startup.txeie.c txeie.c uptime.c #SRCS = startup.txeie.c gpioa.c dht11main.c dht11.c -SRCS = startup.txeie.c gpioa.c ds18b20main.c ds18b20.c +#SRCS = startup.txeie.c gpioa.c ds18b20main.c ds18b20.c +SRCS = startup.txeie.c adc.c adcmain.c OBJS = $(SRCS:.c=.o) LIBOBJS = printf.o putchar.o puts.o CPU = -mthumb -mcpu=cortex-m0 diff --git a/adc.c b/adc.c new file mode 100644 index 0000000..cdba1be --- /dev/null +++ b/adc.c @@ -0,0 +1,394 @@ +/* adc.c -- system layer +** Copyright (c) 2020 Renaud Fivet +** +** ADC for temperature sensor and Vrefint +** 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 RCC_APB2ENR_ADCEN 0x00000200 /* 9: ADC clock enable */ + +#define RCC_CR2 RCC[ 13] +#define RCC_CR2_HSI14ON 0x00000001 /* 1: HSI14 clock enable */ +#define RCC_CR2_HSI14RDY 0x00000002 /* 2: HSI14 clock ready */ + + +#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 ADC ((volatile long *) 0x40012400) +#define ADC_ISR ADC[ 0] +#define ADC_ISR_ADRDY 1 /* 0: ADC Ready */ +#define ADC_ISR_EOC 4 /* 2: End Of Conversion flag */ + +#define ADC_CR ADC[ 2] +#define ADC_CR_ADEN 1 /* 0: ADc ENable command */ +#define ADC_CR_ADSTART 4 /* 2: ADC Start Conversion command */ +#define ADC_CR_ADCAL (1 << 31) /* 31: ADC Start Calibration cmd */ + +#define ADC_CFGR1 ADC[ 3] /* Configuration Register 1 */ +#define ADC_CFGR1_SCANDIR 4 /* 2: Scan sequence direction */ +#define ADC_CFGR1_DISCEN (1 << 16) /* 16: Enable Discontinuous mode */ + +#define ADC_CFGR2 ADC[ 4] /* Configuration Register 2 */ +#define ADC_CFGR2_CKMODE (3 << 30) /* 31-30: Clock Mode Mask */ + /* 31-30: Default 00 HSI14 */ +#define ADC_CFGR2_PCLK2 (1 << 30) /* 31-30: PCLK/2 */ +#define ADC_CFGR2_PCLK4 (2 << 30) /* 31-30: PCLK/4 */ + +#define ADC_SMPR ADC[ 5] /* Sampling Time Register */ +#define ADC_CHSELR ADC[ 10] /* Channel Selection Register */ +#define ADC_DR ADC[ 16] /* Data Register */ +#define ADC_CCR ADC[ 194] /* Common Configuration Register */ +#define ADC_CCR_VREFEN (1 << 22) /* 22: Vrefint Enable */ +#define ADC_CCR_TSEN (1 << 23) /* 23: Temperature Sensor Enable */ + +#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 */ + + +/** SYSTEM MEMORY *************************************************************/ +/* STM32F030 calibration addresses (at 3.3V and 30C) */ +#define TS_CAL ((unsigned short *) 0x1FFFF7B8) +#define VREFINT_CAL ((unsigned short *) 0x1FFFF7BA) + + +/* user LED ON when PA4 is low */ +#define LED_IOP A +#define LED_PIN 4 +#define LED_ON 0 +/* 8MHz quartz, configure PLL at 28MHz */ +//#define HSE 8000000 +#define PLL 7 +#define BAUD 9600 +//#define HSI14 1 + +#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 +#elif CLOCK % 8000000 +# warning HCLK is not multiple of 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)) ; +} + + +static void adc_init( void) { +/* Enable ADC peripheral */ + RCC_APB2ENR |= RCC_APB2ENR_ADCEN ; +/* Setup ADC sampling clock */ +#ifdef HSI14 + RCC_CR2 |= RCC_CR2_HSI14ON ; /* Start HSI14 clock */ + do {} while( !( RCC_CR2 & RCC_CR2_HSI14RDY)) ; /* Wait for stable clock */ +/* Select HSI14 as sampling clock for ADC */ +// ADC_CFGR2 &= ~ADC_CFGR2_CKMODE ; /* Default 00 == HSI14 */ +#else +/* Select PCLK/2 as sampling clock for ADC */ + ADC_CFGR2 |= ADC_CFGR2_PCLK2 ; /* 01 PCLK/2 Over default 00 */ +// ADC_CFGR2 |= ADC_CFGR2_PCLK4 ; /* 10 PCLK/4 Over default 00 */ +#endif + +/* Calibration */ + ADC_CR |= ADC_CR_ADCAL ; + do {} while( ADC_CR & ADC_CR_ADCAL) ; /* Wait end of calibration */ + +/* Enable Command (below Work Around from Errata necessary with PCLK/4) */ + do { + ADC_CR |= ADC_CR_ADEN ; + } while( !( ADC_ISR & ADC_ISR_ADRDY)) ; + +/* Select inputs, precision and scan direction */ + ADC_CHSELR = 3 << 16 ; /* Channel 16: temperature, Channel 17: Vrefint */ + ADC_SMPR = 7 ; + ADC_CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN ; +/* Default scan direction (00) is Temperature before Voltage */ +// ADC_CFGR1 &= ~ADC_CFGR1_SCANDIR ; /* Default 0 is low to high */ + ADC_CFGR1 |= ADC_CFGR1_DISCEN ; /* Enable Discontinuous mode */ +} + +static unsigned adc_convert( void) { +/* Either only one channel in sequence or Discontinuous mode ON */ + ADC_CR |= ADC_CR_ADSTART ; /* Start ADC conversion */ + do {} while( ADC_CR & ADC_CR_ADSTART) ; /* Wait for start command cleared */ + return ADC_DR ; +} + +void adc_vnt( vnt_cmd_t cmd, short *ptrV, short *ptrC) { + if( cmd == VNT_INIT) + adc_init() ; + + if( cmd <= VNT_CAL) { + /* Calibration Values */ + *ptrV = *VREFINT_CAL ; + *ptrC = *TS_CAL ; + return ; + } + +/* ADC Conversion */ + *ptrC = adc_convert() ; + *ptrV = adc_convert() ; + + if( cmd == VNT_VNC) { + *ptrV = 330 * *VREFINT_CAL / *ptrV ; + *ptrC = 850 + (1500 - *ptrC) * 10 / 4 ; + } +} + + +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 adc.c */ diff --git a/adcmain.c b/adcmain.c new file mode 100644 index 0000000..9b90d05 --- /dev/null +++ b/adcmain.c @@ -0,0 +1,39 @@ +/* adcmain.c -- ADC reading of reference voltage and temperature sensor */ +/* Copyright (c) 2020 Renaud Fivet */ + +#include +#include "system.h" + +#define RAW + +int main( void) { + unsigned last = 0 ; + short calV, calC ; + +/* Initialize ADC and fetch calibration values */ + adc_vnt( VNT_INIT, &calV, &calC) ; +#ifdef RAW + printf( "%u, %u\n", calV, calC) ; +#endif + + for( ;;) + if( uptime == last) + yield() ; + else { + short Vsample, Csample ; + + last = uptime ; +#ifdef RAW + adc_vnt( VNT_RAW, &Vsample, &Csample) ; + printf( "%i, %i, %i, %i, ", calV, Vsample, calC, Csample) ; + Vsample = 330 * calV / Vsample ; + Csample = 850 + (1500 - (int) Csample) * 10 / 4 ; +#else + adc_vnt( VNT_VNC, &Vsample, &Csample) ; +#endif + printf( "%i.%i, %i.%i\n", Vsample / 100, Vsample % 100, + Csample / 10, Csample % 10) ; + } +} + +/* end of adcmain.c */ diff --git a/system.h b/system.h index fb77ae2..4724563 100644 --- a/system.h +++ b/system.h @@ -22,4 +22,13 @@ iolvl_t gpioa_read( int pin) ; /* Read level of GPIOA pin */ void usleep( unsigned usecs) ; /* wait at least usecs us */ +typedef enum { + VNT_INIT, + VNT_CAL, + VNT_RAW, + VNT_VNC +} vnt_cmd_t ; + +void adc_vnt( vnt_cmd_t cmd, short *ptrV, short *ptrC) ; + /* end of system.h */