diff --git a/examples/adc_opamp_dma/Makefile b/examples/adc_opamp_dma/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..ac00c743ffd45a07a1f1f1361a0ee32df0419e1b --- /dev/null +++ b/examples/adc_opamp_dma/Makefile @@ -0,0 +1,41 @@ +TARGET:=adc_dma_opamp + +all : flash + +PREFIX:=riscv64-unknown-elf + +GPIO_Toggle:=EXAM/GPIO/GPIO_Toggle/User + +CH32V003FUN:=../../ch32v003fun +MINICHLINK:=../../minichlink + +CFLAGS:= \ + -g -Os -flto -ffunction-sections \ + -static-libgcc \ + -march=rv32ec \ + -mabi=ilp32e \ + -I/usr/include/newlib \ + -I$(CH32V003FUN) \ + -nostdlib \ + -I. -DSTDOUT_UART -Wall + +LDFLAGS:=-T $(CH32V003FUN)/ch32v003fun.ld -Wl,--gc-sections -L../../misc -lgcc + +SYSTEM_C:=$(CH32V003FUN)/ch32v003fun.c + +$(TARGET).elf : $(SYSTEM_C) $(TARGET).c + $(PREFIX)-gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) + +$(TARGET).bin : $(TARGET).elf + $(PREFIX)-size $^ + $(PREFIX)-objdump -S $^ > $(TARGET).lst + $(PREFIX)-objdump -t $^ > $(TARGET).map + $(PREFIX)-objcopy -O binary $< $(TARGET).bin + $(PREFIX)-objcopy -O ihex $< $(TARGET).hex + +flash : $(TARGET).bin + $(MINICHLINK)/minichlink -w $< flash -b + +clean : + rm -rf $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).lst $(TARGET).map $(TARGET).hex + diff --git a/examples/adc_opamp_dma/README.md b/examples/adc_opamp_dma/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ba71af4a9b7e2bd568b1a7d25f2300f9e9fc9c05 --- /dev/null +++ b/examples/adc_opamp_dma/README.md @@ -0,0 +1,49 @@ +# DMA ADC & op-amp example +This example code demonstrates the use of the CH32V003 on-chip ADC in continuous +DMA mode wherein several analog input channels are configured on GPIO and then +automatically digitized into a memory buffer. When software needs a channel +reading it need only access the buffer to get the latest result. + +Additionally, the on-chip op-amp is enabled on one of the input channels to +allow some gain. + +## Theory +This demo will sample four channels, one of which is connected to the internal +op-amp. Sampling will happen continuously with no processor intervention and +results saved in a fixed-length buffer that is constantly overwritten with +new data. When the op-amp is enabled and properly wired up then its output can +be evaluated with respect to other analog signals input to the ADC. + +### ADC +The ADC can sample from eight GPIO pins and two internal references. In this +example we use four channels on GPIO pins. Continuous conversion is used with +DMA enabled. The following channels are used +* Pin PD4, A7 - op-amp output +* Pin PD3, A4 +* Pin PD2, A3 +* Pin PC4, A2 + +### DMA +DMA channel 1 is dedicated to the ADC and is set up for half-words from peripheral +to memory, circular. The buffer length is identical to the number of channels that +are being used so reading from offset N in the buffer is equivalent to reading +channel sequence #N results. + +### Op-Amp +The op-amp is internally connected with its output on pin PD4, positive input on +either pin PD7 or PA2 and negative input on PD0 or PA1. PA1, PA2 and PD7 are +already in use for the crystal and NRST on the WCH eval board so it's difficult +to test the op-amp in that system. +* Using PA1 and PA2 requires removing the crystal and loading caps, as well as +installing zero-ohm resistors. +* Using PD7 as an op-amp input requires disabling the NRST function which must +be done by accessing the user option byte in flash memory. + +## Use +To test ADC DMA only, flash the code to a device and connect a serial terminal +to pin PD5. Observe four columns of data printed for the ADC results on +PD4, PD3, PD2 and C4. With nothing connected they will float around 1.6V and +yield results near 500. Grounding any of those pins will force the result to 0 +while tying the pin to VDD will force it to 1023. + +Testing the op-amp is TBD. diff --git a/examples/adc_opamp_dma/adc.h b/examples/adc_opamp_dma/adc.h new file mode 100644 index 0000000000000000000000000000000000000000..87201e6edfa5a7c1ea27d4fa1e5bb2a5e520fc46 --- /dev/null +++ b/examples/adc_opamp_dma/adc.h @@ -0,0 +1,91 @@ +/* + * Single-File-Header for using ADC with DMA + * 04-13-2023 E. Brombaugh + */ + +#ifndef _ADC_H +#define _ADC_H + +#include <stdint.h> + +#define ADC_NUMCHLS 4 +volatile uint16_t adc_buffer[ADC_NUMCHLS]; + +/* + * initialize adc for DMA + */ +void adc_init( void ) +{ + // ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2 + RCC->CFGR0 &= ~(0x1F<<11); + + // Enable GPIOD and ADC + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | + RCC_APB2Periph_ADC1; + + // PD4 is analog input chl 7 + GPIOD->CFGLR &= ~(0xf<<(4*4)); // CNF = 00: Analog, MODE = 00: Input + + // PD3 is analog input chl 4 + GPIOD->CFGLR &= ~(0xf<<(4*3)); // CNF = 00: Analog, MODE = 00: Input + + // PD2 is analog input chl 3 + GPIOD->CFGLR &= ~(0xf<<(4*2)); // CNF = 00: Analog, MODE = 00: Input + + // PC4 is analog input chl 2 + GPIOC->CFGLR &= ~(0xf<<(4*4)); // CNF = 00: Analog, MODE = 00: Input + + // Reset the ADC to init all regs + RCC->APB2PRSTR |= RCC_APB2Periph_ADC1; + RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1; + + // Set up four conversions on chl 7, 4, 3, 2 + ADC1->RSQR1 = (ADC_NUMCHLS-1) << 20; // four chls in the sequence + ADC1->RSQR2 = 0; + ADC1->RSQR3 = (7<<(5*0)) | (4<<(5*1)) | (3<<(5*2)) | (2<<(5*3)); + + // set sampling time for chl 7, 4, 3, 2 + // 0:7 => 3/9/15/30/43/57/73/241 cycles + ADC1->SAMPTR2 = (7<<(3*7)) | (7<<(3*4)) | (7<<(3*3)) | (7<<(3*2)); + + // turn on ADC + ADC1->CTLR2 |= ADC_ADON; + + // Reset calibration + ADC1->CTLR2 |= ADC_RSTCAL; + while(ADC1->CTLR2 & ADC_RSTCAL); + + // Calibrate + ADC1->CTLR2 |= ADC_CAL; + while(ADC1->CTLR2 & ADC_CAL); + + // Turn on DMA + RCC->AHBPCENR |= RCC_AHBPeriph_DMA1; + + //DMA1_Channel1 is for ADC + DMA1_Channel1->PADDR = (uint32_t)&ADC1->RDATAR; + DMA1_Channel1->MADDR = (uint32_t)adc_buffer; + DMA1_Channel1->CNTR = ADC_NUMCHLS; + DMA1_Channel1->CFGR = + DMA_M2M_Disable | + DMA_Priority_VeryHigh | + DMA_MemoryDataSize_HalfWord | + DMA_PeripheralDataSize_HalfWord | + DMA_MemoryInc_Enable | + DMA_Mode_Circular | + DMA_DIR_PeripheralSRC; + + // Turn on DMA channel 1 + DMA1_Channel1->CFGR |= DMA_CFGR1_EN; + + // enable scanning + ADC1->CTLR1 |= ADC_SCAN; + + // Enable continuous conversion and DMA + ADC1->CTLR2 |= ADC_CONT | ADC_DMA | ADC_EXTSEL; + + // start conversion + ADC1->CTLR2 |= ADC_SWSTART; +} + +#endif diff --git a/examples/adc_opamp_dma/adc_dma_opamp.c b/examples/adc_opamp_dma/adc_dma_opamp.c new file mode 100644 index 0000000000000000000000000000000000000000..0e7889ca256df8b39cd7aa968635c04a004f1dc0 --- /dev/null +++ b/examples/adc_opamp_dma/adc_dma_opamp.c @@ -0,0 +1,61 @@ +/* + * Example for using ADC with DMA and an op-amp + * 04-13-2023 E. Brombaugh + */ + +// Could be defined here, or in the processor defines. +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK + +#include "ch32v003fun.h" +#include <stdio.h> +#include "adc.h" +#include "opamp.h" + +int main() +{ + SystemInit48HSI(); + + // start serial @ default 115200bps + SetupUART( UART_BRR ); + Delay_Ms(100); + printf("\r\r\n\nadc_dma_opamp example\n\r"); + + // init adc + printf("initializing adc..."); + adc_init(); + printf("done.\n\r"); + +#if 0 + printf("MADDR = 0x%08X, Buffer = 0x%08X\n\r", + DMA1_Channel1->CNTR, adc_buffer); + while(1) + { + //printf("STATR = 0x%08X, RDATAR = 0x%04X, CNTR = 0x%04X\n\r", + //ADC1->STATR, ADC1->RDATAR, DMA1_Channel1->CNTR); + } +#endif + + // init op-amp + printf("initializing op-amp...\n\r"); + opamp_init(); + printf("done.\n\r"); + + // Enable GPIOs + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + + // GPIO C1 Push-Pull + GPIOC->CFGLR &= ~(0xf<<(4*1)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*1); + + printf("looping...\n\r"); + while(1) + { + GPIOC->BSHR = (1<<1); // Turn on GPIOs + Delay_Ms( 100 ); + GPIOC->BSHR = (1<<1+16); // Turn off GPIODs + Delay_Ms( 100 ); + printf( "%4d %4d ", adc_buffer[0], adc_buffer[1]); + printf( "%4d %4d\n\r", adc_buffer[2], adc_buffer[3]); + } +} diff --git a/examples/adc_opamp_dma/opamp.h b/examples/adc_opamp_dma/opamp.h new file mode 100644 index 0000000000000000000000000000000000000000..78de0ecc062c98e17b5ce2cb4a1a6d0de2d38af5 --- /dev/null +++ b/examples/adc_opamp_dma/opamp.h @@ -0,0 +1,27 @@ +/* + * Single-File-Header for using op-amp + * 04-13-2023 E. Brombaugh + */ + +#ifndef _OPAMP_H +#define _OPAMP_H + +#include <stdint.h> + +/* + * initialize adc for polling + */ +void opamp_init( void ) +{ + // turn on the op-amp + EXTEN->EXTEN_CTR |= EXTEN_OPA_EN; + + // select op-amp pos pin: 0 = PA2, 1 = PD7 + //EXTEN->EXTEN_CTR |= EXTEN_OPA_PSEL; + + // select op-amp neg pin: 0 = PA1, 1 = PD0 + //EXTEN->EXTEN_CTR |= EXTEN_OPA_PSEL; + +} +#endif +