diff --git a/examples/GPIO/GPIO.c b/examples/GPIO/GPIO.c index ed430880241ba0eb164677bf5c2a07d2dd197917..148aa0198a1710d1b298fbf2f6c038201a90b6e6 100644 --- a/examples/GPIO/GPIO.c +++ b/examples/GPIO/GPIO.c @@ -3,7 +3,7 @@ #define SYSTEM_CORE_CLOCK 48000000 #include "ch32v003fun.h" -#include "wiring_digital.h" +#include "wiring.h" #include <stdio.h> #define APB_CLOCK SYSTEM_CORE_CLOCK @@ -11,37 +11,37 @@ uint32_t count; int main() { - SystemInit48HSI(); - - // Enable GPIO ports - enablePort(port_C); - enablePort(port_D); - - for (int i = pin_C0; i <= pin_C7; i++) { - pinMode(i, pinMode_O_pushPull); - } - - // GPIO D4 Push-Pull - pinMode(pin_D4, pinMode_O_pushPull); - - while (1) { - // Turn on pins - digitalWrite(pin_C0, high); - digitalWrite(pin_D4, high); - Delay_Ms(250); - // Turn off pins - digitalWrite(pin_C0, low); - digitalWrite(pin_D4, low); - Delay_Ms(250); - for (int i = pin_C0; i <= pin_C7; i++) { - digitalWrite(i, high); - Delay_Ms(50); - } - for (int i = pin_C7; i >= pin_C0; i--) { - digitalWrite(i, low); - Delay_Ms(50); - } - Delay_Ms(250); - count++; - } + SystemInit48HSI(); + + // Enable GPIO ports + portEnable(port_C); + portEnable(port_D); + + for (int i = pin_C0; i <= pin_C7; i++) { + pinMode(i, pinMode_O_pushPull); + } + + // GPIO D4 Push-Pull + pinMode(pin_D4, pinMode_O_pushPull); + + while (1) { + // Turn on pins + digitalWrite(pin_C0, high); + digitalWrite(pin_D4, high); + Delay_Ms(250); + // Turn off pins + digitalWrite(pin_C0, low); + digitalWrite(pin_D4, low); + Delay_Ms(250); + for (int i = pin_C0; i <= pin_C7; i++) { + digitalWrite(i, high); + Delay_Ms(50); + } + for (int i = pin_C7; i >= pin_C0; i--) { + digitalWrite(i, low); + Delay_Ms(50); + } + Delay_Ms(250); + count++; + } } diff --git a/examples/GPIO/Makefile b/examples/GPIO/Makefile index 5090155f3f5f8a616ff580b733b6ef8b512906a4..15a92ee8541c73eff2eee710a57b721a6b032c6b 100644 --- a/examples/GPIO/Makefile +++ b/examples/GPIO/Makefile @@ -25,7 +25,7 @@ LDFLAGS:=-T $(CH32V003FUN)/ch32v003fun.ld -Wl,--gc-sections -L../../misc -lgcc SYSTEM_C:=$(CH32V003FUN)/ch32v003fun.c -$(TARGET).elf : $(SYSTEM_C) $(TARGET).c wiring_digital.c +$(TARGET).elf : $(SYSTEM_C) $(TARGET).c wiring.c $(PREFIX)-gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) $(TARGET).bin : $(TARGET).elf diff --git a/examples/GPIO/wiring_digital.c b/examples/GPIO/wiring.c similarity index 61% rename from examples/GPIO/wiring_digital.c rename to examples/GPIO/wiring.c index 891441786b5890f97875bc253d39456c64903d99..ce706a3f257aa41496036d72ce41ba00bd1b59f9 100644 --- a/examples/GPIO/wiring_digital.c +++ b/examples/GPIO/wiring.c @@ -1,6 +1,6 @@ //#include <stdio.h> -#include "wiring_digital.h" +#include "wiring.h" #include <stdint.h> @@ -20,7 +20,7 @@ enum GPIOports getPort (enum GPIOpins pin) { -void enablePort(enum GPIOports port) { +void portEnable(enum GPIOports port) { // Enable GPIOs switch (port) { case port_A: @@ -159,3 +159,93 @@ uint8_t digitalRead(uint8_t pin) { int8_t result = (GPIOx->INDR >> PinOffset) & 1; return result; } + + + + + +void ADCinit() { + // select ADC clock source + // ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2 + RCC->CFGR0 &= ~(0x1F<<11); + + // enable clock to the ADC + RCC->APB2PCENR |= RCC_APB2Periph_ADC1; + + // Reset the ADC to init all regs + RCC->APB2PRSTR |= RCC_APB2Periph_ADC1; + RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1; + + // set sampling time for all inputs to 241 cycles + for (uint8_t i = Ain0_A2; i <= AinVcal; i++) { + ADCsetSampletime(i, Ast_241cy_default); + } + + // set trigger to software + ADC1->CTLR2 |= ADC_EXTSEL; + + // pre-clear conversion queue + ADC1->RSQR1 = 0; + ADC1->RSQR2 = 0; + ADC1->RSQR3 = 0; + + // power the ADC + ADCsetPower(1); +} + + + +void ADCsetSampletime(enum ANALOGinputs input, enum ANALOGsampletimes time) { + // clear + ADC1->SAMPTR2 &= ~(0b111)<<(3*input); + // set + ADC1->SAMPTR2 |= time<<(3*input); // 0:7 => 3/9/15/30/43/57/73/241 cycles +} + + + +void ADCsetPower(uint8_t enable) { + if (enable) { + ADC1->CTLR2 |= ADC_ADON; + if (enable == 1) { + // auto-cal each time after turning on the ADC + // can be overridden by calling with enable > 1. + ADCcalibrate(); + } + } + else { + ADC1->CTLR2 &= ~(ADC_ADON); + } +} + + + +void ADCcalibrate() { + // reset calibration + ADC1->CTLR2 |= ADC_RSTCAL; + while(ADC1->CTLR2 & ADC_RSTCAL); + + // calibrate + ADC1->CTLR2 |= ADC_CAL; + while(ADC1->CTLR2 & ADC_CAL); +} + + + +// inspired by arduinos analogRead() +uint16_t analogRead(enum ANALOGinputs input) { + // set mux to selected input + ADC1->RSQR3 = input; + + // may need a delay right here for the mux to actually finish switching?? + // Arduino inserts a full ms delay right here! + + // start sw conversion (auto clears) + ADC1->CTLR2 |= ADC_SWSTART; + + // wait for conversion complete + while(!(ADC1->STATR & ADC_EOC)); + + // get result + return ADC1->RDATAR; +} diff --git a/examples/GPIO/wiring_digital.h b/examples/GPIO/wiring.h similarity index 60% rename from examples/GPIO/wiring_digital.h rename to examples/GPIO/wiring.h index 8b7deeec0e3d32d46966540e3699cb02d91f15d2..5b822036cc1fad1314fc0b249ee1ae6bb8c6293b 100644 --- a/examples/GPIO/wiring_digital.h +++ b/examples/GPIO/wiring.h @@ -1,13 +1,8 @@ -#ifndef WIRING_DIGITAL_H -#define WIRING_DIGITAL_H +#ifndef WIRING_H +#define WIRING_H #include "../../ch32v003fun/ch32v003fun.h" -// Define the pins that will be used for GPIO -#define MY_GPIO_PIN_1 1 -#define MY_GPIO_PIN_2 2 -// Add more pins as needed - enum lowhigh { @@ -63,11 +58,42 @@ enum GPIOpinState { pinState_high, }; +enum ANALOGinputs { + Ain0_A2, + Ain1_A1, + Ain2_C4, + Ain3_D2, + Ain4_D3, + Ain5_D5, + Ain6_D6, + Ain7_D4, + AinVref, + AinVcal, +}; + +enum ANALOGsampletimes { + Ast_3cy, + Ast_9cy, + Ast_15cy, + Ast_30cy, + Ast_43cy, + Ast_57cy, + Ast_73cy, + Ast_241cy_default, +}; + + enum GPIOports getPort (enum GPIOpins pin); -void enablePort(enum GPIOports port); +void portEnable(enum GPIOports port); void pinMode(enum GPIOpins pin, enum GPIOpinMode mode); void digitalWrite(enum GPIOpins pin, uint8_t value); uint8_t digitalRead(uint8_t pin); -#endif // WIRING_DIGITAL_H +void ADCinit(); +void ADCsetPower(uint8_t enable); +void ADCsetSampletime(enum ANALOGinputs input, enum ANALOGsampletimes time); +void ADCcalibrate(); +uint16_t analogRead(enum ANALOGinputs input); + +#endif // WIRING_H diff --git a/examples/GPIO_analogRead/GPIO_analogRead.c b/examples/GPIO_analogRead/GPIO_analogRead.c new file mode 100644 index 0000000000000000000000000000000000000000..76df1facacafbbf45260486402d91266f1f93d7a --- /dev/null +++ b/examples/GPIO_analogRead/GPIO_analogRead.c @@ -0,0 +1,47 @@ +// blink, but with arduino-like HAL +// Could be defined here, or in the processor defines. +#define SYSTEM_CORE_CLOCK 48000000 + +#include "ch32v003fun.h" +#include "wiring.h" +#include <stdio.h> + +#define APB_CLOCK SYSTEM_CORE_CLOCK + +uint32_t count; + +int main() { + SystemInit48HSI(); + + // Enable GPIO ports + portEnable(port_C); + portEnable(port_D); + + for (int i = pin_C0; i <= pin_C7; i++) { + pinMode(i, pinMode_O_pushPull); + } + + // GPIO D4 Push-Pull + pinMode(pin_D4, pinMode_O_pushPull); + + pinMode(pin_D6, pinMode_I_analog); + ADCinit(); + + while (1) { + digitalWrite(pin_D4, high); + uint8_t leds_to_turn_on = (uint8_t)(((float)(analogRead(Ain6_D6)) / 1024.f) * 8.f * 1.2 - 1.f); + uint8_t led_i = 0; + for (int i = pin_C0; i <= pin_C7; i++) { + if (led_i < leds_to_turn_on) { + digitalWrite(i, high); + } + else { + digitalWrite(i, low); + } + led_i++; + } + digitalWrite(pin_D4, low); + Delay_Ms(250); + count++; + } +} diff --git a/examples/GPIO_analogRead/Makefile b/examples/GPIO_analogRead/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c655dc1fa4c9a43364dafc5d98363fae1c15c418 --- /dev/null +++ b/examples/GPIO_analogRead/Makefile @@ -0,0 +1,43 @@ +TARGET:=GPIO_analogRead + +all : flash + +PREFIX:=riscv64-unknown-elf + +GPIO_Toggle:=EXAM/GPIO/GPIO_Toggle/User + +EVT:=../../ch32v003evt + +MINICHLINK:=../../minichlink +CH32V003FUN:=../../ch32v003fun + +CFLAGS:= \ + -g -Os -flto -ffunction-sections \ + -static-libgcc \ + -march=rv32ec \ + -mabi=ilp32e \ + -I/usr/include/newlib \ + -I$(CH32V003FUN) \ + -nostdlib \ + -I. -DTINYVECTOR -Wall + +LDFLAGS:=-T $(CH32V003FUN)/ch32v003fun.ld -Wl,--gc-sections -L../../misc -lgcc + +SYSTEM_C:=$(CH32V003FUN)/ch32v003fun.c + +$(TARGET).elf : $(SYSTEM_C) $(TARGET).c wiring.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/GPIO_analogRead/README.md b/examples/GPIO_analogRead/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3b9e1b6ca6799c77987e830cf7ea868d095601b5 --- /dev/null +++ b/examples/GPIO_analogRead/README.md @@ -0,0 +1,17 @@ +# GPIO Libaray +On the shoulders of the Blink and the adc_polled example, this Arduino-like GPIO + ADC library stands. + +All pins are adressable as outputs, inputs, with pull-up, etc. but now you can also read the eight muxed inputs. +The pins are in an enum, so you can call them by their name and iterate over them. + +It has been extended by an arduino-like analogRead function. + +It's your responsibility to not blow up a pin. +Only use one pin for one thing and you should be fine. + +# GPIO Example +Connect LED + 1k resistor to each pin (C0 to C7 and D4) and GND. +Connect a 10k pot between GND and VCC, wiper to D6. +Marvel at the colorful glory. + +https://user-images.githubusercontent.com/104343143/231814680-d41ae68f-dc7b-4c9c-a3c7-0b88cc82e541.mp4 diff --git a/examples/GPIO_analogRead/wiring.c b/examples/GPIO_analogRead/wiring.c new file mode 100644 index 0000000000000000000000000000000000000000..ce706a3f257aa41496036d72ce41ba00bd1b59f9 --- /dev/null +++ b/examples/GPIO_analogRead/wiring.c @@ -0,0 +1,251 @@ +//#include <stdio.h> + +#include "wiring.h" +#include <stdint.h> + + + +enum GPIOports getPort (enum GPIOpins pin) { + if (pin <= pin_A2) { + return port_A; + } + else if (pin <= pin_C7) { + return port_C; + } + else if (pin <= pin_D7) { + return port_D; + } + return port_none; +} + + + +void portEnable(enum GPIOports port) { + // Enable GPIOs + switch (port) { + case port_A: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOA; + break; + case port_C: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + break; + case port_D: + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD; + break; + case port_none: + break; + } +} + + + +void pinMode(enum GPIOpins pin, enum GPIOpinMode mode) { + GPIO_TypeDef * GPIOx; + uint16_t PinOffset = 4; + + if (pin <= pin_A2) { + GPIOx = GPIOA; + PinOffset *= pin; + } + else if (pin <= pin_C7) { + GPIOx = GPIOC; + PinOffset *= (pin - 2); + } + else if (pin <= pin_D7) { + GPIOx = GPIOD; + PinOffset *= (pin - 10); + } + else { + return; + } + + GPIOx->CFGLR &= ~(0b1111<<PinOffset); // zero the 4 configuration bits + + uint8_t target_pin_state = pinState_nochange; // by default, pin shall retain its state + + uint8_t modeMask = 0; // configuration mask + + switch (mode) { + case pinMode_I_floating: + modeMask = GPIO_CNF_IN_FLOATING; + break; + case pinMode_I_pullUp: + modeMask = GPIO_CNF_IN_PUPD; + target_pin_state = pinState_high; + break; + case pinMode_I_pullDown: + modeMask = GPIO_CNF_IN_PUPD; + target_pin_state = pinState_low; + break; + case pinMode_I_analog: + modeMask = GPIO_CNF_IN_ANALOG; + break; + case pinMode_O_pushPull: + modeMask = GPIO_Speed_10MHz | GPIO_CNF_OUT_PP; + break; + case pinMode_O_openDrain: + modeMask = GPIO_Speed_10MHz | GPIO_CNF_OUT_OD; + break; + case pinMode_O_pushPullMux: + modeMask = GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF; + break; + case pinMode_O_openDrainMux: + modeMask = GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF; + break; + } + + // wirte mask to CFGR + GPIOx->CFGLR |= modeMask<<PinOffset; + + // set pin state + if (target_pin_state > pinState_nochange) { + digitalWrite(pin, target_pin_state - 1); + } +} + + + +void digitalWrite(enum GPIOpins pin, uint8_t value) { + // no checks given whether pin is currently being toggled by timer! your output trannys are in your hands! beware the magic smoke! + GPIO_TypeDef * GPIOx; + uint16_t PinOffset = 0; + + if (pin <= pin_A2) { + GPIOx = GPIOA; + PinOffset = pin; + } + else if (pin <= pin_C7) { + GPIOx = GPIOC; + PinOffset = (pin - 2); + } + else if (pin <= pin_D7) { + GPIOx = GPIOD; + PinOffset = (pin - 10); + } + else { + return; + } + + if (value) { + GPIOx-> BSHR |= 1 << PinOffset; + } + else { + GPIOx-> BSHR |= 1 << (16 + PinOffset); + } +} + + + +uint8_t digitalRead(uint8_t pin) { + GPIO_TypeDef * GPIOx; + uint16_t PinOffset = 0; + + if (pin <= pin_A2) { + GPIOx = GPIOA; + PinOffset = pin; + } + else if (pin <= pin_C7) { + GPIOx = GPIOC; + PinOffset = (pin - 2); + } + else if (pin <= pin_D7) { + GPIOx = GPIOD; + PinOffset = (pin - 10); + } + else { + return 0; + } + + int8_t result = (GPIOx->INDR >> PinOffset) & 1; + return result; +} + + + + + +void ADCinit() { + // select ADC clock source + // ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2 + RCC->CFGR0 &= ~(0x1F<<11); + + // enable clock to the ADC + RCC->APB2PCENR |= RCC_APB2Periph_ADC1; + + // Reset the ADC to init all regs + RCC->APB2PRSTR |= RCC_APB2Periph_ADC1; + RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1; + + // set sampling time for all inputs to 241 cycles + for (uint8_t i = Ain0_A2; i <= AinVcal; i++) { + ADCsetSampletime(i, Ast_241cy_default); + } + + // set trigger to software + ADC1->CTLR2 |= ADC_EXTSEL; + + // pre-clear conversion queue + ADC1->RSQR1 = 0; + ADC1->RSQR2 = 0; + ADC1->RSQR3 = 0; + + // power the ADC + ADCsetPower(1); +} + + + +void ADCsetSampletime(enum ANALOGinputs input, enum ANALOGsampletimes time) { + // clear + ADC1->SAMPTR2 &= ~(0b111)<<(3*input); + // set + ADC1->SAMPTR2 |= time<<(3*input); // 0:7 => 3/9/15/30/43/57/73/241 cycles +} + + + +void ADCsetPower(uint8_t enable) { + if (enable) { + ADC1->CTLR2 |= ADC_ADON; + if (enable == 1) { + // auto-cal each time after turning on the ADC + // can be overridden by calling with enable > 1. + ADCcalibrate(); + } + } + else { + ADC1->CTLR2 &= ~(ADC_ADON); + } +} + + + +void ADCcalibrate() { + // reset calibration + ADC1->CTLR2 |= ADC_RSTCAL; + while(ADC1->CTLR2 & ADC_RSTCAL); + + // calibrate + ADC1->CTLR2 |= ADC_CAL; + while(ADC1->CTLR2 & ADC_CAL); +} + + + +// inspired by arduinos analogRead() +uint16_t analogRead(enum ANALOGinputs input) { + // set mux to selected input + ADC1->RSQR3 = input; + + // may need a delay right here for the mux to actually finish switching?? + // Arduino inserts a full ms delay right here! + + // start sw conversion (auto clears) + ADC1->CTLR2 |= ADC_SWSTART; + + // wait for conversion complete + while(!(ADC1->STATR & ADC_EOC)); + + // get result + return ADC1->RDATAR; +} diff --git a/examples/GPIO_analogRead/wiring.h b/examples/GPIO_analogRead/wiring.h new file mode 100644 index 0000000000000000000000000000000000000000..5b822036cc1fad1314fc0b249ee1ae6bb8c6293b --- /dev/null +++ b/examples/GPIO_analogRead/wiring.h @@ -0,0 +1,99 @@ +#ifndef WIRING_H +#define WIRING_H + +#include "../../ch32v003fun/ch32v003fun.h" + + + +enum lowhigh { + low, + high, +}; + + + +enum GPIOports{ + port_A, + port_C, + port_D, + port_none, +}; + +enum GPIOpins{ + pin_A1, + pin_A2, + pin_C0, + pin_C1, + pin_C2, + pin_C3, + pin_C4, + pin_C5, + pin_C6, + pin_C7, + pin_D0, + pin_D1, + pin_D2, + pin_D3, + pin_D4, + pin_D5, + pin_D6, + pin_D7, + pin_none, +}; + +enum GPIOpinMode { + pinMode_I_floating, + pinMode_I_pullUp, //pull-mode + ODR(1) + pinMode_I_pullDown, //pull-mode + ODR(0) + pinMode_I_analog, + pinMode_O_pushPull, + pinMode_O_openDrain, + pinMode_O_pushPullMux, + pinMode_O_openDrainMux, +}; + +enum GPIOpinState { + pinState_nochange, + pinState_low, + pinState_high, +}; + +enum ANALOGinputs { + Ain0_A2, + Ain1_A1, + Ain2_C4, + Ain3_D2, + Ain4_D3, + Ain5_D5, + Ain6_D6, + Ain7_D4, + AinVref, + AinVcal, +}; + +enum ANALOGsampletimes { + Ast_3cy, + Ast_9cy, + Ast_15cy, + Ast_30cy, + Ast_43cy, + Ast_57cy, + Ast_73cy, + Ast_241cy_default, +}; + + +enum GPIOports getPort (enum GPIOpins pin); + +void portEnable(enum GPIOports port); +void pinMode(enum GPIOpins pin, enum GPIOpinMode mode); +void digitalWrite(enum GPIOpins pin, uint8_t value); +uint8_t digitalRead(uint8_t pin); + +void ADCinit(); +void ADCsetPower(uint8_t enable); +void ADCsetSampletime(enum ANALOGinputs input, enum ANALOGsampletimes time); +void ADCcalibrate(); +uint16_t analogRead(enum ANALOGinputs input); + +#endif // WIRING_H