diff --git a/.github/workflows/minichlink.yml b/.github/workflows/minichlink.yml new file mode 100644 index 0000000000000000000000000000000000000000..ce757ad9bd8769549d533db2140f992e21439653 --- /dev/null +++ b/.github/workflows/minichlink.yml @@ -0,0 +1,67 @@ +name: Build minichlink + +on: [push, pull_request] +# push: +# paths: +# - minichlink/** +# pull_request: +# paths: +# - minichlink/** +jobs: + build-minichlink: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-12, macos-11] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v3 + - name: Install Dependencies (Linux) + if: ${{ matrix.os == 'ubuntu-latest' }} + run: sudo apt-get update && sudo apt-get install -y build-essential make libusb-1.0-0-dev libudev-dev mingw-w64-x86-64-dev gcc-mingw-w64-x86-64 + # we don't need to brew install libusb on Mac, actually preinstalled on the runner! :) + - name: Build (Linux, Mac) + run: | + cd minichlink + make clean + make V=1 -j3 + # we cross-compile the Windows binaries from Linux + - name: Build (for Windows) + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + cd minichlink + OS=Windows_NT make clean + OS=Windows_NT make V=1 -j3 minichlink.exe + + - name: "Pack (Linux)" + if: ${{ matrix.os == 'ubuntu-latest' }} + run: tar czf minichlink.tar.gz -C minichlink minichlink minichlink.so 99-minichlink.rules + - name: "Pack (Mac)" + if: ${{ matrix.os == 'macos-12' || matrix.os == 'macos-11' }} + run: tar czf minichlink.tar.gz -C minichlink minichlink + # no packing needed for Windows as it's .exe only + + - name: "Upload minichlink (Linux)" + if: ${{ matrix.os == 'ubuntu-latest' }} + uses: actions/upload-artifact@v3 + with: + name: minichlink (Linux) + path: minichlink.tar.gz + - name: "Upload minichlink (MacOs 11)" + if: ${{ matrix.os == 'macos-11' }} + uses: actions/upload-artifact@v3 + with: + name: minichlink (MacOS 11) + path: minichlink.tar.gz + - name: "Upload minichlink (MacOs 12)" + if: ${{ matrix.os == 'macos-12' }} + uses: actions/upload-artifact@v3 + with: + name: minichlink (MacOS 12) + path: minichlink.tar.gz + - name: "Upload minichlink (Windows)" + if: ${{ matrix.os == 'ubuntu-latest' }} + uses: actions/upload-artifact@v3 + with: + name: minichlink (Windows) + path: minichlink/minichlink.exe \ No newline at end of file diff --git a/.gitignore b/.gitignore index a9445131bff9ac74f443e4f7865bb47e32f54769..f79293a9c0b686f0cb5fcd5f5760dfcea671c309 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ minichlink/minichlink minichlink/minichlink.so compile_commands.json +.clangd +.cache diff --git a/README.md b/README.md index 51f5dc2d84e4edcada885326421857ba424124a1..95d0ace963f9abe23b676beee54473ed2a37da40 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,8 @@ Via gdbserver built into minichlink! It works with `gdb-multiarch` as well as i ## System Prep -For installation instructions, see the [https://github.com/cnlohr/ch32v003fun/wiki/Installation](wiki page here) +For installation instructions, see the [wiki page here](https://github.com/cnlohr/ch32v003fun/wiki/Installation) + You can use the pre-compiled minichlink or go to minichlink dir and `make` it. diff --git a/ch32v003fun/ch32v003fun.c b/ch32v003fun/ch32v003fun.c index 3db230e278ae919ad2949855dda25c9299b9711a..6e6e60d4cd857cfa262c115b104370d6eef845c1 100644 --- a/ch32v003fun/ch32v003fun.c +++ b/ch32v003fun/ch32v003fun.c @@ -780,10 +780,10 @@ void handle_reset() " li a0, 0x80\n\ csrw mstatus, a0\n\ li a3, 0x3\n\ - csrw 0x804, a3\n\ la a0, InterruptVector\n\ or a0, a0, a3\n\ - csrw mtvec, a0\n" ); + csrw mtvec, a0\n" + : : : "a0", "a3", "memory"); // Careful: Use registers to prevent overwriting of self-data. // This clears out BSS. @@ -810,8 +810,11 @@ asm volatile( #ifdef CPLUSPLUS // Call __libc_init_array function " call %0 \n\t" -: : "i" (__libc_init_array) : +: : "i" (__libc_init_array) +#else +: : #endif +: "a0", "a1", "a2", "a3", "memory" ); SETUP_SYSTICK_HCLK diff --git a/ch32v003fun/ch32v003fun.h b/ch32v003fun/ch32v003fun.h index 10d5fc22529216ca898f9b61e9c957712743bff4..1634bcb8e1d59b10b564fba9e94370168f4dddee 100644 --- a/ch32v003fun/ch32v003fun.h +++ b/ch32v003fun/ch32v003fun.h @@ -4633,6 +4633,47 @@ RV_STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint8_t priority) NVIC->IPRIOR[(uint32_t)(IRQn)] = priority; } +/********************************************************************* + * SUSPEND ALL INTERRUPTS EXCEPT + * The following 3 functions serve to suspend all interrupts, except for the one you momentarily need. + * The purpose of this is to not disturb the one interrupt of interest and let it run unimpeded. + * procedure: + * 1. save the enabled IRQs: uint32_t IRQ_backup = NVIC_get_enabled_IRQs(); + * 2. disable all IRQs: NVIC_clear_all_IRQs_except(IRQ_of_interest); + * 3. restore the previously enabled IRQs: NVIC_restore_IRQs(IRQ_backup); + * + * bit layout of the IRQ backup + * bit 0 | 1 | 2 | 3 | 4 | 5 | 6 .. 22 | 23 .. 28 + * IRQn 2 | 3 | 12 | res | 14 | res | 16 .. 31 | 32 .. 38 + * IRQn 2 and 3 aren't actually user-settable (see RM). + * + * Specifying an invalid IRQn_to_keep like 0 will disable all interrupts. + */ + +RV_STATIC_INLINE uint32_t NVIC_get_enabled_IRQs() +{ + return ( ((NVIC->ISR[0] >> 2) & 0b11) | ((NVIC->ISR[0] >> 12) << 2) | ((NVIC->ISR[1] & 0b1111111) << 23) ); +} + +RV_STATIC_INLINE void NVIC_clear_all_IRQs_except(uint8_t IRQn_to_keep) +{ + if (!(IRQn_to_keep >> 5)) { // IRQn_to_keep < 32 + NVIC->IRER[0] = (~0) & (~(1 << IRQn_to_keep)); + NVIC->IRER[1] = (~0); + } + else { + IRQn_to_keep = IRQn_to_keep >> 5; + NVIC->IRER[0] = (~0); + NVIC->IRER[1] = (~0) & (~(1 << IRQn_to_keep)); + } +} + +RV_STATIC_INLINE void NVIC_restore_IRQs(uint32_t old_state) +{ + NVIC->IENR[0] = (old_state >> 2) << 12; + NVIC->IENR[1] = old_state >> 23; +} + /********************************************************************* * @fn __WFI * diff --git a/ch32v003fun/ch32v003fun.mk b/ch32v003fun/ch32v003fun.mk index 15330b3b62a6a343138612e7198d59cf3a89f9ca..278b7361d74b728961d7fe4678ce8931aca58ee3 100644 --- a/ch32v003fun/ch32v003fun.mk +++ b/ch32v003fun/ch32v003fun.mk @@ -52,9 +52,12 @@ gdbserver : clangd : make clean bear -- make build + @echo "CompileFlags:" > .clangd + @echo " Remove: [-march=*, -mabi=*]" >> .clangd clangd_clean : - rm -f compile_commands.json + rm -f compile_commands.json .clangd + rm -rf .cache FLASH_COMMAND?=$(MINICHLINK)/minichlink -w $< $(WRITE_SECTION) -b diff --git a/examples/GPIO/GPIO.c b/examples/GPIO/GPIO.c index 148aa0198a1710d1b298fbf2f6c038201a90b6e6..a761850c339f8e445fc38585064b58a92c73a6ec 100644 --- a/examples/GPIO/GPIO.c +++ b/examples/GPIO/GPIO.c @@ -1,47 +1,199 @@ -// blink, but with arduino-like HAL -// Could be defined here, or in the processor defines. +// 2023-06-07 recallmenot + +#define DEMO_GPIO_blink 1 +#define DEMO_GPIO_out 0 +#define DEMO_GPIO_in_btn 0 +#define DEMO_ADC_bragraph 0 +#define DEMO_PWM_dayrider 0 + +#if ((DEMO_GPIO_blink + DEMO_GPIO_out + DEMO_GPIO_in_btn + DEMO_ADC_bragraph + DEMO_PWM_dayrider) > 1 \ + || (DEMO_GPIO_blink + DEMO_GPIO_out + DEMO_GPIO_in_btn + DEMO_ADC_bragraph + DEMO_PWM_dayrider) < 1) +#error "please enable ONE of the demos by setting it to 1 and the others to 0" +#endif + + #define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK #include "ch32v003fun.h" -#include "wiring.h" + +#include "ch32v003_GPIO_branchless.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); +#if DEMO_GPIO_blink == 1 + GPIO_portEnable(GPIO_port_C); + GPIO_portEnable(GPIO_port_D); + // GPIO D0 Push-Pull + GPIO_pinMode(GPIO_port_D, 0, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + // GPIO D4 Push-Pull + GPIO_pinMode(GPIO_port_D, 4, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + // GPIO C0 Push-Pull + GPIO_pinMode(GPIO_port_C, 0, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); +#elif DEMO_GPIO_out == 1 + GPIO_portEnable(GPIO_port_C); + GPIO_portEnable(GPIO_port_D); + // GPIO D4 Push-Pull + GPIO_pinMode(GPIO_port_D, 4, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + // GPIO C0 - C7 Push-Pull + for (int i = 0; i <= 7; i++) { + GPIO_pinMode(GPIO_port_C, i, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); } - +#elif DEMO_GPIO_in_btn == 1 + GPIO_portEnable(GPIO_port_C); + GPIO_portEnable(GPIO_port_D); + // GPIO D4 Push-Pull + GPIO_pinMode(GPIO_port_D, 3, GPIO_pinMode_I_pullUp, GPIO_SPEED_IN); + // GPIO C0 - C7 Push-Pull + for (int i = 0; i <= 7; i++) { + GPIO_pinMode(GPIO_port_C, i, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + } +#elif DEMO_ADC_bragraph == 1 + GPIO_portEnable(GPIO_port_C); + GPIO_portEnable(GPIO_port_D); // GPIO D4 Push-Pull - pinMode(pin_D4, pinMode_O_pushPull); + GPIO_pinMode(GPIO_port_D, 4, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + // GPIO D6 analog in + GPIO_pinMode(GPIO_port_D, 6, GPIO_pinMode_I_analog, GPIO_SPEED_IN); + // GPIO C0 - C7 Push-Pull + for (int i = 0; i<= 7; i++) { + GPIO_pinMode(GPIO_port_C, i, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + } + GPIO_ADCinit(); +#elif DEMO_PWM_dayrider == 1 + //SetupUART( UART_BRR ); + GPIO_portEnable(GPIO_port_C); + GPIO_portEnable(GPIO_port_D); + // GPIO D4 Push-Pull + GPIO_pinMode(GPIO_port_D, 4, GPIO_pinMode_O_pushPull, GPIO_Speed_10MHz); + // GPIO D6 analog in + GPIO_pinMode(GPIO_port_D, 6, GPIO_pinMode_I_analog, GPIO_SPEED_IN); + // GPIO C0 - C7 Push-Pull + for (int i = 0; i<= 7; i++) { + GPIO_pinMode(GPIO_port_C, i, GPIO_pinMode_O_pushPullMux, GPIO_Speed_50MHz); + } + GPIO_tim2_map(GPIO_tim2_output_set_1__C5_C2_D2_C1); + GPIO_tim2_init(); + GPIO_tim2_enableCH(4); + GPIO_tim2_enableCH(2); + GPIO_tim2_enableCH(1); + GPIO_tim1_map(GPIO_tim1_output_set_0__D2_A1_C3_C4__D0_A2_D1); + GPIO_tim1_init(); + GPIO_tim1_enableCH(3); + GPIO_tim1_enableCH(4); +#endif + + while (1) { - // Turn on pins - digitalWrite(pin_C0, high); - digitalWrite(pin_D4, high); +#if DEMO_GPIO_blink == 1 + GPIO_digitalWrite(GPIO_port_D, 0, high); + GPIO_digitalWrite(GPIO_port_D, 4, high); + GPIO_digitalWrite(GPIO_port_C, 0, high); + Delay_Ms( 250 ); + GPIO_digitalWrite(GPIO_port_D, 0, low); + GPIO_digitalWrite(GPIO_port_D, 4, low); + GPIO_digitalWrite(GPIO_port_C, 0, low); + Delay_Ms( 250 ); +#elif DEMO_GPIO_out == 1 + GPIO_digitalWrite(GPIO_port_D, 4, low); + Delay_Ms(1000); + GPIO_digitalWrite(GPIO_port_D, 4, 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); + for (int i = 0; i <= 7; i++) { + GPIO_digitalWrite_hi(GPIO_port_C, i); Delay_Ms(50); } - for (int i = pin_C7; i >= pin_C0; i--) { - digitalWrite(i, low); + for (int i = 7; i >= 0; i--) { + GPIO_digitalWrite_lo(GPIO_port_C, i); Delay_Ms(50); } - Delay_Ms(250); - count++; +#elif DEMO_GPIO_in_btn == 1 + uint8_t button_is_pressed = !GPIO_digitalRead(GPIO_port_D, 3); + static uint8_t leds_to_turn_on; + if (button_is_pressed && leds_to_turn_on < 8) { + leds_to_turn_on++; + } + else if (!button_is_pressed && leds_to_turn_on > 0) { + leds_to_turn_on--; + } + uint8_t led_i = 0; + for (int i = 0; i<= 7; i++) { + if (led_i < leds_to_turn_on) { + GPIO_digitalWrite_hi(GPIO_port_C, i); + } + else { + GPIO_digitalWrite_lo(GPIO_port_C, i); + } + led_i++; + } + Delay_Ms(50); +#elif DEMO_ADC_bragraph == 1 + GPIO_digitalWrite(GPIO_port_D, 4, high); + uint16_t analog_result = GPIO_analogRead(GPIO_Ain6_D6); + analog_result = analog_result > 10 ? + (analog_result < ((1 << 10) - 42) ? analog_result + 42 : (1 << 10)) + : 0; + uint8_t leds_to_turn_on = analog_result >> 7; + uint8_t led_i = 0; + for (int i = 0; i<= 7; i++) { + if (led_i < leds_to_turn_on) { + GPIO_digitalWrite_hi(GPIO_port_C, i); + } + else { + GPIO_digitalWrite_lo(GPIO_port_C, i); + } + led_i++; + } + GPIO_digitalWrite(GPIO_port_D, 4, low); + Delay_Ms(50); +#elif DEMO_PWM_dayrider == 1 + #define NUMLEDS 5 + #define TICKS_NEXT 149 // lower = faster scanning + #define TICK_i 2143 // lower = faster fade-out + GPIO_digitalWrite(GPIO_port_D, 4, high); + static uint8_t led_focus = 0; + static uint8_t direction = 0; + + static uint16_t led_PW[NUMLEDS]; + + static uint32_t tick; + + for (uint8_t i = 0; i < NUMLEDS; i++) { + if ((i == led_focus) && ((tick % TICKS_NEXT) == 0)) { + led_PW[i] = 1023; + //printf("focus %u, tick %u, direction %u\r\n", led_focus, tick, direction); + if (direction == 0) { + led_focus++; + if (led_focus >= (NUMLEDS - 1)) { + direction = 1; + } + } + else { + led_focus--; + if (led_focus == 0) { + direction = 0; + } + } + tick++; + } + else { + led_PW[i] = (led_PW[i] > 2) ? (led_PW[i] - 3) : 0; + } + } + GPIO_tim2_analogWrite(4, led_PW[0]); + GPIO_tim2_analogWrite(2, led_PW[1]); + GPIO_tim1_analogWrite(3, led_PW[2]); + GPIO_tim1_analogWrite(4, led_PW[3]); + GPIO_tim2_analogWrite(1, led_PW[4]); + GPIO_digitalWrite(GPIO_port_D, 4, low); + tick++; + Delay_Us(TICK_i); +#endif } } diff --git a/examples/GPIO/Makefile b/examples/GPIO/Makefile index 87347371d38e65e7c05f84225b553f957d5d1eca..7e60f814bb33181340eca2ad204cee695088fcc8 100644 --- a/examples/GPIO/Makefile +++ b/examples/GPIO/Makefile @@ -3,7 +3,7 @@ all : flash TARGET:=GPIO CFLAGS+=-DTINYVECTOR -ADDITIONAL_C_FILES+=../../extralibs/wiring.c +CFLAGS+=-DSTDOUT_UART include ../../ch32v003fun/ch32v003fun.mk diff --git a/examples/GPIO/README.md b/examples/GPIO/README.md index a6c23799ff7e6df932b10774273f86094daac8b3..33ac22d79daecb0e64c0c96c8df2f0d50450934a 100644 --- a/examples/GPIO/README.md +++ b/examples/GPIO/README.md @@ -1,14 +1,22 @@ -# GPIO Libaray -On the shoulders of the Blink example, this Arduino-like GPIO library stands. +# one GPIO libaray to rule them all -All pins are adressable as outputs, inputs, with pull-up, etc. -The pins are in an enum, so you can call them by their name and iterate over them. +This Arduino-like GPIO library offers + * digital IO + * analog-to-digital + * digital-to-analog (PWM) -It's your responsibility to not blow up a pin. -Only use one pin for one thing and you should be fine. +Great care has been taken to make the resulting code as fast and tiny as possible. Let the compiler suffer! +Hand-written blink compiles to 500 bytes, blink using this library compiles to 504 bytes! # GPIO Example -Connect LED + 1k resistor to each pin (C0 to C7 and D4) and GND. -Marvel at the colorful glory. -https://user-images.githubusercontent.com/104343143/231585338-725f1197-dfa0-484d-8707-f0824af80b7e.mp4 +Connect LED + 1k resistor to each LED pin (C0 to C7 and D4) and GND. +Connect a button to GND and D3. +Connect a 10k pot between GND and VCC, wiper to D6. + +The desired demo may be selected in GPIO.c by setting it to 1 and the others to 0. +Marvel at the colorful glory. + + + +https://github.com/recallmenot/ch32v003fun/assets/104343143/afb4027d-a609-467a-96c5-0cc3283366a4 diff --git a/examples/GPIO_analogRead/GPIO_analogRead.c b/examples/GPIO_analogRead/GPIO_analogRead.c deleted file mode 100644 index 76df1facacafbbf45260486402d91266f1f93d7a..0000000000000000000000000000000000000000 --- a/examples/GPIO_analogRead/GPIO_analogRead.c +++ /dev/null @@ -1,47 +0,0 @@ -// 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/README.md b/examples/GPIO_analogRead/README.md deleted file mode 100644 index 3b9e1b6ca6799c77987e830cf7ea868d095601b5..0000000000000000000000000000000000000000 --- a/examples/GPIO_analogRead/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# 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/blink/blink.bin b/examples/blink/blink.bin index 9d2f625331af46afe752e4c674ea3ca6a67b5f49..b2c6174d1dac1759b26f345f4dde42f709e0e59e 100755 Binary files a/examples/blink/blink.bin and b/examples/blink/blink.bin differ diff --git a/examples/GPIO_analogRead/Makefile b/examples/tim2_pwm_remap/Makefile similarity index 50% rename from examples/GPIO_analogRead/Makefile rename to examples/tim2_pwm_remap/Makefile index a965d90a0e3e624237b8b7234cfce7e09475c44e..36df10e860825f2e0d005debbe3cbcb791b2b591 100644 --- a/examples/GPIO_analogRead/Makefile +++ b/examples/tim2_pwm_remap/Makefile @@ -1,9 +1,7 @@ all : flash -TARGET:=GPIO_analogRead +TARGET:=tim2_pwm_remap -CFLAGS+=-DTINYVECTOR -ADDITIONAL_C_FILES+=../../extralibs/wiring.c include ../../ch32v003fun/ch32v003fun.mk diff --git a/examples/tim2_pwm_remap/tim2_pwm_remap.c b/examples/tim2_pwm_remap/tim2_pwm_remap.c new file mode 100644 index 0000000000000000000000000000000000000000..1d2c4bbfa43fab930c8ccdbd69b776b7e84e33dd --- /dev/null +++ b/examples/tim2_pwm_remap/tim2_pwm_remap.c @@ -0,0 +1,175 @@ +/* + * Example for using AFIO to remap peripheral outputs to alternate configuration + * 06-01-2023 B. Roy, based on previous work by: + * 03-28-2023 E. Brombaugh + * 05-29-2023 recallmenot adapted from Timer1 to Timer2 + * + * Usage: + * Connect LEDs between PD3 and GND, PD4 and GND, PC1 and GND, and PC7 and GND + * Observe activity on PD3 and PD4, then activity on PC1 and PC7, and back + * + * Nutshell: + * 1. Ensure you're providing a clock to the AFIO peripheral! Save yourself an + * hour of troubleshooting! + * RCC->APB2PCENR |= RCC_APB2Periph_AFIO + * 2. Apply the remapping configuration bits to the AFIO register: + * AFIO->PCFR1 |= AFIO_PCFR1_TIM2_REMAP_FULLREMAP + * 3. Go on about your business. + * + * / + + +Timer 2 pin mappings by AFIO->PCFR1 + 00 AFIO_PCFR1_TIM2_REMAP_NOREMAP + D4 T2CH1ETR + D3 T2CH2 + C0 T2CH3 + D7 T2CH4 --note: requires disabling nRST in opt + 01 AFIO_PCFR1_TIM2_REMAP_PARTIALREMAP1 + C5 T2CH1ETR_ + C2 T2CH2_ + D2 T2CH3_ + C1 T2CH4_ + 10 AFIO_PCFR1_TIM2_REMAP_PARTIALREMAP2 + C1 T2CH1ETR_ + D3 T2CH2 + C0 T2CH3 + D7 T2CH4 --note: requires disabling nRST in opt + 11 AFIO_PCFR1_TIM2_REMAP_FULLREMAP + C1 T2CH1ETR_ + C7 T2CH2_ + D6 T2CH3_ + D5 T2CH4_ +*/ + +// 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> + +/****************************************************************************************** + * initialize TIM2 for PWM + ******************************************************************************************/ +void t2pwm_init( void ) +{ + // Enable GPIOC, GPIOD, TIM2, and AFIO *very important!* + RCC->APB2PCENR |= RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC; + RCC->APB1PCENR |= RCC_APB1Periph_TIM2; + + // PD4 is T2CH1, 10MHz Output alt func, push-pull (also works in oepn drain OD_AF) + GPIOD->CFGLR &= ~(0xf<<(4*4)); + GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4); + + // PD3 is T2CH2, 10MHz Output alt func, push-pull (also works in oepn drain OD_AF) + GPIOD->CFGLR &= ~(0xf<<(4*3)); + GPIOD->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*3); + + // PC1 is T2CH1_, 10MHz Output alt func, push-pull (also works in oepn drain OD_AF) + GPIOC->CFGLR &= ~(0xf<<(4*1)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*1); + + // PC7 is T2CH2_, 10MHz Output alt func, push-pull (also works in oepn drain OD_AF) + GPIOC->CFGLR &= ~(0xf<<(4*7)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*7); + + // Reset TIM2 to init all regs + RCC->APB1PRSTR |= RCC_APB1Periph_TIM2; + RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2; + + // SMCFGR: default clk input is CK_INT + // set TIM2 clock prescaler divider + TIM2->PSC = 0x0000; + // set PWM total cycle width + TIM2->ATRLR = 255; + + // for channel 1 and 2, let CCxS stay 00 (output), set OCxM to 110 (PWM I) + // enabling preload causes the new pulse width in compare capture register only to come into effect when UG bit in SWEVGR is set (= initiate update) (auto-clears) + TIM2->CHCTLR1 |= TIM_OC1M_2 | TIM_OC1M_1 | TIM_OC1PE; + TIM2->CHCTLR1 |= TIM_OC2M_2 | TIM_OC2M_1 | TIM_OC2PE; + + // CTLR1: default is up, events generated, edge align + // enable auto-reload of preload + TIM2->CTLR1 |= TIM_ARPE; + + // Enable CH1 output, positive pol + TIM2->CCER |= TIM_CC1E | TIM_CC1P; + // Enable CH2 output, positive pol + TIM2->CCER |= TIM_CC2E | TIM_CC2P; + + // initialize counter + TIM2->SWEVGR |= TIM_UG; + + // Enable TIM2 + TIM2->CTLR1 |= TIM_CEN; +} + +/***************************************************************************************** + * set timer channel PW + *****************************************************************************************/ +void t2pwm_setpw(uint8_t chl, uint16_t width) +{ + switch(chl&3) + { + case 0: TIM2->CH1CVR = width; break; + case 1: TIM2->CH2CVR = width; break; + case 2: TIM2->CH3CVR = width; break; + case 3: TIM2->CH4CVR = width; break; + } + TIM2->SWEVGR |= TIM_UG; // load new value in compare capture register +} + +/***************************************************************************************** + * Remap T1CH1/T1CH2 from PD4/PD3 to PC1/PC7 + * + * Can remap on-the fly; no need to re-initialize timers, reset GPIO mode/config, etc. + * + * Leaves the previous pins configured as 'alternate function' mode - i.e. disconnected + * from the GPIO peripheral, and floating. + * + *****************************************************************************************/ +void ToggleRemap(void) { + if(AFIO->PCFR1 & AFIO_PCFR1_TIM2_REMAP_FULLREMAP) { + AFIO->PCFR1 &= AFIO_PCFR1_TIM2_REMAP_NOREMAP; //clear remapping bits + printf("Standard Mapping!\r\n"); + } + else { + AFIO->PCFR1 |= AFIO_PCFR1_TIM2_REMAP_FULLREMAP; //set fullremap mode + printf("Full Remapping!\r\n"); + } +}; + +/***************************************************************************************** + * entry + *****************************************************************************************/ +int main() +{ + uint32_t count = 0; + + SystemInit48HSI(); + + // start serial @ default 115200bps + SetupDebugPrintf(); + + Delay_Ms( 100 ); + printf("\r\r\n\ntim2_pwm example, with remap\n\r"); + + // init TIM2 for PWM + printf("initializing tim2..."); + t2pwm_init(); + printf("done.\n\r"); + + printf("looping...\n\r"); + + while(1) + { + for(;count<255;count++){ + t2pwm_setpw(0, count); // Chl 1 + t2pwm_setpw(1, 255-count); // Chl 2 180° out-of-phase + Delay_Ms( 5 ); + } + count = 0; + ToggleRemap(); + } +} diff --git a/extralibs/ch32v003_GPIO_branchless.h b/extralibs/ch32v003_GPIO_branchless.h new file mode 100644 index 0000000000000000000000000000000000000000..455716b669f8438108fccdfd0c9f27feb82c263f --- /dev/null +++ b/extralibs/ch32v003_GPIO_branchless.h @@ -0,0 +1,474 @@ +// 2023-06-07 recallmenot + +//######## necessities + +// include guards +#ifndef CH32V003_GPIO_BR_H +#define CH32V003_GPIO_BR_H + +// includes +#include <stdint.h> //uintN_t support +#include "../ch32v003fun/ch32v003fun.h" + + + +/*######## library description +This is a speedy and light GPIO library due to + static inlining of most functions + compile-time abstraction + branchless where it counts +*/ + + + +/*######## library usage and configuration + +first, enable the desired port. + +digital usage is quite Arduino-like: +pinMode +digitalWrite +digitalWrite_lo +digitalWrite_hi +digitalRead + + + +analog-to-digital usage is almost Arduino-like: +pinMode +ADCinit +analogRead + +By default, this library inserts a delay of 300 µs between configuration of the ADC input mux and the time the conversion starts. +This serves to counteract the high input impedance of the ADC, especially if it is increased by external resistors. +The input impedance of port A appears to be especially large. +You may modify it to your liking using the following define before including this library. +#define GPIO_ADC_MUX_DELAY 1200 + +GPIO_ADC_sampletime controls the time each conversion is granted, by default it is GPIO_ADC_sampletime_241cy_default, all options originate from the GPIO_ADC_sampletimes enum. +To alter it, you have 3 options: + * `#define GPIO_ADC_sampletime GPIO_ADC_sampletime_43cy` before including this library + * call the GPIO_ADC_set_sampletime function-like macro to momentarrily set it for one channel + * call the GPIO_ADC_set_sampletimes_all function-like macro to to momentarrily set it for all channels + +You may also disable the ADC to save power between infrequent measurements. + + + +digital-to-analog (PWM) usage is quite different: +pinMode +GPIO_timX_map +GPIO_timX_init +GPIO_timX_enableCH +GPIO_timX_analogWrite + +This is due to the fact that the CH32V003 has 2 timers, which each can be connected to 4 pre-defined sets (mappings) of pins. +Then you address the 4 channels of the timers, instead of the pins. + +By default, the timers will be configured to count up to 2^10, which is 10 bits or 1024 discrete steps. +You may alter this to suit your needs, for example to an 8 bit resolution (256 discrete steps). +Insert this before including this library: +#define GPIO_timer_resolution (1 << 8) + +By default, the timers will operate with a clock prescaler of 2 but you may choose 1 or 4 if you wish to alter the speed. +Insert this before including this library: +#define GPIO_timer_prescaler TIM_CKD_DIV1; // APB_CLOCK / 1024 / 1 = 46.9kHz + +You may calculate the base frequency of the timer (the rate of repetition of your signal) like follows: +fpwm = APB_CLOCK / resolution / prescaler +This puts the defaults at an inaudible 23.4kHz. +The higher the frequency, the greater the EMI radiation will be. +With low frequencies, say below 1000Hz, LEDs may exhibit perceivable flicker. + +*/ + + + +//######## ports, pins and states: use these for the functions below! + +enum GPIO_port_n { + GPIO_port_A = 0b00, + GPIO_port_C = 0b10, + GPIO_port_D = 0b11, +}; + +enum GPIO_pinModes { + GPIO_pinMode_I_floating, + GPIO_pinMode_I_pullUp, + GPIO_pinMode_I_pullDown, + GPIO_pinMode_I_analog, + GPIO_pinMode_O_pushPull, + GPIO_pinMode_O_openDrain, + GPIO_pinMode_O_pushPullMux, + GPIO_pinMode_O_openDrainMux, +}; + +enum lowhigh { + low, + high, +}; + +// analog inputs +enum GPIO_analog_inputs { + GPIO_Ain0_A2, + GPIO_Ain1_A1, + GPIO_Ain2_C4, + GPIO_Ain3_D2, + GPIO_Ain4_D3, + GPIO_Ain5_D5, + GPIO_Ain6_D6, + GPIO_Ain7_D4, + GPIO_AinVref, + GPIO_AinVcal, +}; + +// how many cycles the ADC shall sample the input for (speed vs precision) +enum GPIO_ADC_sampletimes { + GPIO_ADC_sampletime_3cy, + GPIO_ADC_sampletime_9cy, + GPIO_ADC_sampletime_15cy, + GPIO_ADC_sampletime_30cy, + GPIO_ADC_sampletime_43cy, + GPIO_ADC_sampletime_57cy, + GPIO_ADC_sampletime_73cy, + GPIO_ADC_sampletime_241cy_default, +}; + +enum GPIO_tim1_output_sets { + GPIO_tim1_output_set_0__D2_A1_C3_C4__D0_A2_D1, + GPIO_tim1_output_set_1__C6_C7_C0_D3__C3_C4_D1, + GPIO_tim1_output_set_2__D2_A1_C3_C4__D0_A2_D1, + GPIO_tim1_output_set_3__C4_C7_C5_D4__C3_D2_C6, +}; + +enum GPIO_tim2_output_sets { + GPIO_tim2_output_set_0__D4_D3_C0_D7, + GPIO_tim2_output_set_1__C5_C2_D2_C1, + GPIO_tim2_output_set_2__C1_D3_C0_D7, + GPIO_tim2_output_set_3__C1_C7_D6_D5, +}; + + + +//######## interface function overview: use these! +// most functions have been reduced to function-like macros, actual definitions downstairs + +// setup +#define GPIO_portEnable(GPIO_port_n) +#define GPIO_pinMode(GPIO_port_n, pin, pinMode, GPIO_Speed) + +// digital +#define GPIO_digitalWrite_hi(GPIO_port_n, pin) +#define GPIO_digitalWrite_lo(GPIO_port_n, pin) +#define GPIO_digitalWrite(GPIO_port_n, pin, lowhigh) +#define GPIO_digitalWrite_branching(GPIO_port_n, pin, lowhigh) +#define GPIO_digitalRead(GPIO_port_n, pin) + +// analog to digital +static inline void GPIO_ADCinit(); +#define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime) +#define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime) +#define GPIO_ADC_set_power(enable) +#define GPIO_ADC_calibrate() +static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input); + +// digital to analog (PWM) +#define GPIO_tim1_map(GPIO_tim1_output_set) +#define GPIO_tim2_map(GPIO_tim2_output_set) +static inline void GPIO_tim1_init(); +static inline void GPIO_tim2_init(); +#define GPIO_tim1_enableCH(channel) +#define GPIO_tim2_enableCH(channel) +#define GPIO_tim1_analogWrite(channel, value) +#define GPIO_tim2_analogWrite(channel, value) + + + +//######## internal function declarations + + + +//######## internal variables + + + +//######## preprocessor macros + +#define CONCAT(a, b) a ## b +#define CONCAT_INDIRECT(a, b) CONCAT(a, b) + +#define GPIOx_to_port_n2(GPIOx) GPIOx_to_port_n_##GPIOx +#define GPIOx_to_port_n(GPIOx) GPIOx_to_port_n2(GPIOx) +#define GPIOx_to_port_n_GPIO_port_A 0b00 +#define GPIOx_to_port_n_GPIO_port_C 0b10 +#define GPIOx_to_port_n_GPIO_port_D 0b11 + +#define GPIO_port_n_to_GPIOx2(GPIO_port_n) GPIO_port_n_to_GPIOx_##GPIO_port_n +#define GPIO_port_n_to_GPIOx(GPIO_port_n) GPIO_port_n_to_GPIOx2(GPIO_port_n) +#define GPIO_port_n_to_GPIOx_GPIO_port_A GPIOA +#define GPIO_port_n_to_GPIOx_GPIO_port_C GPIOC +#define GPIO_port_n_to_GPIOx_GPIO_port_D GPIOD + +#define GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph_##GPIO_port_n +#define GPIO_port_n_to_RCC_APB2Periph(GPIO_port_n) GPIO_port_n_to_RCC_APB2Periph2(GPIO_port_n) +#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_A RCC_APB2Periph_GPIOA +#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_C RCC_APB2Periph_GPIOC +#define GPIO_port_n_to_RCC_APB2Periph_GPIO_port_D RCC_APB2Periph_GPIOD + +#define GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG_##GPIO_pinMode(GPIO_Speed) +#define GPIO_pinMode_to_CFG(GPIO_pinMode, GPIO_Speed) GPIO_pinMode_to_CFG2(GPIO_pinMode, GPIO_Speed) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_floating(GPIO_Speed) (GPIO_SPEED_IN | GPIO_CNF_IN_FLOATING) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullUp(GPIO_Speed) (GPIO_SPEED_IN | GPIO_CNF_IN_PUPD) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_pullDown(GPIO_Speed) (GPIO_SPEED_IN | GPIO_CNF_IN_PUPD) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_I_analog(GPIO_Speed) (GPIO_SPEED_IN | GPIO_CNF_IN_ANALOG) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPull(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrain(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_OD) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_pushPullMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_OUT_PP_AF) +#define GPIO_pinMode_to_CFG_GPIO_pinMode_O_openDrainMux(GPIO_Speed) (GPIO_Speed | GPIO_CNF_IN_ANALOG) + +#define GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n, pin) GPIO_pinMode_set_PUPD_##GPIO_pinMode(GPIO_port_n, pin) +#define GPIO_pinMode_set_PUPD(GPIO_pinMode, GPIO_port_n, pin) GPIO_pinMode_set_PUPD2(GPIO_pinMode, GPIO_port_n, pin) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_floating(GPIO_port_n, pin) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullUp(GPIO_port_n, pin) GPIO_port_n_to_GPIOx(GPIO_port_n)->BSHR = (1 << pin) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_pullDown(GPIO_port_n, pin) GPIO_port_n_to_GPIOx(GPIO_port_n)->BSHR = (1 << (pin + 16)) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_I_analog(GPIO_port_n, pin) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPull(GPIO_port_n, pin) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_openDrain(GPIO_port_n, pin) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_pushPullMux(GPIO_port_n, pin) +#define GPIO_pinMode_set_PUPD_GPIO_pinMode_O_openDrainMux(GPIO_port_n, pin) + +#if !defined(GPIO_ADC_MUX_DELAY) +#define GPIO_ADC_MUX_DELAY 200 +#endif + +#if !defined(GPIO_ADC_sampletime) +#define GPIO_ADC_sampletime GPIO_ADC_sampletime_241cy_default +#endif + +#if !defined(GPIO_timer_resolution) +#define GPIO_timer_resolution (1 << 10) +#endif + +#if !defined(GPIO_timer_prescaler) +#define GPIO_timer_prescaler TIM_CKD_DIV2 // APB_CLOCK / 1024 / 2 = 23.4kHz +#endif + +// maintenance define +#if !defined(SYSTEM_CORE_CLOCK) +#define SYSTEM_CORE_CLOCK 48000000 +#define APB_CLOCK SYSTEM_CORE_CLOCK +#endif + + + +//######## define requirements / maintenance defines + + + +//######## small function definitions, static inline + + +#undef GPIO_portEnable +#define GPIO_portEnable(GPIO_port_n) RCC->APB2PCENR |= GPIO_port_n_to_RCC_APB2Periph(GPIO_port_n); + +#undef GPIO_pinMode +#define GPIO_pinMode(GPIO_port_n, pin, pinMode, GPIO_Speed) ({ \ + GPIO_port_n_to_GPIOx(GPIO_port_n)->CFGLR &= ~(0b1111 << (4 * pin)); \ + GPIO_port_n_to_GPIOx(GPIO_port_n)->CFGLR |= (GPIO_pinMode_to_CFG(pinMode, GPIO_Speed) << (4 * pin)); \ + GPIO_pinMode_set_PUPD(pinMode, GPIO_port_n, pin); \ +}) + +#undef GPIO_digitalWrite_hi +#define GPIO_digitalWrite_hi(GPIO_port_n, pin) GPIO_port_n_to_GPIOx(GPIO_port_n)->BSHR = (1 << pin) +#undef GPIO_digitalWrite_lo +#define GPIO_digitalWrite_lo(GPIO_port_n, pin) GPIO_port_n_to_GPIOx(GPIO_port_n)->BSHR = (1 << (pin + 16)) + +#undef GPIO_digitalWrite +#define GPIO_digitalWrite2(GPIO_port_n, pin, lowhigh) GPIO_digitalWrite_##lowhigh(GPIO_port_n, pin) +#define GPIO_digitalWrite(GPIO_port_n, pin, lowhigh) GPIO_digitalWrite2(GPIO_port_n, pin, lowhigh) +#define GPIO_digitalWrite_low(GPIO_port_n, pin) GPIO_digitalWrite_lo(GPIO_port_n, pin) +#define GPIO_digitalWrite_0(GPIO_port_n, pin) GPIO_digitalWrite_lo(GPIO_port_n, pin) +#define GPIO_digitalWrite_high(GPIO_port_n, pin) GPIO_digitalWrite_hi(GPIO_port_n, pin) +#define GPIO_digitalWrite_1(GPIO_port_n, pin) GPIO_digitalWrite_hi(GPIO_port_n, pin) + +#undef GPIO_digitalWrite_branching +#define GPIO_digitalWrite_branching(GPIO_port_n, pin, lowhigh) (lowhigh ? GPIO_digitalWrite_hi(GPIO_port_n, pin) : GPIO_digitalWrite_lo(GPIO_port_n, pin)) + +#undef GPIO_digitalRead +#define GPIO_digitalRead(GPIO_port_n, pin) ((GPIO_port_n_to_GPIOx(GPIO_port_n)->INDR >> pin) & 0b1) + + +#undef GPIO_ADC_set_sampletime +// 0:7 => 3/9/15/30/43/57/73/241 cycles +#define GPIO_ADC_set_sampletime(GPIO_analog_input, GPIO_ADC_sampletime) ({ \ + ADC1->SAMPTR2 &= ~(0b111) << (3 * GPIO_analog_input); \ + ADC1->SAMPTR2 |= GPIO_ADC_sampletime << (3 * GPIO_analog_input); \ +}) + +#undef GPIO_ADC_set_sampletimes_all +#define GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime) ({ \ + ADC1->SAMPTR2 &= 0; \ + ADC1->SAMPTR2 |= \ + GPIO_ADC_sampletime << (0 * 3) \ + | GPIO_ADC_sampletime << (1 * 3) \ + | GPIO_ADC_sampletime << (2 * 3) \ + | GPIO_ADC_sampletime << (3 * 3) \ + | GPIO_ADC_sampletime << (4 * 3) \ + | GPIO_ADC_sampletime << (5 * 3) \ + | GPIO_ADC_sampletime << (6 * 3) \ + | GPIO_ADC_sampletime << (7 * 3) \ + | GPIO_ADC_sampletime << (8 * 3) \ + | GPIO_ADC_sampletime << (9 * 3); \ + ADC1->SAMPTR1 &= 0; \ + ADC1->SAMPTR1 |= \ + GPIO_ADC_sampletime << (0 * 3) \ + | GPIO_ADC_sampletime << (1 * 3) \ + | GPIO_ADC_sampletime << (2 * 3) \ + | GPIO_ADC_sampletime << (3 * 3) \ + | GPIO_ADC_sampletime << (4 * 3) \ + | GPIO_ADC_sampletime << (5 * 3); \ +}) + +#undef GPIO_ADC_set_power +#define GPIO_ADC_set_power2(enable) GPIO_ADC_set_power_##enable +#define GPIO_ADC_set_power(enable) GPIO_ADC_set_power2(enable) +#define GPIO_ADC_set_power_1 ADC1->CTLR2 |= ADC_ADON +#define GPIO_ADC_set_power_0 ADC1->CTLR2 &= ~(ADC_ADON) + +#undef GPIO_ADC_calibrate +#define GPIO_ADC_calibrate() ({ \ + ADC1->CTLR2 |= ADC_RSTCAL; \ + while(ADC1->CTLR2 & ADC_RSTCAL); \ + ADC1->CTLR2 |= ADC_CAL; \ + while(ADC1->CTLR2 & ADC_CAL); \ +}) + +// large but will likely only ever be called once +static inline void GPIO_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 + GPIO_ADC_set_sampletimes_all(GPIO_ADC_sampletime); + + // set trigger to software + ADC1->CTLR2 |= ADC_EXTSEL; + + // pre-clear conversion queue + ADC1->RSQR1 = 0; + ADC1->RSQR2 = 0; + ADC1->RSQR3 = 0; + + // power the ADC + GPIO_ADC_set_power(1); + GPIO_ADC_calibrate(); +} + +static inline uint16_t GPIO_analogRead(enum GPIO_analog_inputs input) { + // set mux to selected input + ADC1->RSQR3 = input; + // allow everything to precharge + Delay_Us(GPIO_ADC_MUX_DELAY); + // start sw conversion (auto clears) + ADC1->CTLR2 |= ADC_SWSTART; + // wait for conversion complete + while(!(ADC1->STATR & ADC_EOC)) {} + // get result + return ADC1->RDATAR; +} + + + +#undef GPIO_tim1_map +#define GPIO_tim1_map(GPIO_tim1_output_set) ({ \ + RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \ + AFIO->PCFR1 |= ((GPIO_tim1_output_set & 0b11) << 6); \ +}) + +#undef GPIO_tim2_map +#define GPIO_tim2_map(GPIO_tim2_output_set) ({ \ + RCC->APB2PCENR |= RCC_APB2Periph_AFIO; \ + AFIO->PCFR1 |= ((GPIO_tim2_output_set & 0b11) << 8); \ +}) + +static inline void GPIO_tim1_init() { + // enable TIM1 + RCC->APB2PCENR |= RCC_APB2Periph_TIM1; + // reset TIM1 to init all regs + RCC->APB2PRSTR |= RCC_APB2Periph_TIM1; + RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1; + // SMCFGR: default clk input is CK_INT + // set clock prescaler divider + TIM1->PSC = GPIO_timer_prescaler; + // set PWM total cycle width + TIM1->ATRLR = GPIO_timer_resolution; + // CTLR1: default is up, events generated, edge align + // enable auto-reload of preload + TIM1->CTLR1 |= TIM_ARPE; + // initialize counter + TIM1->SWEVGR |= TIM_UG; + // disengage brake + TIM1->BDTR |= TIM_MOE; + // Enable TIM1 + TIM1->CTLR1 |= TIM_CEN; +} +static inline void GPIO_tim2_init() { + // enable TIM2 + RCC->APB1PCENR |= RCC_APB1Periph_TIM2; + // reset TIM2 to init all regs + RCC->APB1PRSTR |= RCC_APB1Periph_TIM2; + RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2; + // SMCFGR: default clk input is CK_INT + // set clock prescaler divider + TIM2->PSC = GPIO_timer_prescaler; + // set PWM total cycle width + TIM2->ATRLR = GPIO_timer_resolution; + // CTLR1: default is up, events generated, edge align + // enable auto-reload of preload + TIM2->CTLR1 |= TIM_ARPE; + // initialize counter + TIM2->SWEVGR |= TIM_UG; + // Enable TIM2 + TIM2->CTLR1 |= TIM_CEN; +} + +#define GPIO_timer_channel_set2(timer, channel) GPIO_timer_channel_set_##channel(timer) +#define GPIO_timer_channel_set(timer, channel) GPIO_timer_channel_set2(timer, channel) +#define GPIO_timer_channel_set_1(timer) timer->CHCTLR1 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable) +#define GPIO_timer_channel_set_2(timer) timer->CHCTLR1 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8) +#define GPIO_timer_channel_set_3(timer) timer->CHCTLR2 |= (TIM_OCMode_PWM1 | TIM_OCPreload_Enable) +#define GPIO_timer_channel_set_4(timer) timer->CHCTLR2 |= ((TIM_OCMode_PWM1 | TIM_OCPreload_Enable) << 8) + +#undef GPIO_tim1_enableCH +#define GPIO_tim1_enableCH(channel) ({ \ + GPIO_timer_channel_set(TIM1, channel); \ + TIM1->CCER |= (TIM_OutputState_Enable) << (4 * (channel - 1)); \ +}) +#undef GPIO_tim2_enableCH +#define GPIO_tim2_enableCH(channel) ({ \ + GPIO_timer_channel_set(TIM2, channel); \ + TIM2->CCER |= (TIM_OutputState_Enable ) << (4 * (channel - 1)); \ +}) + +#define GPIO_timer_CVR(channel) CONCAT_INDIRECT(CH, CONCAT_INDIRECT(channel, CVR)) + +#undef GPIO_tim1_analogWrite +#define GPIO_tim1_analogWrite(channel, value) ({ \ + TIM1->GPIO_timer_CVR(channel) = value; \ + TIM1->SWEVGR |= TIM_UG; \ +}) +#undef GPIO_tim2_analogWrite +#define GPIO_tim2_analogWrite(channel, value) ({ \ + TIM2->GPIO_timer_CVR(channel) = value; \ + TIM2->SWEVGR |= TIM_UG; \ +}) + +#endif // CH32V003_GPIO_BR_H diff --git a/extralibs/wiring.c b/extralibs/wiring.c deleted file mode 100644 index b04f8e46dc07a1832b4332edbc3a0e44b02e427b..0000000000000000000000000000000000000000 --- a/extralibs/wiring.c +++ /dev/null @@ -1,238 +0,0 @@ -//#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; - - GPIOx = (GPIO_TypeDef *)(((uint8_t*)(GPIOA))+(pin>>3)*0x0400); - PinOffset = (pin & 0x7)<<2; - - 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 - 16); - } - else if (pin <= pin_D7) { - GPIOx = GPIOD; - PinOffset = (pin - 24); - } - 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 - 16); - } - else if (pin <= pin_D7) { - GPIOx = GPIOD; - PinOffset = (pin - 24); - } - 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/extralibs/wiring.h b/extralibs/wiring.h deleted file mode 100644 index 3b7d0d74e0a1a8f1d41fca115b4aaeb3901cc8e2..0000000000000000000000000000000000000000 --- a/extralibs/wiring.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef WIRING_H -#define WIRING_H - -#include "ch32v003fun.h" - - - -enum lowhigh { - low, - high, -}; - - - -enum GPIOports{ - port_A, - port_C, - port_D, - port_none, -}; - -enum GPIOpins{ - pin_A1 = 1, - pin_A2, - pin_C0 = 16, - pin_C1, - pin_C2, - pin_C3, - pin_C4, - pin_C5, - pin_C6, - pin_C7, - pin_D0 = 24, - 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 diff --git a/platformio.ini b/platformio.ini index 263b6ac00e167e52572d7cda8edff404de3f7e12..3edc591904e025923f2883664a83269f1e1840eb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -39,9 +39,6 @@ build_src_filter = ${fun_base.build_src_filter} +<examples/external_crystal> [env:GPIO] build_src_filter = ${fun_base.build_src_filter} ${fun_base.extra_libs_srcs} +<examples/GPIO> -[env:GPIO_analogRead] -build_src_filter = ${fun_base.build_src_filter} ${fun_base.extra_libs_srcs} +<examples/GPIO_analogRead> - [env:optionbytes] build_src_filter = ${fun_base.build_src_filter} +<examples/optionbytes>