├── hardware ├── USB_PD_Tester_BOM.tsv ├── USB_PD_Tester_gerber.zip └── USB_PD_Tester_schematic.pdf ├── 3dprint ├── USB_PD_Tester_case.FCStd ├── USB_PD_Tester_case_top.stl ├── USB_PD_Tester_case_bottom.stl └── USB_PD_Tester_case_button.stl ├── documentation ├── USB_PD_Tester_pic1.jpg ├── USB_PD_Tester_pic2.jpg ├── USB_PD_Tester_pic3.jpg ├── USB_PD_Tester_pic4.jpg ├── USB_PD_Tester_pic5.jpg ├── USB_PD_Tester_pic6.jpg ├── USB_PD_Tester_pic7.jpg ├── USB_PD_Tester_pic8.jpg ├── USB_PD_Tester_pic9.jpg ├── USB_PD_Tester_pic10.jpg ├── USB_PD_Tester_pic11.jpg └── USB_PD_Tester_wiring.png ├── software └── pd_tester │ ├── bin │ ├── pd_tester.bin │ └── pd_tester.hex │ ├── config.h │ ├── platformio.ini │ ├── src │ ├── print.h │ ├── i2c_soft.h │ ├── print.c │ ├── i2c_soft.c │ ├── usbpd_sink.h │ ├── main.c │ ├── usbpd.h │ ├── ssd1306_txt.h │ ├── system.c │ ├── usbpd_sink.c │ ├── ssd1306_txt.c │ ├── system.h │ └── gpio.h │ ├── ld │ └── ch32x035.ld │ └── makefile ├── LICENSE └── README.md /hardware/USB_PD_Tester_BOM.tsv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/hardware/USB_PD_Tester_BOM.tsv -------------------------------------------------------------------------------- /3dprint/USB_PD_Tester_case.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/3dprint/USB_PD_Tester_case.FCStd -------------------------------------------------------------------------------- /3dprint/USB_PD_Tester_case_top.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/3dprint/USB_PD_Tester_case_top.stl -------------------------------------------------------------------------------- /hardware/USB_PD_Tester_gerber.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/hardware/USB_PD_Tester_gerber.zip -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic1.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic2.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic3.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic4.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic5.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic6.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic7.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic8.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic9.jpg -------------------------------------------------------------------------------- /hardware/USB_PD_Tester_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/hardware/USB_PD_Tester_schematic.pdf -------------------------------------------------------------------------------- /software/pd_tester/bin/pd_tester.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/software/pd_tester/bin/pd_tester.bin -------------------------------------------------------------------------------- /3dprint/USB_PD_Tester_case_bottom.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/3dprint/USB_PD_Tester_case_bottom.stl -------------------------------------------------------------------------------- /3dprint/USB_PD_Tester_case_button.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/3dprint/USB_PD_Tester_case_button.stl -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic10.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_pic11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_pic11.jpg -------------------------------------------------------------------------------- /documentation/USB_PD_Tester_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/HEAD/documentation/USB_PD_Tester_wiring.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. 2 | To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send 3 | a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 4 | -------------------------------------------------------------------------------- /software/pd_tester/config.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // User Configurations 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | // Pin definitions 8 | #define PIN_KEY_UP PA0 // pin connected to UP button (active low) 9 | #define PIN_KEY_DOWN PA1 // pin connected to DOWN button (active low) 10 | #define PIN_KEY_SLCT PA4 // pin connected to SELECT button (active low) 11 | #define PIN_SCL PA5 // I2C SCL connected to OLED 12 | #define PIN_SDA PA6 // I2C SDA connected to OLED 13 | #define PIN_LED PB1 // pin connected to LED (active low) 14 | 15 | // If using PC18 and/or PC19 for OLED, disable SWJ 16 | #define DISABLE_SWJ 0 // 0: normal SWJ pins, 1: OLED pins 17 | 18 | // MCU supply voltage 19 | #define USB_VDD 0 // 0: 3.3V, 1: 5V 20 | -------------------------------------------------------------------------------- /software/pd_tester/platformio.ini: -------------------------------------------------------------------------------- 1 | ; =================================================================================== 2 | ; PlatformIO Project Configuration File 3 | ; =================================================================================== 4 | ; Project: USB PD Tester for CH32X035 5 | ; Author: Stefan Wagner 6 | ; Year: 2024 7 | ; URL: https://github.com/wagiminator 8 | ; =================================================================================== 9 | ; Install PlatformIO and CH32V: 10 | ; https://pio-ch32v.readthedocs.io/en/latest/ 11 | ; https://github.com/Community-PIO-CH32V/platform-ch32v 12 | ; =================================================================================== 13 | 14 | [env:CH32X035] 15 | platform = https://github.com/Community-PIO-CH32V/platform-ch32v.git 16 | board = genericCH32X035F7P6 17 | 18 | build_flags = -I. -D F_CPU=48000000 19 | board_build.ldscript = $PROJECT_DIR/ld/ch32x035.ld 20 | board_build.use_lto = yes 21 | 22 | upload_protocol = minichlink 23 | upload_command = pip install chprog && chprog $SOURCE 24 | -------------------------------------------------------------------------------- /software/pd_tester/src/print.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Basic PRINT Functions * v1.1 * 3 | // =================================================================================== 4 | // 5 | // Functions available: 6 | // -------------------- 7 | // printF(putchar, f, ...) Uses printf (supports %s, %c, %d, %u, %x, %b, %02d, %%) 8 | // printD(putchar, n) Print decimal value as string via putchar function 9 | // printW(putchar, n) Print 32-bit hex word value as string via putchar function 10 | // printH(putchar, n) Print 16-bit hex half-word value as string via putchar function 11 | // printB(putchar, n) Print 8-bit hex byte value as string via putchar function 12 | // printS(putchar, s) Print string via putchar function 13 | // println(putchar, s) Print string with newline via putchar function 14 | // 15 | // 2023 by Stefan Wagner: https://github.com/wagiminator 16 | 17 | #pragma once 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #include 24 | 25 | void printD(void (*putchar) (char c), uint32_t value); 26 | void printB(void (*putchar) (char c), uint8_t value); 27 | void printH(void (*putchar) (char c), uint16_t value); 28 | void printW(void (*putchar) (char c), uint32_t value); 29 | void printS(void (*putchar) (char c), const char* str); 30 | void println(void (*putchar) (char c), const char* str); 31 | void printF(void (*putchar) (char c), const char *format, ...); 32 | 33 | #ifdef __cplusplus 34 | }; 35 | #endif 36 | -------------------------------------------------------------------------------- /software/pd_tester/src/i2c_soft.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software I2C Master Functions for CH32X035/X034/X033 * v1.1 * 3 | // =================================================================================== 4 | // 5 | // Simple I2C bitbanging. ACK bit of the slave is ignored. Clock stretching by the 6 | // slave is not allowed. External pull-up resistors (4k7 - 10k) are mandatory! 7 | // 8 | // Functions available: 9 | // -------------------- 10 | // I2C_init() I2C init function 11 | // I2C_start(addr) I2C start transmission, address must contain R/W bit 12 | // I2C_restart(addr) I2C restart transmission, address must contain R/W bit 13 | // I2C_stop() I2C stop transmission 14 | // I2C_write(data) I2C transmit one data byte to the slave 15 | // I2C_read(ack) I2C receive one data byte (set ack=0 for last byte) 16 | // 17 | // I2C_writeBuffer(buf,len) Send buffer (*buf) with length (len) via I2C and stop 18 | // I2C_readBuffer(buf,len) Read buffer (*buf) with length (len) via I2C and stop 19 | // 20 | // Define SDA/SCL pin and clock rate below! 21 | // 22 | // Further information: https://github.com/wagiminator/ATtiny13-TinyOLEDdemo 23 | // 2023 by Stefan Wagner: https://github.com/wagiminator 24 | 25 | #pragma once 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #include "config.h" 32 | #include "system.h" 33 | #include "gpio.h" 34 | 35 | // I2C parameters 36 | #ifndef PIN_SDA 37 | #define PIN_SDA PA11 // pin connected to serial data of the I2C bus 38 | #define PIN_SCL PA10 // pin connected to serial clock of the I2C bus 39 | #endif 40 | #define I2C_CLKRATE 400000 // I2C bus clock rate in Hz 41 | 42 | // I2C Functions 43 | void I2C_init(void); // I2C init function 44 | void I2C_start(uint8_t addr); // I2C start transmission 45 | void I2C_restart(uint8_t addr); // I2C restart transmission 46 | void I2C_stop(void); // I2C stop transmission 47 | void I2C_write(uint8_t data); // I2C transmit one data byte to the slave 48 | uint8_t I2C_read(uint8_t ack); // I2C receive one data byte from the slave 49 | 50 | void I2C_writeBuffer(uint8_t* buf, uint16_t len); 51 | void I2C_readBuffer(uint8_t* buf, uint16_t len); 52 | 53 | #ifdef __cplusplus 54 | }; 55 | #endif 56 | -------------------------------------------------------------------------------- /software/pd_tester/ld/ch32x035.ld: -------------------------------------------------------------------------------- 1 | ENTRY( jump_reset ) 2 | 3 | MEMORY 4 | { 5 | FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 62K 6 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K 7 | } 8 | 9 | SECTIONS 10 | { 11 | .init : 12 | { 13 | _sinit = .; 14 | . = ALIGN(4); 15 | KEEP(*(SORT_NONE(.init.jump))) 16 | KEEP(*(SORT_NONE(.init.data))) 17 | . = ALIGN(4); 18 | _einit = .; 19 | } >FLASH AT>FLASH 20 | 21 | .vector : 22 | { 23 | . = ALIGN(4); 24 | *(.vector); 25 | . = ALIGN(4); 26 | } >FLASH AT>FLASH 27 | 28 | .text : 29 | { 30 | . = ALIGN(4); 31 | *(.text) 32 | *(.text.*) 33 | *(.rodata) 34 | *(.rodata*) 35 | *(.glue_7) 36 | *(.glue_7t) 37 | *(.gnu.linkonce.t.*) 38 | . = ALIGN(4); 39 | } >FLASH AT>FLASH 40 | 41 | .fini : 42 | { 43 | KEEP(*(SORT_NONE(.fini))) 44 | . = ALIGN(4); 45 | } >FLASH AT>FLASH 46 | 47 | PROVIDE(_etext = .); 48 | PROVIDE(_eitcm = .); 49 | 50 | .preinit_array : 51 | { 52 | PROVIDE_HIDDEN(__preinit_array_start = .); 53 | KEEP(*(.preinit_array)) 54 | PROVIDE_HIDDEN(__preinit_array_end = .); 55 | } >FLASH AT>FLASH 56 | 57 | .init_array : 58 | { 59 | PROVIDE_HIDDEN(__init_array_start = .); 60 | KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)SORT_BY_INIT_PRIORITY(.ctors.*))) 61 | KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .ctors)) 62 | PROVIDE_HIDDEN(__init_array_end = .); 63 | } >FLASH AT>FLASH 64 | 65 | .fini_array : 66 | { 67 | PROVIDE_HIDDEN(__fini_array_start = .); 68 | KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 69 | KEEP(*(.fini_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .dtors)) 70 | PROVIDE_HIDDEN(__fini_array_end = .); 71 | } >FLASH AT>FLASH 72 | 73 | .ctors : 74 | { 75 | KEEP(*crtbegin.o(.ctors)) 76 | KEEP(*crtbegin?.o(.ctors)) 77 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .ctors)) 78 | KEEP(*(SORT(.ctors.*))) 79 | KEEP(*(.ctors)) 80 | } >FLASH AT>FLASH 81 | 82 | .dtors : 83 | { 84 | KEEP(*crtbegin.o(.dtors)) 85 | KEEP(*crtbegin?.o(.dtors)) 86 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .dtors)) 87 | KEEP(*(SORT(.dtors.*))) 88 | KEEP(*(.dtors)) 89 | } >FLASH AT>FLASH 90 | 91 | .dalign : 92 | { 93 | . = ALIGN(4); 94 | PROVIDE(_data_vma = .); 95 | } >RAM AT>FLASH 96 | 97 | .dlalign : 98 | { 99 | . = ALIGN(4); 100 | PROVIDE(_data_lma = .); 101 | } >FLASH AT>FLASH 102 | 103 | .data : 104 | { 105 | . = ALIGN(4); 106 | *(.gnu.linkonce.r.*) 107 | *(.data .data.*) 108 | *(.gnu.linkonce.d.*) 109 | . = ALIGN(8); 110 | PROVIDE(__global_pointer$ = . + 0x800); 111 | *(.sdata .sdata.*) 112 | *(.sdata2*) 113 | *(.gnu.linkonce.s.*) 114 | . = ALIGN(8); 115 | *(.srodata.cst16) 116 | *(.srodata.cst8) 117 | *(.srodata.cst4) 118 | *(.srodata.cst2) 119 | *(.srodata .srodata.*) 120 | . = ALIGN(4); 121 | PROVIDE(_edata = .); 122 | } >RAM AT>FLASH 123 | 124 | .bss : 125 | { 126 | . = ALIGN(4); 127 | PROVIDE(_sbss = .); 128 | *(.sbss*) 129 | *(.gnu.linkonce.sb.*) 130 | *(.bss*) 131 | *(.gnu.linkonce.b.*) 132 | *(COMMON*) 133 | . = ALIGN(4); 134 | PROVIDE(_ebss = .); 135 | } >RAM AT>FLASH 136 | 137 | PROVIDE(_end = _ebss); 138 | PROVIDE(end = . ); 139 | PROVIDE(_eusrstack = ORIGIN(RAM) + LENGTH(RAM)); 140 | } 141 | -------------------------------------------------------------------------------- /software/pd_tester/makefile: -------------------------------------------------------------------------------- 1 | # =================================================================================== 2 | # Project Makefile 3 | # =================================================================================== 4 | # Project: USB PD Tester for CH32X035 5 | # Author: Stefan Wagner 6 | # Year: 2024 7 | # URL: https://github.com/wagiminator 8 | # =================================================================================== 9 | # Install toolchain: 10 | # sudo apt install build-essential libnewlib-dev gcc-riscv64-unknown-elf 11 | # sudo apt install python3 python3-pip 12 | # pip install chprog 13 | # 14 | # Provide access permission to USB bootloader: 15 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="4348", ATTR{idProduct}=="55e0", MODE="666"' | sudo tee /etc/udev/rules.d/99-ch55x.rules 16 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="55e0", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-ch55x.rules 17 | # sudo udevadm control --reload-rules 18 | # 19 | # Set the device to bootloader mode and type "make flash" in the command line. 20 | # =================================================================================== 21 | 22 | # Files and Folders 23 | TARGET = pd_tester 24 | INCLUDE = include 25 | SOURCE = src 26 | BIN = bin 27 | 28 | # Microcontroller Settings 29 | F_CPU = 48000000 30 | LDSCRIPT = ld/ch32x035.ld 31 | CPUARCH = -march=rv32imac -mabi=ilp32 32 | 33 | # Toolchain 34 | PREFIX = riscv64-unknown-elf 35 | CC = $(PREFIX)-gcc 36 | OBJCOPY = $(PREFIX)-objcopy 37 | OBJDUMP = $(PREFIX)-objdump 38 | OBJSIZE = $(PREFIX)-size 39 | NEWLIB = /usr/include/newlib 40 | ISPTOOL = chprog $(BIN)/$(TARGET).bin 41 | CLEAN = rm -f *.lst *.obj *.cof *.list *.map *.eep.hex *.o *.d 42 | 43 | # Compiler Flags 44 | CFLAGS = -g -Os -flto -ffunction-sections -fdata-sections -fno-builtin -nostdlib 45 | CFLAGS += $(CPUARCH) -DF_CPU=$(F_CPU) -I$(NEWLIB) -I$(INCLUDE) -I$(SOURCE) -I. -Wall 46 | LDFLAGS = -T$(LDSCRIPT) -lgcc -Wl,--gc-sections,--build-id=none 47 | CFILES = $(wildcard ./*.c) $(wildcard $(SOURCE)/*.c) $(wildcard $(SOURCE)/*.S) 48 | 49 | # Symbolic Targets 50 | help: 51 | @echo "Use the following commands:" 52 | @echo "make all compile and build $(TARGET).elf/.bin/.hex/.asm" 53 | @echo "make hex compile and build $(TARGET).hex" 54 | @echo "make asm compile and disassemble to $(TARGET).asm" 55 | @echo "make bin compile and build $(TARGET).bin" 56 | @echo "make flash compile and upload to MCU" 57 | @echo "make clean remove all build files" 58 | 59 | $(BIN)/$(TARGET).elf: $(CFILES) 60 | @echo "Building $(BIN)/$(TARGET).elf ..." 61 | @mkdir -p $(BIN) 62 | @$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) 63 | 64 | $(BIN)/$(TARGET).lst: $(BIN)/$(TARGET).elf 65 | @echo "Building $(BIN)/$(TARGET).lst ..." 66 | @$(OBJDUMP) -S $^ > $(BIN)/$(TARGET).lst 67 | 68 | $(BIN)/$(TARGET).map: $(BIN)/$(TARGET).elf 69 | @echo "Building $(BIN)/$(TARGET).map ..." 70 | @$(OBJDUMP) -t $^ > $(BIN)/$(TARGET).map 71 | 72 | $(BIN)/$(TARGET).bin: $(BIN)/$(TARGET).elf 73 | @echo "Building $(BIN)/$(TARGET).bin ..." 74 | @$(OBJCOPY) -O binary $< $(BIN)/$(TARGET).bin 75 | 76 | $(BIN)/$(TARGET).hex: $(BIN)/$(TARGET).elf 77 | @echo "Building $(BIN)/$(TARGET).hex ..." 78 | @$(OBJCOPY) -O ihex $< $(BIN)/$(TARGET).hex 79 | 80 | $(BIN)/$(TARGET).asm: $(BIN)/$(TARGET).elf 81 | @echo "Disassembling to $(BIN)/$(TARGET).asm ..." 82 | @$(OBJDUMP) -d $(BIN)/$(TARGET).elf > $(BIN)/$(TARGET).asm 83 | 84 | all: $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm size 85 | 86 | elf: $(BIN)/$(TARGET).elf removetemp size 87 | 88 | bin: $(BIN)/$(TARGET).bin removetemp size removeelf 89 | 90 | hex: $(BIN)/$(TARGET).hex removetemp size removeelf 91 | 92 | asm: $(BIN)/$(TARGET).asm removetemp size removeelf 93 | 94 | flash: $(BIN)/$(TARGET).bin size removeelf 95 | @echo "Uploading to MCU ..." 96 | @$(ISPTOOL) 97 | 98 | clean: 99 | @echo "Cleaning all up ..." 100 | @$(CLEAN) 101 | @rm -f $(BIN)/$(TARGET).elf $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm 102 | 103 | size: 104 | @echo "------------------" 105 | @echo "FLASH: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$1 + $$2}') bytes" 106 | @echo "SRAM: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$2 + $$3}') bytes" 107 | @echo "------------------" 108 | 109 | removetemp: 110 | @echo "Removing temporary files ..." 111 | @$(CLEAN) 112 | 113 | removeelf: 114 | @echo "Removing $(BIN)/$(TARGET).elf ..." 115 | @rm -f $(BIN)/$(TARGET).elf 116 | -------------------------------------------------------------------------------- /software/pd_tester/src/print.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Basic PRINT Functions * v1.1 * 3 | // =================================================================================== 4 | // 2023 by Stefan Wagner: https://github.com/wagiminator 5 | 6 | #include 7 | #include "print.h" 8 | 9 | // Print decimal value 10 | void printD(void (*putchar) (char c), uint32_t value) { 11 | uint8_t digitval; // current digit value 12 | uint8_t leadflag = 0; // flag for leading spaces 13 | uint32_t divider = 1000000000; // current divider 14 | while(divider) { // for all digits 15 | digitval = value / divider; // calculate digit value 16 | value = value % divider; // set value to division remainder 17 | divider /= 10; // calculate next divider 18 | if(digitval) leadflag++; // end of leading spaces 19 | if(!divider) leadflag++; // least digit has to be printed 20 | if(leadflag) putchar(digitval + '0'); // print the digit 21 | } 22 | } 23 | 24 | // Convert 4-bit byte nibble into hex character and print it via putchar 25 | void printN(void (*putchar) (char c), uint8_t nibble) { 26 | putchar((nibble <= 9) ? ('0' + nibble) : ('A' - 10 + nibble)); 27 | } 28 | 29 | // Convert 8-bit byte into hex characters and print it via putchar 30 | void printB(void (*putchar) (char c), uint8_t value) { 31 | printN(putchar, value >> 4); 32 | printN(putchar, value & 0x0f); 33 | } 34 | 35 | // Convert 16-bit half-word into hex characters and print it via putchar 36 | void printH(void (*putchar) (char c), uint16_t value) { 37 | printB(putchar, value >> 8); 38 | printB(putchar, value); 39 | } 40 | 41 | // Convert 32-bit word into hex characters and print it via putchar 42 | void printW(void (*putchar) (char c), uint32_t value) { 43 | printH(putchar, value >> 16); 44 | printH(putchar, value); 45 | } 46 | 47 | // Print string via putchar 48 | void printS(void (*putchar) (char c), const char* str) { 49 | while(*str) putchar(*str++); 50 | } 51 | 52 | // Print string with newline via putchar 53 | void println(void (*putchar) (char c), const char* str) { 54 | while(*str) putchar(*str++); 55 | putchar('\n'); 56 | } 57 | 58 | // printf, supports %s, %c, %d, %u, %x, %b, %02d, %% 59 | void _itoa(void (*putchar) (char c), int32_t, int8_t, int8_t); 60 | static void _vfprintf(void (*putchar) (char c), const char *format, va_list arg); 61 | 62 | void printF(void (*putchar) (char c), const char *format, ...) { 63 | va_list arg; 64 | va_start(arg, format); 65 | _vfprintf(putchar, format, arg); 66 | va_end(arg); 67 | } 68 | 69 | static void _vfprintf(void (*putchar) (char c), const char* str, va_list arp) { 70 | int32_t d, r, w, s; 71 | char *c; 72 | 73 | while((d = *str++) != 0) { 74 | if(d != '%') { 75 | putchar(d); 76 | continue; 77 | } 78 | d = *str++; 79 | w = r = s = 0; 80 | if(d == '%') { 81 | putchar(d); 82 | d = *str++; 83 | } 84 | if(d == '0') { 85 | d = *str++; 86 | s = 1; 87 | } 88 | while((d >= '0') && (d <= '9')) { 89 | w += w * 10 + (d - '0'); 90 | d = *str++; 91 | } 92 | if(s) w = -w; 93 | if(d == 's') { 94 | c = va_arg(arp, char*); 95 | while(*c) putchar(*(c++)); 96 | continue; 97 | } 98 | if(d == 'c') { 99 | putchar((char)va_arg(arp, int)); 100 | continue; 101 | } 102 | if(d =='\0') break; 103 | else if(d == 'u') r = 10; 104 | else if(d == 'd') r = -10; 105 | else if(d == 'x') r = 16; 106 | else if(d == 'b') r = 2; 107 | else str--; 108 | if(r == 0) continue; 109 | if(r > 0) _itoa(putchar, (uint32_t)va_arg(arp, int32_t), r, w); 110 | else _itoa(putchar, (int32_t)va_arg(arp, int32_t), r, w); 111 | } 112 | } 113 | 114 | void _itoa(void (*putchar) (char c), int32_t val, int8_t rad, int8_t len) { 115 | char c, sgn = 0, pad = ' '; 116 | char s[20]; 117 | uint8_t i = 0; 118 | 119 | if(rad < 0) { 120 | rad = -rad; 121 | if(val < 0) { 122 | val = -val; 123 | sgn = '-'; 124 | } 125 | } 126 | if(len < 0) { 127 | len = -len; 128 | pad = '0'; 129 | } 130 | if(len > 20) return; 131 | do { 132 | c = (char)((uint32_t)val % rad); 133 | if (c >= 10) c += ('A' - 10); 134 | else c += '0'; 135 | s[i++] = c; 136 | val = (uint32_t)val / rad; 137 | } while(val); 138 | if((sgn != 0) && (pad != '0')) s[i++] = sgn; 139 | while(i < len) s[i++] = pad; 140 | if((sgn != 0) && (pad == '0')) s[i++] = sgn; 141 | do putchar(s[--i]); 142 | while(i); 143 | } 144 | -------------------------------------------------------------------------------- /software/pd_tester/src/i2c_soft.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software I2C Master Functions for CH32X035/X034/X033 * v1.1 * 3 | // =================================================================================== 4 | // 5 | // Simple I2C bitbanging. ACK bit of the slave is ignored. Clock stretching by the 6 | // slave is not allowed. External pull-up resistors (4k7 - 10k) are mandatory! 7 | // 8 | // Further information: https://github.com/wagiminator/ATtiny13-TinyOLEDdemo 9 | // 2023 by Stefan Wagner: https://github.com/wagiminator 10 | 11 | #include "i2c_soft.h" 12 | 13 | // =================================================================================== 14 | // I2C Delay 15 | // =================================================================================== 16 | #define I2C_DLY_TICKS_H (((F_CPU * 9) / (I2C_CLKRATE * 25)) - 41) 17 | #define I2C_DLY_TICKS_L (((F_CPU * 16) / (I2C_CLKRATE * 25)) - 76) 18 | 19 | #if I2C_DLY_TICKS_H >= 1 20 | #define I2C_DELAY_H() DLY_ticks(I2C_DLY_TICKS_H) 21 | #else 22 | #define I2C_DELAY_H() 23 | #endif 24 | 25 | #if I2C_DLY_TICKS_L >= 1 26 | #define I2C_DELAY_L() DLY_ticks(I2C_DLY_TICKS_L) 27 | #else 28 | #define I2C_DELAY_L() 29 | #endif 30 | 31 | // =================================================================================== 32 | // I2C Pin Macros 33 | // =================================================================================== 34 | #define I2C_SDA_HIGH() PIN_input(PIN_SDA) // release SDA -> pulled HIGH by resistor 35 | #define I2C_SDA_LOW() PIN_output(PIN_SDA) // SDA LOW -> pulled LOW by MCU 36 | #define I2C_SCL_HIGH() PIN_input(PIN_SCL) // release SCL -> pulled HIGH by resistor 37 | #define I2C_SCL_LOW() PIN_output(PIN_SCL) // SCL LOW -> pulled LOW by MCU 38 | #define I2C_SDA_READ() PIN_read(PIN_SDA) // read SDA pin 39 | #define I2C_CLOCKOUT() I2C_DELAY_L();I2C_SCL_HIGH();I2C_DELAY_H();I2C_SCL_LOW() 40 | 41 | // =================================================================================== 42 | // I2C Functions 43 | // =================================================================================== 44 | 45 | // I2C init function 46 | void I2C_init(void) { 47 | PIN_input(PIN_SCL); // release SCL 48 | PIN_input(PIN_SDA); // release SDA 49 | PIN_low(PIN_SCL); // preset for SCL low 50 | PIN_low(PIN_SDA); // preset for SDA low 51 | } 52 | 53 | // I2C transmit one data byte to the slave, ignore ACK bit, no clock stretching allowed 54 | void I2C_write(uint8_t data) { 55 | uint8_t i; 56 | for(i=8; i; i--, data<<=1) { // transmit 8 bits, MSB first 57 | (data & 0x80) ? (I2C_SDA_HIGH()) : (I2C_SDA_LOW()); // SDA HIGH if bit is 1 58 | I2C_CLOCKOUT(); // clock out -> slave reads the bit 59 | } 60 | I2C_SDA_HIGH(); // release SDA for ACK bit of slave 61 | I2C_CLOCKOUT(); // 9th clock pulse is for the ignored ACK bit 62 | } 63 | 64 | // I2C start transmission 65 | void I2C_start(uint8_t addr) { 66 | I2C_SDA_LOW(); // start condition: SDA goes LOW first 67 | I2C_DELAY_H(); // delay 68 | I2C_SCL_LOW(); // start condition: SCL goes LOW second 69 | I2C_write(addr); // send slave address 70 | } 71 | 72 | // I2C restart transmission 73 | void I2C_restart(uint8_t addr) { 74 | I2C_SDA_HIGH(); // prepare SDA for HIGH to LOW transition 75 | I2C_DELAY_H(); // delay 76 | I2C_SCL_HIGH(); // restart condition: clock HIGH 77 | I2C_start(addr); // start again 78 | } 79 | 80 | // I2C stop transmission 81 | void I2C_stop(void) { 82 | I2C_SDA_LOW(); // prepare SDA for LOW to HIGH transition 83 | I2C_DELAY_H(); // delay 84 | I2C_SCL_HIGH(); // stop condition: SCL goes HIGH first 85 | I2C_DELAY_H(); // delay 86 | I2C_SDA_HIGH(); // stop condition: SDA goes HIGH second 87 | } 88 | 89 | // I2C receive one data byte from the slave (ack=0 for last byte, ack>0 if more bytes to follow) 90 | uint8_t I2C_read(uint8_t ack) { 91 | uint8_t i; 92 | uint8_t data = 0; // variable for the received byte 93 | I2C_SDA_HIGH(); // release SDA -> will be toggled by slave 94 | for(i=8; i; i--) { // receive 8 bits 95 | data <<= 1; // bits shifted in right (MSB first) 96 | I2C_DELAY_L(); // delay 97 | I2C_SCL_HIGH(); // clock HIGH 98 | I2C_DELAY_H(); // delay 99 | if(I2C_SDA_READ()) data |= 1; // read bit 100 | I2C_SCL_LOW(); // clock LOW -> slave prepares next bit 101 | } 102 | if(ack) I2C_SDA_LOW(); // pull SDA LOW to acknowledge (ACK) 103 | I2C_CLOCKOUT(); // clock out -> slave reads ACK bit 104 | return data; // return the received byte 105 | } 106 | 107 | // Send data buffer via I2C bus and stop 108 | void I2C_writeBuffer(uint8_t* buf, uint16_t len) { 109 | while(len--) I2C_write(*buf++); // write buffer 110 | I2C_stop(); // stop transmission 111 | } 112 | 113 | // Read data via I2C bus to buffer and stop 114 | void I2C_readBuffer(uint8_t* buf, uint16_t len) { 115 | while(len--) *buf++ = I2C_read(len > 0); 116 | I2C_stop(); 117 | } 118 | -------------------------------------------------------------------------------- /software/pd_tester/src/usbpd_sink.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB PD SINK Handler for CH32X035 * v1.5 * 3 | // =================================================================================== 4 | // 5 | // Functions available: 6 | // -------------------- 7 | // PD_connect() Initialize USB-PD and connect, returns 0 if failed 8 | // PD_negotiate() Negotiate current settings, returns 0 if failed 9 | // PD_setVoltage(mV) Request specified voltage in millivolts, returns 0 if failed 10 | // 11 | // PD_getPDONum() Get total number of PDOs 12 | // PD_getFixedNum() Get number of fixed power PDOs 13 | // PD_getPPSNum() Get number of programmable power PDOs 14 | // 15 | // PD_getPDOVoltage(p) Get voltage of specified fixed power PDO (1..PD_getFixedNum()) 16 | // PD_getPDOMinVoltage(p) Get min voltage of specified PDO (p = 1..PD_getPDONum()) 17 | // PD_getPDOMaxVoltage(p) Get max voltage of specified PDO (p = 1..PD_getPDONum()) 18 | // PD_getPDOMaxCurrent(p) Get max current of specified PDO (p = 1..PD_getPDONum()) 19 | // 20 | // PD_getPDO() Get active PDO 21 | // PD_getVoltage() Get active voltage 22 | // PD_getCurrent() Get active max current 23 | // 24 | // Reference: https://github.com/openwch/ch32x035 25 | // 2023 by Stefan Wagner: https://github.com/wagiminator 26 | 27 | #pragma once 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #include "config.h" 34 | #include "system.h" 35 | #include "usbpd.h" 36 | 37 | // =================================================================================== 38 | // Parameters and Checks 39 | // =================================================================================== 40 | #if SYS_USE_VECTORS == 0 41 | #error Interrupt vector table must be enabled (SYS_USE_VECTORS in system.h)! 42 | #endif 43 | 44 | #if F_CPU == 48000000 45 | #define USBPD_TMR_TX (80-1) // timer value for USB PD BMC TX @ F_CPU=48MHz 46 | #define USBPD_TMR_RX (120-1) // timer value for USB PD BMC RX @ F_CPU=48MHz 47 | #elif F_CPU == 24000000 48 | #define USBPD_TMR_TX (40-1) // timer value for USB PD BMC TX @ F_CPU=24MHz 49 | #define USBPD_TMR_RX (60-1) // timer value for USB PD BMC RX @ F_CPU=24MHz 50 | #elif F_CPU == 12000000 51 | #define USBPD_TMR_TX (20-1) // timer value for USB PD BMC TX @ F_CPU=12MHz 52 | #define USBPD_TMR_RX (30-1) // timer value for USB PD BMC RX @ F_CPU=12MHz 53 | #elif F_CPU == 6000000 54 | #define USBPD_TMR_TX (10-1) // timer value for USB PD BMC TX @ F_CPU=6MHz 55 | #define USBPD_TMR_RX (15-1) // timer value for USB PD BMC RX @ F_CPU=6MHz 56 | #else 57 | #error Unsupported system frequency for USBPD! 58 | #endif 59 | 60 | // =================================================================================== 61 | // Type defines 62 | // =================================================================================== 63 | typedef struct { 64 | uint16_t Current; 65 | uint16_t Voltage; 66 | } FixedSourceCap_t; 67 | 68 | typedef struct { 69 | uint16_t MinVoltage; 70 | uint16_t MaxVoltage; 71 | uint16_t Current; 72 | } PPSSourceCap_t; 73 | 74 | typedef enum { 75 | CC_IDLE = 0u, 76 | CC_CHECK_CONNECT, 77 | CC_CONNECT, 78 | CC_SOURCE_CAP, 79 | CC_SEND_REQUEST, 80 | CC_WAIT_ACCEPT, 81 | CC_ACCEPT, 82 | CC_WAIT_PS_RDY, 83 | CC_PS_RDY, 84 | CC_GET_SOURCE_CAP, 85 | } cc_state_t; 86 | 87 | typedef struct { 88 | volatile cc_state_t CC_State; 89 | volatile cc_state_t CC_LastState; 90 | volatile uint8_t CC_NoneTimes; 91 | volatile uint8_t CC1_ConnectTimes; 92 | volatile uint8_t CC2_ConnectTimes; 93 | FixedSourceCap_t* FixedSourceCap; 94 | PPSSourceCap_t* PPSSourceCap; 95 | volatile uint8_t SourcePDONum; 96 | volatile uint8_t SourcePPSNum; 97 | volatile uint8_t PD_Version; 98 | volatile uint16_t WaitTime; 99 | volatile uint8_t SetPDONum; 100 | volatile uint8_t LastSetPDONum; 101 | volatile uint16_t SetVoltage; 102 | volatile uint16_t LastSetVoltage; 103 | volatile uint8_t USBPD_READY; 104 | volatile uint8_t SourceMessageID; 105 | volatile uint8_t SinkMessageID; 106 | volatile uint8_t SinkGoodCRCOver; 107 | volatile uint8_t SourceGoodCRCOver; 108 | } pd_control_t; 109 | 110 | // =================================================================================== 111 | // Functions 112 | // =================================================================================== 113 | uint8_t PD_connect(void); // Initialize PD and connect 114 | uint8_t PD_negotiate(void); // Negotiate current settings 115 | uint8_t PD_setVoltage(uint16_t voltage); // Set specified voltage (in millivolts) 116 | 117 | uint8_t PD_getPDONum(void); // Get total number of PDOs 118 | uint8_t PD_getFixedNum(void); // Get number of fixed power PDOs 119 | uint8_t PD_getPPSNum(void); // Get number of programmable power PDOs 120 | 121 | uint16_t PD_getPDOVoltage(uint8_t pdonum); // Get voltage of specified fixed power PDO 122 | uint16_t PD_getPDOMinVoltage(uint8_t pdonum); // Get minimum voltage of specified PDO 123 | uint16_t PD_getPDOMaxVoltage(uint8_t pdonum); // Get maximum voltage of specified PDO 124 | uint16_t PD_getPDOMaxCurrent(uint8_t pdonum); // Get max current of specified PDO 125 | 126 | uint8_t PD_getPDO(void); // Get active PDO 127 | uint16_t PD_getVoltage(void); // Get active voltage 128 | uint16_t PD_getCurrent(void); // Get active max current 129 | 130 | uint8_t PD_setPDO(uint8_t pdonum, uint16_t voltage); // Set specified PDO and voltage 131 | 132 | #ifdef __cplusplus 133 | } 134 | #endif 135 | -------------------------------------------------------------------------------- /software/pd_tester/src/main.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Project: USB PD Tester for CH32X035 3 | // Version: v1.3 4 | // Year: 2024 5 | // Author: Stefan Wagner 6 | // Github: https://github.com/wagiminator 7 | // EasyEDA: https://easyeda.com/wagiminator 8 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 9 | // =================================================================================== 10 | // 11 | // Description: 12 | // ------------ 13 | // The USB PD Tester allows users to retrieve and test the capabilities of a connected 14 | // USB Power Delivery Adapter. 15 | // 16 | // References: 17 | // ----------- 18 | // - WCH Nanjing Qinheng Microelectronics: http://wch.cn 19 | // 20 | // Compilation Instructions: 21 | // ------------------------- 22 | // - Make sure GCC toolchain (gcc-riscv64-unknown-elf, newlib) and Python3 with chprog 23 | // are installed. In addition, Linux requires access rights to the USB bootloader. 24 | // - Press the BOOT button on the MCU board and keep it pressed while connecting it 25 | // via USB to your PC. 26 | // - Run 'make flash'. 27 | 28 | 29 | // =================================================================================== 30 | // Libraries, Definitions and Macros 31 | // =================================================================================== 32 | #include // user configurations 33 | #include // OLED text functions 34 | #include // USB PD sink functions 35 | 36 | // Global variables 37 | uint8_t select = 1; // selected PDO 38 | uint8_t active = 1; // active PDO 39 | uint16_t voltage = 5000; // selected voltage 40 | 41 | // =================================================================================== 42 | // Functions 43 | // =================================================================================== 44 | 45 | // Set selected PDO marker 46 | void setSelect(uint8_t pdo) { 47 | OLED_cursor(120, select - 1); OLED_write(' '); 48 | if(pdo > PD_getPDONum()) select = 1; 49 | else if(pdo < 1) select = PD_getPDONum(); 50 | else select = pdo; 51 | OLED_cursor(120, select - 1); OLED_write('<'); 52 | } 53 | 54 | // Set active PDO marker 55 | void setActive(uint8_t pdo) { 56 | OLED_cursor( 0, active - 1); OLED_write(' '); 57 | active = pdo; 58 | OLED_cursor( 0, active - 1); OLED_write('*'); 59 | } 60 | 61 | // Set selected voltage 62 | void setVoltage(uint16_t v) { 63 | if (v <= PD_getPDOMinVoltage(select)) voltage = PD_getPDOMinVoltage(select); 64 | else if(v >= PD_getPDOMaxVoltage(select)) voltage = PD_getPDOMaxVoltage(select); 65 | else voltage = v; 66 | OLED_cursor(30, 6); OLED_printf(">%6dmV <", voltage); 67 | } 68 | 69 | // Print source capabilities 70 | void printSourceCap(void) { 71 | uint8_t i; 72 | OLED_clear(); 73 | for(i = 1; i <= PD_getPDONum(); i++) { 74 | if(i <= PD_getFixedNum()) 75 | OLED_printf(" (%d)%6dmV %5dmA ", i, PD_getPDOVoltage(i), PD_getPDOMaxCurrent(i)); 76 | else 77 | OLED_printf(" [%d]%6dmV-%5dmV ", i, PD_getPDOMinVoltage(i), PD_getPDOMaxVoltage(i)); 78 | } 79 | setSelect(select); 80 | setActive(active); 81 | } 82 | 83 | // Print selected programmable power PDO infos 84 | void printPPS(void) { 85 | OLED_clear(); 86 | OLED_printf("Select voltage of [%d]\n", select); 87 | OLED_printf("min voltage:%7dmV", PD_getPDOMinVoltage(select)); 88 | OLED_printf("max voltage:%7dmV", PD_getPDOMaxVoltage(select)); 89 | OLED_printf("max current:%7dmA", PD_getPDOMaxCurrent(select)); 90 | setVoltage(voltage); 91 | } 92 | 93 | // =================================================================================== 94 | // Main Function 95 | // =================================================================================== 96 | int main(void) { 97 | // Variables 98 | uint8_t i; 99 | uint8_t keydelay; 100 | 101 | // Setup OLED and USB-PD 102 | #if DISABLE_SWJ 103 | RCC->APB2PCENR |= RCC_AFIOEN; // enable AFIO clock 104 | AFIO->PCFR1 |= AFIO_PCFR1_SWJ_CFG_2; // disable SWJ on pins PC18 and PC19 105 | #endif 106 | OLED_init(); 107 | OLED_clear(); 108 | OLED_printf("Connecting..."); 109 | if(!PD_connect()) { 110 | OLED_printf("FAILED"); 111 | while(1); 112 | } 113 | 114 | // Print source capabilities 115 | printSourceCap(); 116 | 117 | // Setup button pins 118 | PIN_input_PU(PIN_KEY_UP); 119 | PIN_input_PU(PIN_KEY_DOWN); 120 | PIN_input_PU(PIN_KEY_SLCT); 121 | 122 | // Loop 123 | while(1) { 124 | 125 | if(!PIN_read(PIN_KEY_UP)) { 126 | setSelect(select - 1); 127 | while(!PIN_read(PIN_KEY_UP)); 128 | } 129 | 130 | if(!PIN_read(PIN_KEY_DOWN)) { 131 | setSelect(select + 1); 132 | while(!PIN_read(PIN_KEY_DOWN)); 133 | } 134 | 135 | if(!PIN_read(PIN_KEY_SLCT)) { 136 | if(select <= PD_getFixedNum()) { 137 | if(PD_setPDO(select, PD_getPDOVoltage(select))) 138 | setActive(select); 139 | } 140 | else { 141 | printPPS(); 142 | while(!PIN_read(PIN_KEY_SLCT)); 143 | DLY_ms(10); 144 | while(PIN_read(PIN_KEY_SLCT)) { 145 | 146 | if(!PIN_read(PIN_KEY_UP)) { 147 | setVoltage(voltage + 20); 148 | i = keydelay; 149 | while((i--) && (!PIN_read(PIN_KEY_UP))) DLY_ms(10); 150 | keydelay = 2; 151 | } 152 | 153 | else if(!PIN_read(PIN_KEY_DOWN)) { 154 | setVoltage(voltage - 20); 155 | i = keydelay; 156 | while((i--) && (!PIN_read(PIN_KEY_DOWN))) DLY_ms(10); 157 | keydelay = 2; 158 | } 159 | 160 | else keydelay = 50; 161 | 162 | PD_negotiate(); 163 | } 164 | 165 | if(PD_setPDO(select, voltage)) 166 | active = select; 167 | printSourceCap(); 168 | } 169 | while(!PIN_read(PIN_KEY_SLCT)); 170 | } 171 | 172 | PD_negotiate(); 173 | DLY_ms(10); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /software/pd_tester/src/usbpd.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB PD Constant and Structure Defines * v1.1 * 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | 13 | // Get values from PDO representation 14 | #define POWER_DECODE_50MV(value) ((uint16_t)(((value) * 50))) // From 50mV multiples to mV 15 | #define POWER_DECODE_100MV(value) ((uint16_t)(((value) * 100))) // From 100mV multiples to mV 16 | #define POWER_DECODE_10MA(value) ((uint16_t)(((value) * 10))) // From 10mA multiples to mA 17 | #define POWER_DECODE_50MA(value) ((uint16_t)(((value) * 50))) // From 50mA multiples to mA 18 | 19 | // PD PHY Channel 20 | #define USBPD_CCNONE 0x00u 21 | #define USBPD_CC1 0x01u 22 | #define USBPD_CC2 0x02u 23 | 24 | // USB PD Revision 25 | #define USBPD_SPECIFICATION_REV1 0x00u // Revision 1.0 26 | #define USBPD_SPECIFICATION_REV2 0x01u // Revision 2.0 27 | #define USBPD_SPECIFICATION_REV3 0x02u // Revision 3.0 28 | 29 | // USB PD Revision aliases 30 | #define USBPD_REVISION_10 0x00u // Revision 1.0 31 | #define USBPD_REVISION_20 0x01u // Revision 2.0 32 | #define USBPD_REVISION_30 0x02u // Revision 3.0 33 | 34 | typedef struct { 35 | uint32_t MaxCurrentIn10mAunits : 10u; 36 | uint32_t VoltageIn50mVunits : 10u; 37 | uint32_t PeakCurrent : 2u; 38 | uint32_t Reserved_22bit : 1u; 39 | uint32_t EPRModeCapable : 1u; 40 | uint32_t UnchunkedExtendedMessage : 1u; 41 | uint32_t DualRoleData : 1u; 42 | uint32_t USBCommunicationsCapable : 1u; 43 | uint32_t UnconstrainedPower : 1u; 44 | uint32_t USBSuspendSupported : 1u; 45 | uint32_t DualRolePower : 1u; 46 | uint32_t FixedSupply : 2u; 47 | } USBPD_SourceFixedSupplyPDO_t; 48 | 49 | typedef struct { 50 | uint32_t MaxCurrentIn50mAincrements : 7u; 51 | uint32_t Reserved_7bit : 1u; // shall be set to zero 52 | uint32_t MinVoltageIn100mVincrements: 8u; 53 | uint32_t Reserved_16bit : 1u; // shall be set to zero 54 | uint32_t MaxVoltageIn100mVincrements: 8u; 55 | uint32_t Reserved_25_26bit : 2u; // shall be set to zero 56 | uint32_t PPSpowerLimited : 1u; 57 | uint32_t SPRprogrammablePowerSupply : 2u; // 00b 58 | uint32_t AugmentedPowerDataObject : 2u; // 11b 59 | } USBPD_SourcePPSSupplyPDO_t; 60 | 61 | typedef struct { 62 | uint32_t MaxPowerIn1Wincrements : 8u; 63 | uint32_t MinVoltageIn100mVincrements: 8u; 64 | uint32_t Reserved_16bit : 1u; // shall be set to zero 65 | uint32_t MaxVoltageIn100mVincrements: 9u; 66 | uint32_t Reserved_26_27bit : 2u; // shall be set to zero 67 | uint32_t EPRprogrammablePowerSupply : 2u; // 01b 68 | uint32_t AugmentedPowerDataObject : 2u; // 11b 69 | } USBPD_SourceEPRSupplyPDO_t; 70 | 71 | typedef union { 72 | uint32_t d32; 73 | USBPD_SourceFixedSupplyPDO_t SourceFixedPDO; 74 | USBPD_SourcePPSSupplyPDO_t SourcePPSPDO; 75 | USBPD_SourceEPRSupplyPDO_t SourceEPRPDO; 76 | } USBPD_PDO_t; 77 | 78 | typedef enum { 79 | USBPD_CONTROL_MSG_GOODCRC = 0x01u, 80 | USBPD_CONTROL_MSG_GOTOMIN = 0x02u, 81 | USBPD_CONTROL_MSG_ACCEPT = 0x03u, 82 | USBPD_CONTROL_MSG_REJECT = 0x04u, 83 | USBPD_CONTROL_MSG_PING = 0x05u, 84 | USBPD_CONTROL_MSG_PS_RDY = 0x06u, 85 | USBPD_CONTROL_MSG_GET_SRC_CAP = 0x07u, 86 | USBPD_CONTROL_MSG_GET_SNK_CAP = 0x08u, 87 | USBPD_CONTROL_MSG_DR_SWAP = 0x09u, 88 | USBPD_CONTROL_MSG_PR_SWAP = 0x0Au, 89 | USBPD_CONTROL_MSG_VCONN_SWAP = 0x0Bu, 90 | USBPD_CONTROL_MSG_WAIT = 0x0Cu, 91 | USBPD_CONTROL_MSG_SOFT_RESET = 0x0Du, 92 | USBPD_CONTROL_MSG_DATA_RESET = 0x0Eu, 93 | USBPD_CONTROL_MSG_DATA_RESET_COMPLETE = 0x0Fu, 94 | USBPD_CONTROL_MSG_NOT_SUPPORTED = 0x10u, 95 | USBPD_CONTROL_MSG_GET_SRC_CAPEXT = 0x11u, 96 | USBPD_CONTROL_MSG_GET_STATUS = 0x12u, 97 | USBPD_CONTROL_MSG_FR_SWAP = 0x13u, 98 | USBPD_CONTROL_MSG_GET_PPS_STATUS = 0x14u, 99 | USBPD_CONTROL_MSG_GET_COUNTRY_CODES = 0x15u, 100 | USBPD_CONTROL_MSG_GET_SNK_CAPEXT = 0x16u, 101 | USBPD_CONTROL_MSG_GET_SRC_INFO = 0x17u, 102 | USBPD_CONTROL_MSG_GET_REVISION = 0x18u, 103 | } USBPD_ControlMessage_t; 104 | 105 | typedef enum { 106 | USBPD_DATA_MSG_SRC_CAP = 0x01u, 107 | USBPD_DATA_MSG_REQUEST = 0x02u, 108 | USBPD_DATA_MSG_BIST = 0x03u, 109 | USBPD_DATA_MSG_SNK_CAP = 0x04u, 110 | USBPD_DATA_MSG_BATTERY_STATUS = 0x05u, 111 | USBPD_DATA_MSG_ALERT = 0x06u, 112 | USBPD_DATA_MSG_GET_COUNTRY_INFO = 0x07u, 113 | USBPD_DATA_MSG_ENTER_USB = 0x08u, 114 | USBPD_DATA_MSG_EPR_REUEST = 0x09u, 115 | USBPD_DATA_MSG_EPR_MODE = 0x0Au, 116 | USBPD_DATA_MSG_SRC_INFO = 0x0Bu, 117 | USBPD_DATA_MSG_REVISION = 0x0Cu, 118 | USBPD_DATA_MSG_VENDOR_DEFINED = 0x0Fu 119 | } USBPD_DataMessage_t; 120 | 121 | typedef union { 122 | uint16_t d16; 123 | USBPD_DataMessage_t DataMessage; 124 | USBPD_ControlMessage_t ControlMessage; 125 | } USBPD_MessageType_t; 126 | 127 | typedef struct { 128 | uint16_t MessageType : 5u; 129 | uint16_t PortDataRole : 1u; // 0b->UFP, 1b->DFP 130 | uint16_t SpecificationRevision : 2u; // 00b->v1.0, 01b->v2.0, 10b->v3.0 131 | uint16_t PortPowerRole : 1u; // 0b->sink, 1b->source 132 | uint16_t MessageID : 3u; 133 | uint16_t NumberOfDataObjects : 3u; 134 | uint16_t Extended : 1u; 135 | } USBPD_MessageHeader_tt; 136 | 137 | typedef union { 138 | uint16_t d16; 139 | USBPD_MessageHeader_tt MessageHeader; 140 | } USBPD_MessageHeader_t; 141 | 142 | typedef struct { 143 | uint32_t MaxOperatingCurrent10mAunits : 10u; 144 | uint32_t OperatingCurrentIn10mAunits : 10u; 145 | uint32_t Reserved20_21 : 2u; // 00b 146 | uint32_t EPRModeCapable : 1u; 147 | uint32_t UnchunkedExtendedMessage : 1u; 148 | uint32_t NoUSBSuspend : 1u; 149 | uint32_t USBCommunicationsCapable : 1u; 150 | uint32_t CapabilityMismatch : 1u; 151 | uint32_t GiveBackFlag : 1u; 152 | uint32_t ObjectPosition : 4u; 153 | } USBPD_SinkFixedVariableRDO_t; 154 | 155 | typedef struct { 156 | uint32_t OperatingCurrentIn50mAunits : 7u; 157 | uint32_t Reserved7_8 : 2u; // shall be set to zero 158 | uint32_t OutputVoltageIn20mVunits : 12u; 159 | uint32_t Reserved21 : 1u; // shall be set to zero 160 | uint32_t EPRModeCapable : 1u; 161 | uint32_t UnchunkedExtendedMessage : 1u; 162 | uint32_t NoUSBSuspend : 1u; 163 | uint32_t USBCommunicationsCapable : 1u; 164 | uint32_t CapabilityMismatch : 1u; 165 | uint32_t Rserved27 : 1u; // shall be set to zero 166 | uint32_t ObjectPosition : 4u; 167 | } USBPD_SinkPPSRDO_t; 168 | 169 | typedef union { 170 | uint32_t d32; 171 | USBPD_SinkFixedVariableRDO_t SinkFixedVariableRDO; 172 | USBPD_SinkPPSRDO_t SinkPPSRDO; 173 | } USBPD_SINKRDO_t; 174 | 175 | #ifdef __cplusplus 176 | } 177 | #endif 178 | -------------------------------------------------------------------------------- /software/pd_tester/src/ssd1306_txt.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // SSD1306/SH1106/SH1107 I2C OLED Text Functions * v1.3 * 3 | // =================================================================================== 4 | // 5 | // Collection of the most necessary functions for controlling an SSD1306/SH1106 I2C 6 | // OLED for the display of simple text, working without a screen buffer. 7 | // 8 | // Functions available: 9 | // -------------------- 10 | // OLED_init() Init OLED display 11 | // OLED_display(v) Switch display on/off (0: display off, 1: display on) 12 | // OLED_contrast(v) Set OLED contrast (0-255) 13 | // OLED_invert(v) Invert display (0: inverse off, 1: inverse on) 14 | // OLED_flip(xflip,yflip) Flip display (0: flip off, 1: flip on) 15 | // OLED_vscroll(y) Scroll display vertically (0-64) 16 | // OLED_clear() Clear screen of OLED display 17 | // OLED_clearLine(y) Clear line y 18 | // 19 | // OLED_cursor(x,y) Set text cursor at position (x,y) 20 | // OLED_textsize(sz) Set text size (0: 5x8, 1: 5x16, 2: 10x16), enable OLED_BIGCHARS 21 | // OLED_textinvert(v) Invert text (0: inverse off, 1: inverse on) 22 | // OLED_write(c) Write character at cursor position or handle control characters 23 | // OLED_print(str) Print string (*str) at cursor position 24 | // OLED_printSegment(v,d,l,dp) Print value (v) at cursor position using defined segment font 25 | // with (d) number of digits, (l) leading (0: '0', 1: space) and 26 | // decimal point at position (dp) counted from the right 27 | // OLED_drawBitmap(bmp,w,h) Draw bitmap (pointer *bmp) at cursor position 28 | // width (w) in pixels, hight (h) in 8-pixel lines 29 | // 30 | // If print functions are activated (see below, print.h must be included): 31 | // ----------------------------------------------------------------------- 32 | // OLED_printf(f, ...) printf (supports %s, %c, %d, %u, %x, %b, %02d, %%) 33 | // OLED_printD(n) Print decimal value 34 | // OLED_printW(n) Print 32-bit hex word value 35 | // OLED_printH(n) Print 16-bit hex half-word value 36 | // OLED_printB(n) Print 8-bit hex byte value 37 | // OLED_printS(s) Print string 38 | // OLED_println(s) Print string with newline 39 | // OLED_newline() Send newline 40 | // 41 | // Tested devices: 42 | // --------------- 43 | // - 1.5" 128x128 SH1107 44 | // - 1.3" 128x64 SH1106 45 | // - 0.96" 128x64 SSD1306 46 | // - 0.91" 128x32 SSD1306 47 | // - 0.49" 64x32 SSD1306 48 | // - 0.42" 72x40 SSD1306 49 | // 50 | // References: 51 | // ----------- 52 | // - Neven Boyanov: https://github.com/tinusaur/ssd1306xled 53 | // - Stephen Denne: https://github.com/datacute/Tiny4kOLED 54 | // - David Johnson-Davies: http://www.technoblogy.com/show?TV4 55 | // - TinyOLEDdemo: https://github.com/wagiminator/attiny13-tinyoleddemo 56 | // - TinyTerminal: https://github.com/wagiminator/ATtiny85-TinyTerminal 57 | // - OLED Font Editor: http://sourpuss.net/projects/fontedit/ 58 | // 59 | // 2022 by Stefan Wagner: https://github.com/wagiminator 60 | 61 | #pragma once 62 | 63 | #ifdef __cplusplus 64 | extern "C" { 65 | #endif 66 | 67 | #include "i2c_soft.h" // choose your I2C library 68 | #include "system.h" 69 | 70 | // OLED Parameters 71 | #define OLED_ADDR 0x3C // OLED I2C device address 72 | #define OLED_WIDTH 128 // OLED width in pixels 73 | #define OLED_HEIGHT 64 // OLED height in pixels 74 | #define OLED_SH1106 0 // OLED driver - 0: SSD1306/SH1107, 1: SH1106 75 | 76 | #define OLED_BOOT_TIME 50 // OLED boot up time in milliseconds 77 | #define OLED_INIT_I2C 1 // 1: init I2C with OLED_init() 78 | #define OLED_XFLIP 1 // 1: flip screen in X-direction with OLED_init() 79 | #define OLED_YFLIP 1 // 1: flip screen in Y-direction with OLED_init() 80 | #define OLED_INVERT 0 // 1: invert screen with OLED_init() 81 | 82 | // OLED Text Settings 83 | #define OLED_PRINT 1 // 1: include print functions (needs print.h) 84 | #define OLED_BIGCHARS 0 // 1: use big fonts (OLED_textsize()) 85 | #define OLED_SEG_FONT 0 // 0: standard font, 1: 13x32 digits, 2: 5x16 digits 86 | #define OLED_SEG_SPACE 3 // width of space between segment digits in pixels 87 | 88 | // OLED Modes 89 | #define OLED_CMD_MODE 0x00 // set command mode 90 | #define OLED_DAT_MODE 0x40 // set data mode 91 | #define OLED_CMD_ONCE 0x80 // send one command byte 92 | #define OLED_DAT_ONCE 0xC0 // send one data byte 93 | 94 | // OLED Commands 95 | #define OLED_COLUMN_LOW 0x00 // set lower 4 bits of start column (0x00 - 0x0F) 96 | #define OLED_COLUMN_HIGH 0x10 // set higher 4 bits of start column (0x10 - 0x1F) 97 | #define OLED_MEMORYMODE 0x20 // set memory addressing mode (following byte) 98 | #define OLED_COLUMNS 0x21 // set start and end column (following 2 bytes) 99 | #define OLED_PAGES 0x22 // set start and end page (following 2 bytes) 100 | #define OLED_STARTLINE 0x40 // set display start line (0x40-0x7F = 0-63) 101 | #define OLED_CONTRAST 0x81 // set display contrast (following byte, 0-255) 102 | #define OLED_CHARGEPUMP 0x8D // (following byte - 0x14:enable, 0x10: disable) 103 | #define OLED_XFLIP_OFF 0xA0 // don't flip display horizontally 104 | #define OLED_XFLIP_ON 0xA1 // flip display horizontally 105 | #define OLED_RESUME 0xA4 // display all on resume 106 | #define OLED_ALL_ON 0xA5 // display all on 107 | #define OLED_INVERT_OFF 0xA6 // set non-inverted display 108 | #define OLED_INVERT_ON 0xA7 // set inverse display 109 | #define OLED_MULTIPLEX 0xA8 // set multiplex ratio (following byte) 110 | #define OLED_DISPLAY_OFF 0xAE // set display off (sleep mode) 111 | #define OLED_DISPLAY_ON 0xAF // set display on 112 | #define OLED_PAGE 0xB0 // set start page (0xB0-0xB7 = 0-7) 113 | #define OLED_YFLIP_OFF 0xC0 // don't flip display vertically 114 | #define OLED_YFLIP_ON 0xC8 // flip display vertically 115 | #define OLED_OFFSET 0xD3 // set display offset (y-scroll: following byte) 116 | #define OLED_CLOCK 0xD5 // set frequency (bits 7-4) and divider (bits 3-0) 117 | #define OLED_PRECHARGE 0xD9 // set pre-charge period (following byte) 118 | #define OLED_COMPINS 0xDA // set COM pin config (following byte) 119 | #define OLED_VCOM_DETECT 0xDB // set VCOM detect (following byte) 120 | 121 | // OLED Control Functions 122 | void OLED_init(void); // OLED init function 123 | void OLED_display(uint8_t val); // Switch display on/off (0: display off, 1: display on) 124 | void OLED_contrast(uint8_t val); // Set display contrast (0-255) 125 | void OLED_invert(uint8_t val); // Invert display (0: inverse off, 1: inverse on) 126 | void OLED_flip(uint8_t xflip, uint8_t yflip); // Flip display (0: flip off, 1: flip on) 127 | void OLED_vscroll(uint8_t y); // Scroll display vertically (0-64) 128 | 129 | // OLED Text Functions 130 | void OLED_clear(void); // Clear screen 131 | void OLED_clearLine(uint8_t y); // Clear line y 132 | void OLED_write(char c); // Write a character or handle control characters 133 | void OLED_print(char* str); // Print a string 134 | void OLED_cursor(uint8_t x, uint8_t y); // Set cursor 135 | void OLED_textinvert(uint8_t yes); // Invert text 136 | 137 | #if OLED_BIGCHARS > 0 138 | void OLED_textsize(uint8_t size); // Set text size (0: 5x8, 1: 5x16, 2: 10x16) 139 | #endif 140 | 141 | // OLED Special Functions 142 | void OLED_drawBitmap(const uint8_t* bmp, uint8_t w, uint8_t h); 143 | void OLED_clearRect(uint8_t w, uint8_t h); 144 | void OLED_printSegment(uint16_t value, uint8_t digits, uint8_t lead, uint8_t decimal); 145 | 146 | #define OLED_textcolor(c) OLED_textinvert(!(c)) 147 | 148 | // OLED Cursor Position 149 | extern uint8_t OLED_x, OLED_y; 150 | 151 | // Additional print functions (if activated, see above) 152 | #if OLED_PRINT == 1 153 | #include "print.h" 154 | #define OLED_printD(n) printD(OLED_write, n) // print decimal as string 155 | #define OLED_printW(n) printW(OLED_write, n) // print word as string 156 | #define OLED_printH(n) printH(OLED_write, n) // print half-word as string 157 | #define OLED_printB(n) printB(OLED_write, n) // print byte as string 158 | #define OLED_printS(s) printS(OLED_write, s) // print string 159 | #define OLED_println(s) println(OLED_write, s) // print string with newline 160 | #define OLED_newline() OLED_write('\n') // send newline 161 | #define OLED_printf(f, ...) printF(OLED_write, f, ##__VA_ARGS__) 162 | #endif 163 | 164 | #ifdef __cplusplus 165 | }; 166 | #endif 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # USB PD Tester 2 | The USB PD Tester is a monitoring and triggering device designed for USB Power Delivery. It allows you to test a wide range of USB Type-C PD power supplies and their corresponding cables. This tool not only displays the capabilities of the power supply on an OLED but also enables you to select one of the available fixed or programmable voltages for output on the screw terminal. With this, it can also be used to power your projects with various selectable voltages and high currents, serving as a versatile variable power supply. 3 | 4 | The USB PD Tester is built around the inexpensive (30 cents at the time of writing) and user-friendly CH32X035 RISC-V microcontroller, which comes with integrated USB 2.0, USB PD 2.0/3.0, and USB Type-C hardware support. 5 | 6 | - Project video (Youtube): https://youtu.be/wqLiRnbcISo 7 | 8 | ![USB_PD_Tester_pic1.jpg](https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/main/documentation/USB_PD_Tester_pic1.jpg) 9 | 10 | # USB Power Delivery 11 | USB Power Delivery (USB PD) is a protocol that extends the capabilities of standard USB connections, allowing for faster charging and power delivery between devices. USB PD operates through negotiation between the source and sink devices. They exchange information through PDOs, determining the optimal power level for charging or powering the sink device. PPS enhances flexibility by allowing dynamic adjustments, and CC1 and CC2 are the communication channels involved in the negotiation process: 12 | - Source: The source is a device that provides power, such as a charger or power bank. It can negotiate with the connected device to determine the optimal power level. Here the USB PD power supply to be tested acts as the source. 13 | - Sink: The sink is a device that consumes power, like a smartphone or laptop. It communicates with the source to negotiate the power it needs for charging or other operations. Here the USB PD Tester acts as the sink. 14 | - PDO (Power Delivery Object): A PDO is a data structure exchanged between the source and sink during negotiation. It contains information about the available voltage and current levels. The devices negotiate and agree on a mutually supported PDO for power delivery. 15 | - PPS (Programmable Power Supply): PPS is a feature in USB PD that allows dynamic adjustment of the voltage and current levels during operation. It enables more flexible power delivery based on the specific needs of the connected device. 16 | - CC1 and CC2 (Configuration Channel 1 and 2): These are the communication channels used by USB Type-C connectors for negotiating power delivery. CC1 and CC2 lines carry information about the capabilities of the devices and facilitate negotiation. 17 | 18 | # Hardware 19 | ## Schematic 20 | ![USB_PD_Tester_wiring.png](https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/main/documentation/USB_PD_Tester_wiring.png) 21 | 22 | ## CH32X035 F7P6 32-bit RISC-V USB Microcontroller 23 | CH32X035F7P6 is a low-cost microcontroller that utilizes the QingKe 32-bit RISC-V4C core, supporting the RV32IMAC instruction set along with self-extending instructions. This microcontroller comes with a built-in USB PHY, supporting USB2.0 full-speed device functions and a USB PD PHY with source and sink capabilities. It features a programmable protocol I/O controller (PIOC), an operational amplifier (OPA) with programmable gain (PGA), an analog comparator (CMP), a 12-bit analog-to-digital converter (ADC), an 11-channel touch-key controller, 3 groups of USART, I2C, SPI, multiple timers, and various other peripheral resources. The device can operate at clock frequencies of up to 48MHz and is compatible with a supply voltage range of 2.0V to 5.5V. The CH32X035F7P6 includes 48KB of flash, 20KB of SRAM, and an embedded USB bootloader. 24 | 25 | ## 78L05 Voltage Regulator 26 | The 78L05 is a simple and inexpensive voltage regulator that can convert input voltages up to 30V to an output voltage of 5V with an output current of up to 100mA and a dropout voltage of 1.7V. The 78L05 supplies all elements of the circuit with up to 5V. 27 | 28 | ## SSD1306 OLED Display Module 29 | A low-cost SSD1306 4-pin I2C 128x64 pixels 0.96-inch OLED module is used as the display device. Make sure to acquire one with the correct pinout! 30 | 31 | ![USB_PD_Tester_pic2.jpg](https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/main/documentation/USB_PD_Tester_pic2.jpg) 32 | 33 | # Software 34 | ## USB Bootloader 35 | ### Installing Drivers for the Bootloader 36 | On Linux you do not need to install a driver. However, by default Linux will not expose enough permission to upload your code with the USB bootloader. In order to fix this, open a terminal and run the following commands: 37 | 38 | ``` 39 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="4348", ATTR{idProduct}=="55e0", MODE="666"' | sudo tee /etc/udev/rules.d/99-ch55x.rules 40 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="55e0", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-ch55x.rules 41 | sudo udevadm 42 | ``` 43 | 44 | For Windows you can use the [Zadig](https://zadig.akeo.ie/) tool to install the correct driver. Here, click "Options" -> "List All Devices" and select the USB module. Then install the libusb-win32 driver. To do this, the board must be connected and the microcontroller must be in bootloader mode. 45 | 46 | ### Entering Bootloader Mode 47 | The bootloader must be started manually for new uploads. To do this, the board must first be disconnected from the USB port. Now press the BOOT button and keep it pressed while reconnecting the board to the USB port of your PC. The chip now starts in bootloader mode, the BOOT button can be released and new firmware can be uploaded via USB within the next couple of seconds. 48 | 49 | ## Compiling and Uploading Firmware using the Makefile 50 | ### Linux 51 | Install the toolchain (GCC compiler, Python3, and chprog): 52 | ``` 53 | sudo apt install build-essential libnewlib-dev gcc-riscv64-unknown-elf 54 | sudo apt install python3 python3-pip 55 | pip install chprog 56 | ``` 57 | 58 | Open a terminal and navigate to the folder with the *makefile*. Press the BOOT button and keep it pressed while connecting the board to the USB port of your PC. Run the following command to compile and upload: 59 | ``` 60 | make flash 61 | ``` 62 | 63 | ### Other Operating Systems 64 | Follow the instructions on [CNLohr's ch32v003fun page](https://github.com/cnlohr/ch32v003fun/wiki/Installation) to set up the toolchain on your respective operating system (for Windows, use WSL). Also, install [Python3](https://www.pythontutorial.net/getting-started/install-python/) and [chprog](https://pypi.org/project/chprog/). Compile and upload with "make flash". Note that I only have Debian-based Linux and have not tested it on other operating systems. 65 | 66 | ## Compiling and Uploading Firmware using PlatformIO 67 | - Install [PlatformIO](https://platformio.org) and [platform-ch32v](https://github.com/Community-PIO-CH32V/platform-ch32v). Follow [these instructions](https://pio-ch32v.readthedocs.io/en/latest/installation.html) to do so. Linux/Mac users may also need to install [pyenv](https://realpython.com/intro-to-pyenv). 68 | - Click on "Open Project" and select the firmware folder with the *platformio.ini* file. 69 | - Press the BOOT button and keep it pressed while connecting the board to the USB port of your PC. Then click "Upload". 70 | 71 | ## Uploading pre-compiled Firmware Binary 72 | WCH offers the free but closed-source software [WCHISPTool](https://www.wch.cn/downloads/WCHISPTool_Setup_exe.html) to upload firmware with Windows via the USB bootloader. Press the BOOT button and keep it pressed while connecting the board to the USB port of your PC. Release the BOOT button, open the *pd_tester.hex* file in the *bin* folder with WCHISPTool and upload it to the microcontroller. 73 | 74 | If [Python3](https://www.pythontutorial.net/getting-started/install-python/) is installed, you can also use the platform-independent open-source command-line tool [chprog](https://pypi.org/project/chprog/) for uploading: 75 | ``` 76 | chprog bin/pd_tester.bin 77 | ``` 78 | 79 | # Building Instructions 80 | 1. Take the Gerber files (the *zip* file inside the *hardware* folder) and upload them to a PCB (printed circuit board) manufacturer of your choice (e.g., [JLCPCB](https://jlcpcb.com/)). They will use these files to create the circuit board for your device and send it to you. 81 | 2. Once you have the PCB, you can start soldering the components onto it. Use the BOM (bill of materials) and schematic as a guide to make sure everything is connected correctly. You can find the corresponding files in the *hardware* folder. Remove the plastic part from the pin header of the OLED, trim the pins, and solder the OLED module flush onto the PCB. Do not solder the screw terminal yet. 82 | 3. Print the casing with your 3D printer. You can find the corresponding *stl* files in the *3dprint* folder. 83 | 4. Insert the button extensions into the corresponding holes on the top part of the casing. Insert the circuit board and secure it with 4 self-tightening M2x5mm screws. 84 | 5. Now solder the screw terminal onto the circuit board. 85 | 6. Close the casing with the back panel. 86 | 7. Upload the firmware by following the instructions in the previous section (see above). 87 | 88 | ![USB_PD_Tester_pic3.jpg](https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/main/documentation/USB_PD_Tester_pic3.jpg) 89 | 90 | # Operating Instructions 91 | 1. Connect the USB PD Tester to a USB Type-C PD power supply using a USB-C cable. The available PDOs and their corresponding capabilities are displayed on the OLED. 92 | 2. Utilize the UP and DOWN buttons to choose the desired PDO. A left-angle bracket on the right side serves as the selection indicator. Programmable PDOs, indicated by numbers within square brackets, allow precise adjustments of the output voltage in 20mV steps within the specified range. 93 | 3. Press the SLCT button to activate the chosen PDO. An asterisk on the left side of the PDO confirms the activation. 94 | 4. The selected voltage is now accessible on the screw terminal for further use. 95 | 96 | ![USB_PD_Tester_pic4.jpg](https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/main/documentation/USB_PD_Tester_pic4.jpg) 97 | ![USB_PD_Tester_pic5.jpg](https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/main/documentation/USB_PD_Tester_pic5.jpg) 98 | 99 | # References, Links and Notes 100 | - [EasyEDA Design Files](https://oshwlab.com/wagiminator) 101 | - [MCU Templates](https://github.com/wagiminator/MCU-Templates) 102 | - [MCU Flash Tools](https://github.com/wagiminator/MCU-Flash-Tools) 103 | - [CH32X035 Datasheets](http://www.wch-ic.com/products/CH32X035.html) 104 | - [SSD1306 Datasheet](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) 105 | - [78L05 Datasheet](https://datasheet.lcsc.com/lcsc/2209271730_HX-hengjiaxing-78L05_C5181466.pdf) 106 | - [ATtiny814 USB PD Adapter](https://github.com/wagiminator/ATtiny814-USB-PD-Adapter) 107 | - [ATtiny412 USB PD Inverter](https://github.com/wagiminator/ATtiny412-USB-PD-Inverter) 108 | - [TI Primer on USB PD](https://www.ti.com/lit/wp/slyy109b/slyy109b.pdf) 109 | - [CH32X035 F7P6 on Aliexpress](https://aliexpress.com/item/1005006199310724.html) 110 | - [128x64 OLED on Aliexpress](https://aliexpress.com/wholesale?SearchText=128+64+0.96+oled) 111 | 112 | ![USB_PD_Tester_pic6.jpg](https://raw.githubusercontent.com/wagiminator/CH32X035-USB-PD-Tester/main/documentation/USB_PD_Tester_pic6.jpg) 113 | 114 | # License 115 | ![license.png](https://i.creativecommons.org/l/by-sa/3.0/88x31.png) 116 | 117 | This work is licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License. 118 | (http://creativecommons.org/licenses/by-sa/3.0/) 119 | -------------------------------------------------------------------------------- /software/pd_tester/bin/pd_tester.hex: -------------------------------------------------------------------------------- 1 | :040000006F108024D9 2 | :10000400000000000000000036020000360200007C 3 | :1000140000000000360200000000000000000000A4 4 | :10002400360200003602000000000000000000005C 5 | :10003400360200000000000036020000000000004C 6 | :100044003602000036020000360200000000000004 7 | :1000540036020000360200003602000036020000BC 8 | :1000640036020000360200003602000036020000AC 9 | :10007400360200003602000036020000360200009C 10 | :10008400360200003602000036020000360200008C 11 | :10009400360200003602000036020000360200007C 12 | :1000A400360200003602000036020000360200006C 13 | :1000B400360200003602000036020000360200005C 14 | :1000C400360200001E08000036020000360200005E 15 | :0C00D40036020000360200003602000078 16 | :1000E000B7F700E09C4737F700E03E951C47898F43 17 | :1000F000E3CE07FE828079714ECE5EC6B70910FF4F 18 | :10010000B70B00F122D426D252CC56CA5AC862C4C8 19 | :1001100066C206D64AD0AA84A14A37140140FD1B04 20 | :10012000370C0003B70C0004FD19370B4000370AE9 21 | :1001300030000327048093978401E18733777701A8 22 | :1001400063DA0708336797012320E48003270480DC 23 | :100150000945FD1A33773701336767012320E480AF 24 | :10016000413703270480860493FAFA0F3377370167 25 | :10017000336747012320E48093F4F40FE39B0AFAEA 26 | :1001800003270480B70700F1FD177D8FB707000430 27 | :100190005D8F2320E480832704800945B3F737016E 28 | :1001A000B3E767012320F480253F83270480B25002 29 | :1001B0009254B3F93701B3E9490123203481225421 30 | :1001C0000259F249624AD24A424BB24B224C924CFB 31 | :1001D000456182803367870185BF411122C437148E 32 | :1001E000014003270480B70700F126C29384F7FF7C 33 | :1001F000658FB707000306C65D8F2320E48009459D 34 | :10020000C53503270480B70710FFFD177D8FB7079B 35 | :1002100040005D8F2320E4800945D9358327048081 36 | :1002200037070004B240E58FD98F2320F4802244A1 37 | :1002300092444101828001A09387C18603C7470190 38 | :1002400083C65701158F1377F70F6368A700DC4744 39 | :100250000A053E950355E5FF8280198D1947330540 40 | :10026000E5029C4B3E950355A5FF82809387C1868E 41 | :1002700003C7470183C65701158F1377F70F6368CC 42 | :10028000A700DC470A053E950355E5FF8280198DDE 43 | :1002900019473305E5029C4B3E950355C5FF828007 44 | :1002A0009387C18603C7470183C65701158F13770C 45 | :1002B000F70F6368A700DC470A053E950355C5FFA5 46 | :1002C0008280198D19473305E5029C4B3E950355F5 47 | :1002D000E5FF8280B77702401307200A2396E700E4 48 | :1002E0002397E7009387C186A3840700238507002F 49 | :1002F00023840700238A0700A38A070023A007009E 50 | :1003000023A207001387418323810702D8C7A38153 51 | :1003100007021387C1802382070298CB0547238BEE 52 | :10032000E70023800702238DE700A38DE700056720 53 | :1003300013078738239EE700239FE7008280B77763 54 | :10034000024003D70700118B21C303D7E700420700 55 | :100350004183136707012397E700B777024013072C 56 | :10036000F0042391E7004205A38207004181239313 57 | :10037000A700A384070003C747001377F70F13678D 58 | :1003800037002382E700828003D7C70042074183FA 59 | :10039000136707012396E700C9B7797122D426D2E3 60 | :1003A0004AD04ECE52CC56CA9387C18606D65AC87A 61 | :1003B0005EC6239F07000569238007029304F00FA0 62 | :1003C0001384C1861309892D93894183138A8181FE 63 | :1003D000834B040293FBFB0F63960B00FD1493F415 64 | :1003E000F40F91ECB25022540259F249624AD24AB7 65 | :1003F000424BB24B3335900092544561828037B501 66 | :10040000030013050598E939B777024003D7C70001 67 | :10041000420741831377E7F1420741832396E700C0 68 | :1004200003D7C70042074183136707042396E700F9 69 | :1004300083D6C70003D7E700C2064207418313777C 70 | :10044000E7F1420741832397E70003D7E700C18222 71 | :1004500042074183136707042397E70083D7E70028 72 | :100460000947858B99E313F7160083578401854666 73 | :100470008507C207C183231CF4001C406393D70483 74 | :100480006318F70823050400834794009546850701 75 | :1004900093F7F70FA304F4008347940093F7F70F43 76 | :1004A00063F1F602A304040089471CC0B77602403A 77 | :1004B00083D70600C207C183ED9BC207C183239087 78 | :1004C000F6001440854763FBD70249E78347840061 79 | :1004D0001547850793F7F70F2304F400834784003B 80 | :1004E00093F7F70F637CF700230404002320040034 81 | :1004F000B7E700E03707020023A2E718032B040048 82 | :100500001C402547636FF7088A07CA979C43828778 83 | :100510008947A3040400631CF7028347A40095469F 84 | :10052000850793F7F70F2305F4008347A40093F79B 85 | :10053000F70FE3F8F6F82305040018C0B776024079 86 | :1005400083D70600C207C18393E747008DBF230509 87 | :10055000040085BF2304040055B7B7E700E0370760 88 | :10056000020023A2E718BD3385471CC01DA85840D0 89 | :100570001C406308F7023777024093077007231186 90 | :10058000F7008347470093F7C70F93E7270023023D 91 | :10059000F700B7E700E0A38807423707020023A26D 92 | :1005A000E710232264012DB58347340293F7F70F38 93 | :1005B000EDDBA3010402B7E700E0370702000328E0 94 | :1005C000C400144823A2E718A30A0400B70800F0E7 95 | :1005D000370300C0294E9305200319461305400632 96 | :1005E0008347440193F7F70F63EAFB00B7E700E0A6 97 | :1005F0003707020023A2E710914785BF13972B000E 98 | :10060000B307EA009C43B3FE170163986E060347E5 99 | :10061000540193DE170193FEFE0F1377F70F330794 100 | :10062000C702B38EAE0236972311D7010347540198 101 | :1006300093DE870093FEFE0F1377F70F3307C70291 102 | :1006400093F7F707B38EAE0236972310D70103470F 103 | :100650005401B387B7021377F70F3307C7023697F2 104 | :100660002312F70083475401850793F7F70FA30A76 105 | :10067000F400850B93FBFB0FA5B793FEF73FA9830F 106 | :1006800093F7F73FB38ECE034297B387B702231099 107 | :10069000D7012311F700F1BF58401C406300F70C4D 108 | :1006A0008347A401834624020565130725009D8A1C 109 | :1006B000A606D98E21671307F7E1F98E0347640177 110 | :1006C00093F6F6F313F6F70F0D8B1A07D98E03473F 111 | :1006D0004401034854019395C7011377F70F13782A 112 | :1006E000F80F330707416355C7088357C4015147C3 113 | :1006F000C207C183B3D7E7021307F5FFF98FA60737 114 | :1007000003475401CD8F834544011377F70F329788 115 | :1007100093F5F50F0D8F19467D173307C702138622 116 | :10072000C180329703574700130620033357C7028F 117 | :10073000370600031377F707518F370600FD1306BE 118 | :1007400006F8F18FD98F138781842311F700C183B5 119 | :100750002310D7002312F7001945D53683474402EA 120 | :1007600093F7F70FE38F07E2230204029547F5BBE7 121 | :100770001307F6FF0A074E9703570700A9473706E6 122 | :1007800000033357F7021377F73F9317A700518FF2 123 | :100790005D8FB367B70045BF8347340293F7F70F08 124 | :1007A000E38107E0A3010402A5471CC0231C040049 125 | :1007B000CDBB85472300F4020347A4018347B4015E 126 | :1007C0001377F70F93F7F70F631CF7000357C40174 127 | :1007D0008357E4014207C2074183C183E303F7DC87 128 | :1007E0008347A401094593F7F70FA30DF40083573E 129 | :1007F000C401C207C183231FF400230004028347FE 130 | :100800002402034764019D8BA6070D8B1A0793E70B 131 | :100810007700D98F2394F1841D36A947B9B35D7150 132 | :100820003ED0B77702403AD203C7970086C696C437 133 | :100830009AC29EC022DE26DC2ADA2ED832D636D4E0 134 | :1008400042CE46CC4ACA72C876C67AC47EC21377F4 135 | :10085000070235CB03C7970085460D8B631BD70472 136 | :1008600083D7A7009546C207C18363F4F60403D477 137 | :1008700081841389818493160401C1869384C1867F 138 | :1008800063CE060E1D66618E9376F4016310061228 139 | :100890008D476383F60E99476388F6106390E60EE2 140 | :1008A0002382D40283C72402850793F7F70F23819D 141 | :1008B000F402377702408347970093F7F70F93E7E7 142 | :1008C0000702A304F700B777024003C79700620747 143 | :1008D0006187635D070403D7C700854642074183EC 144 | :1008E0003D9B420741832396E70003D7E700420779 145 | :1008F00041833D9B420741832397E700130770071D 146 | :100900002391E70003C747001377C70F136727003A 147 | :100910002382E7001387C186A301D70203C797008C 148 | :10092000136707F81377F70FA384E70037770240C0 149 | :100930008347970093F7070491CB8347970093F77A 150 | :10094000F70F93E70704A304F70069327254B64027 151 | :10095000A64216438643E2545255C2553256A25619 152 | :10096000125782577248E2485249424EB24E224FC5 153 | :10097000924F6161730020309D479CC01305005A5F 154 | :10098000EFF00FF6A381040203C7640193579400AC 155 | :100990009D8BA6070D8B1A0793E71700D98F094582 156 | :1009A0002310F900693A31B7A147C1BFE398E6FCCB 157 | :1009B0000D4798C01357C4001D8B238AE4001357BA 158 | :1009C00064000D8B238BE40093878184014771467B 159 | :1009D000938681818507E303C7FA03C51700B385B2 160 | :1009E000E60005072380A500F5B7411122C437149E 161 | :1009F000014083270480370700F17D17F98F3707FF 162 | :100A0000000306C6D98F2320F4800945EFF04FED8F 163 | :100A100083270480370710FF7D17F98F37073000D1 164 | :100A2000D98F2320F4802244B2401305800741016E 165 | :100A30006FF06FEC411122C426C206C69D47AA84FE 166 | :100A4000014463E3B7002E8423849180A3848180D2 167 | :100A5000693F0145EFF02FEA130404FB1375F40F0F 168 | :100A6000EFF06FE913F5F400EFF0EFE813D5440071 169 | :100A700013650501EFF02FE82244B2409244410192 170 | :100A80006FF0AFF5411122C44AC006C626C2214408 171 | :100A90001309F00F7D141374F40F63182401B2408E 172 | :100AA00022449244024941018280A285014559377E 173 | :100AB0002D3F13050004EFF00FE493040008FD142C 174 | :100AC000014593F4F40FEFF00FE3F5F8EFF0EFF0DA 175 | :100AD000A285014585377DBF011501114205418180 176 | :100AE0004EC603C7818022CC131425002A944204E9 177 | :100AF00006CE26CA4AC89307A00741809389818001 178 | :100B000063F9E70083C591800145850593F5F50FED 179 | :100B10001537E13D13050004EFF0EFDD93045400B9 180 | :100B20000145C2040569EFF00FDDC1801309093AE0 181 | :100B3000A287CA97050403C5070042044180EFF06D 182 | :100B40008FDBE39784FEEFF04FE983C70900F240A3 183 | :100B5000624499072380F900D2444249B2490561B1 184 | :100B60008280AA8793F707061375F50791C3ADB77F 185 | :100B7000A9476319F50083C59180850593F5F50FA5 186 | :100B800001454DBDB5476315F50083C59180CDBFC7 187 | :100B90008280014863DC0500B305B040E205E185D1 188 | :100BA000635605003305A0401308D00293080002E5 189 | :100BB000635806003306C04062066186930800034E 190 | :100BC000D1478146254E63D3C70A8280BE86B3775C 191 | :100BD000B50213F7F70F6370FE0813077703335559 192 | :100BE000B50200101377F70F3303D4009387160074 193 | :100BF0002306E3FE93F7F70F71F9630C0800130760 194 | :100C000000036388E800A2978906238607FF93F70D 195 | :100C1000F60F3E8763C4C704630D08009306000304 196 | :100C20006399D80014108507369793F7F70F2306BA 197 | :100C300007FF1384F7FF64001374F40F2694034531 198 | :100C400004000537A2877D14E39BF4FEB2502254C2 199 | :100C50009254456182801307070351B7141085072A 200 | :100C6000369793F7F70F230617FF65B7797106D606 201 | :100C700022D426D2A9BF411122C4370400208345C3 202 | :100C8000140026C2AA84FD1593F5F50F13058007FD 203 | :100C900006C64D331305000281351387C18683468E 204 | :100CA00047019307140093F6F60F63F49602054785 205 | :100CB0002380E70083C5070013058007FD1593F522 206 | :100CC000F50F8D3B2244B24092441305C00341010D 207 | :100CD00021B581E403474701E1BF23809700D9BFD5 208 | :100CE000411126C2B70400209384040083C5040088 209 | :100CF00022C42A84FD1593F5F50F014506C61D3B58 210 | :100D000013050002D13B9305F4FF014593F5F50F60 211 | :100D10002380840005332244B24092441305A0028C 212 | :100D200041015DBB1D7126DAA40022DC4ED652D4EF 213 | :100D300056D25AD05ECE62CC06DE4AD82E84B2C4D9 214 | :100D4000B6C6BAC8BECAC2CCC6CE26C69309500221 215 | :100D5000130A0003A54A294B930B3007130C3006E6 216 | :100D60000345040009EDF2506254D2544259B2597D 217 | :100D7000225A925A025BF24B624C256182806306D2 218 | :100D800035010504F93B2689A1A883471400638C2B 219 | :100D9000370109040147639647018347040005476B 220 | :100DA0000504014639A8130550023D33834724004A 221 | :100DB0000D04CDB7B30766030504B6973E96834787 222 | :100DC000F4FF938607FDE3F7DAFE19C33306C0404C 223 | :100DD000639D770113894400844003C5040019E131 224 | :100DE000CA84BDBF8504B53BCDBF6398870103C5E9 225 | :100DF000040013894400B533E5B7B5D7130750078E 226 | :100E00006389E70213074006638DE7001307800735 227 | :100E10006387E702130720068945638DE7007D1489 228 | :100E20009DB76206138944006186D9558840953381 229 | :100E300045BFA9456206138944006186C5BFC14507 230 | :100E4000D5BF4111B70700204AC003C9170026C209 231 | :100E5000AA844A8522C406C6EFF00FBE13842180FF 232 | :100E6000636895024A85EFF02FBD2310A4009945D1 233 | :100E70007945C936035604002244B24092440249DF 234 | :100E80008565056593850530130525B6410159BD76 235 | :100E90004A85EFF0AFBD63E6A4004A85EFF00FBDD1 236 | :100EA000E9B723109400E1B7797126D256CAB70486 237 | :100EB000004022D44AD04ECE52CC06D60544D93674 238 | :100EC000938AC18605698569FD14056A83C74A014D 239 | :100ED00093F7F70F63F48702B707002003C51700E5 240 | :100EE000593BB70700202254B25092540259F2499C 241 | :100EF000624AD24A03C507004561DDB383C74A0190 242 | :100F000003C75A01998F93F7F70F63EA870203A784 243 | :100F1000CA00B30794008A07BA9783D627002285B0 244 | :100F200036C6EFF0EFB7B2462A8722869305CA305D 245 | :100F3000130529B60504FD331374F40F41BF228550 246 | :100F4000EFF08FAF2AC62285EFF04FB2B2462A8764 247 | :100F5000228693850932E9BFB717014003A70780AE 248 | :100F6000B70610FFFD167971758FB706400006D6DB 249 | :100F700022D426D24AD04ECE52CC56CA5AC85EC6C9 250 | :100F800062C466C26AC0558F23A0E78003A70780AA 251 | :100F9000B70600F1FD16758FB7060004558F13864E 252 | :100FA000078023A0E780930700025CCA37A52400CE 253 | :100FB000930700045CCA130505F0EFF06F92353417 254 | :100FC000014505690144EFF00F9381441309095865 255 | :100FD000AD49B307990003C507008504EFF0AF9151 256 | :100FE000E39934FFEFF06F9F713C05698565938548 257 | :100FF0004533130529B63D33B7170240984FB7065E 258 | :101000000200B71401401367170198CFD84BCA8A62 259 | :10101000558FD8CB83A744C0B7060044370701409B 260 | :10102000A207A183D58F23A2F4C01C4F93E70730FA 261 | :101030001CCF93878184C20737770240C18323186E 262 | :10104000F700F977A1072310F700F157A304F70081 263 | :10105000EFF0AFB401E9856593854534130529B6F2 264 | :10106000D13101A0913583A70480B706F1FFFD16A9 265 | :10107000C19B93E7870023A0F480854723A8F480D1 266 | :1010800083A70480B70B002093F7F7F093E70708D6 267 | :1010900023A0F480894723A8F48083A70480938940 268 | :1010A000C186856CF58FB7060800D58F23A0F48024 269 | :1010B000C14723A8F480056DB714014083A784803D 270 | :1010C000858B85CF83A78480898B99EB03C51B0013 271 | :1010D00005051375F50F453683A78480898BEDDFF1 272 | :1010E00083A78480C18B8DC7EFF02FAB37550700E6 273 | :1010F00013050530EFE0DFFED1B703C51B007D15FA 274 | :101100001375F50F8D3E83A78480858BEDDF5DBF62 275 | :1011100083C7490103C7590103C91B00998F93F77E 276 | :10112000F70F63E7270303A7C90093172900238D4F 277 | :101130002901BA9783D7E7FF239EF900EFF0EFA5C7 278 | :1011400019C14A85713E83A78480C18BEDDF69BFD9 279 | :10115000EFF05F934A869385CC3413852AB6D9364F 280 | :101160004A85EFF06F8D2A8693054D3613852AB692 281 | :10117000553E4A85EFF08F8F85652A8693858537A2 282 | :1011800013852AB645364A85EFF08F9185652A8604 283 | :101190009385C53813852AB6713603D52180138B04 284 | :1011A0002180453183A78480C18BEDDF375A07004A 285 | :1011B00013050A30EFE0DFF2130CF00F130A0A30C8 286 | :1011C00083A784801387048003550B00C18B91EFA4 287 | :1011D000238D2901239EA900EFF02F9C09C5B70795 288 | :1011E000002023802701C931B9BF1C47858B95E7B3 289 | :1011F000510542054181B1317D141374F40F63161A 290 | :1012000084010944EFF06F9965BF83A78480858BC3 291 | :10121000EDFB5285EFE0DFECC5B71C47898B95E30A 292 | :1012200031154205418131397D141374F40FE30AFD 293 | :1012300084FD83A78480898BE9F75285EFE05FEA1C 294 | :10124000E5B7130420037DBF8567938787F597F182 295 | :10125000FF1F9381215B174100201301A1DA7D4517 296 | :10126000731005BC13058008732005308D457390FD 297 | :101270004580130540004D8D731055307390173421 298 | :10128000B70700200567938707001307C7589386A1 299 | :10129000818063E8D702938781801387418963E85F 300 | :1012A000E702B7270240094798C3B717024023A2B5 301 | :1012B000070037F700E0954614C3714798CF7300D5 302 | :1012C000203010431107910723AEC7FED9B791070D 303 | :1012D00023AE07FEE9B700005A050000A205000092 304 | :1012E0006E050000A805000098060000A205000099 305 | :1012F000A2050000A205000098070000B207000048 306 | :101300003E2536646D56203C0000000020282564F0 307 | :10131000292536646D56202535646D412000000076 308 | :10132000205B25645D2536646D562D2535646D562C 309 | :1013300020000000436F6E6E656374696E672E2E29 310 | :101340002E0000004641494C4544000053656C6541 311 | :10135000637420766F6C74616765206F66205B250F 312 | :10136000645D0A006D696E20766F6C746167653A22 313 | :101370002537646D560000006D617820766F6C74BF 314 | :101380006167653A2537646D560000006D6178200D 315 | :1013900063757272656E743A2537646D41000000A2 316 | :1013A000000000000000005F0000000700070014BC 317 | :1013B0007F147F14242A7F2A12231308646236497B 318 | :1013C0005522500004030000001C2241000041226D 319 | :1013D0001C0014083E081408083E0808008060003D 320 | :1013E00000080808080800606000002010080402D7 321 | :1013F0003E5149453E44427F404042615149462268 322 | :10140000414949361814127F102F494949313E4944 323 | :101410004949320301710907364949493626494984 324 | :10142000493E0036360000008068000000081422A3 325 | :1014300000141414141400221408000201510906A7 326 | :101440003E415D555E7C1211127C7F494949363E12 327 | :10145000414141227F4141221C7F494949417F0945 328 | :101460000909013E4149493A7F0808087F41417F07 329 | :1014700041412040413F017F081422417F404040CC 330 | :10148000407F020C027F7F0408107F3E4141413EB5 331 | :101490007F090909063E4141C1BE7F091929462637 332 | :1014A0004949493201017F01013F4040403F1F202F 333 | :1014B00040201F3F4038403F631408146307087002 334 | :1014C00008076151494543007F414100020408106B 335 | :1014D000200041417F000804020408404040404091 336 | :1014E000000609090620545454787F44444438388F 337 | :1014F00044444428384444447F385454541808FE23 338 | :1015000009010218A4A4A4787F0404047800447D8F 339 | :1015100040000080847D00417F10284400417F40CE 340 | :10152000007C047C04787C04040478384444443807 341 | :10153000FC2424241818242424FC7C08040408080F 342 | :1015400054545420043F4440203C4040403C1C2024 343 | :1015500040201C3C4030403C44281028441CA0A0A3 344 | :10156000A07C4464544C4408083641410000FF000C 345 | :101570000041413608080804081008FFFFFFFFFF7C 346 | :0C158000A83F8D142000DA12A1C8AF00B3 347 | :08158C000101881300000000BA 348 | :00000001FF 349 | -------------------------------------------------------------------------------- /software/pd_tester/src/system.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Basic System Functions for CH32X035/X034/X033 * v1.1 * 3 | // =================================================================================== 4 | // 5 | // This file must be included!!!! 6 | // 7 | // References: 8 | // ----------- 9 | // - WCH Nanjing Qinheng Microelectronics: http://wch.cn 10 | // 11 | // 2023 by Stefan Wagner: https://github.com/wagiminator 12 | 13 | #include "system.h" 14 | 15 | // =================================================================================== 16 | // Setup Microcontroller (this function is called automatically at startup) 17 | // =================================================================================== 18 | void SYS_init(void) { 19 | 20 | // Init system clock 21 | #if SYS_CLK_INIT > 0 22 | #if F_CPU <= 12000000 23 | //FLASH->ACTLR = FLASH_ACTLR_LATENCY_0; 24 | #elif F_CPU <= 24000000 25 | FLASH->ACTLR = FLASH_ACTLR_LATENCY_1; 26 | #else 27 | FLASH->ACTLR = FLASH_ACTLR_LATENCY_2; 28 | #endif 29 | RCC->CFGR0 = SYS_CLK_DIV; 30 | #endif 31 | 32 | // Init SYSTICK 33 | #if SYS_TICK_INIT > 0 34 | STK_init(); 35 | #endif 36 | 37 | // Enable GPIO 38 | #if SYS_GPIO_EN > 0 39 | RCC->APB2PCENR = RCC_IOPAEN | RCC_IOPBEN | RCC_IOPCEN; 40 | #endif 41 | } 42 | 43 | // =================================================================================== 44 | // System Clock Functions 45 | // =================================================================================== 46 | 47 | // Reset system clock to default state 48 | void CLK_reset(void) { 49 | RCC->CTLR |= (uint32_t)0x00000001; 50 | RCC->CFGR0 = (uint32_t)0x00000050; 51 | FLASH->ACTLR = (uint32_t)0x00000000; 52 | } 53 | 54 | // Setup pin PB9 for MCO (output, push-pull, alternate) 55 | void MCO_init(void) { 56 | RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPBEN; 57 | GPIOB->CFGHR = (GPIOB->CFGHR & ~((uint32_t)0b1111<<(1<<2))) | ((uint32_t)0b1011<<(1<<2)); 58 | RCC->CFGR0 |= RCC_CFGR0_MCO_SYSCLK; 59 | } 60 | 61 | // =================================================================================== 62 | // Delay Functions 63 | // =================================================================================== 64 | 65 | // Wait n counts of SysTick 66 | void DLY_ticks(uint32_t n) { 67 | uint32_t end = STK->CNTL + n; 68 | while(((int32_t)(STK->CNTL - end)) < 0); 69 | } 70 | 71 | // =================================================================================== 72 | // Bootloader (BOOT) Functions 73 | // =================================================================================== 74 | 75 | // Perform software reset and jump to bootloader 76 | void BOOT_now(void) { 77 | FLASH->KEYR = FLASH_KEY1; 78 | FLASH->KEYR = FLASH_KEY2; 79 | FLASH->BOOT_MODEKEYR = FLASH_KEY1; 80 | FLASH->BOOT_MODEKEYR = FLASH_KEY2; // unlock flash 81 | FLASH->STATR |= FLASH_STATR_BOOT_MODE; // start bootloader after software reset 82 | FLASH->CTLR |= FLASH_CTLR_LOCK; // lock flash 83 | RCC->RSTSCKR |= RCC_RMVF; // clear reset flags 84 | NVIC->CFGR = NVIC_RESETSYS | NVIC_KEY3; // perform software reset 85 | } 86 | 87 | // =================================================================================== 88 | // Independent Watchdog Timer (IWDG) Functions 89 | // =================================================================================== 90 | 91 | // Start independent watchdog timer (IWDG) with given amount of WDG clock cycles 92 | // (ticks). The IWDG clock is HSI/1024/prescaler. One tick is 64*1024*1000/48000000 ms 93 | // long, max ticks is 4095 = 5591ms. 94 | // Once the IWDG has been started, it cannot be disabled, only reloaded (feed). 95 | void IWDG_start_t(uint16_t ticks) { 96 | IWDG->CTLR = 0x5555; // allow register modification 97 | while(IWDG->STATR & IWDG_PVU); // wait for clock register to be ready 98 | IWDG->PSCR = 0b100; // set clock prescaler 64 99 | while(IWDG->STATR & IWDG_RVU); // wait for reload register to be ready 100 | IWDG->RLDR = ticks; // set watchdog counter reload value 101 | IWDG->CTLR = 0xAAAA; // load reload value into watchdog counter 102 | IWDG->CTLR = 0xCCCC; // enable IWDG 103 | } 104 | 105 | // Reload watchdog counter with n ticks, n<=4095 106 | void IWDG_reload_t(uint16_t ticks) { 107 | IWDG->CTLR = 0x5555; // allow register modification 108 | while(IWDG->STATR & IWDG_RVU); // wait for reload register to be ready 109 | IWDG->RLDR = ticks; // set watchdog counter reload value 110 | IWDG->CTLR = 0xAAAA; // load reload value into watchdog counter 111 | } 112 | 113 | // =================================================================================== 114 | // Automatic Wake-up Timer (AWU) Functions 115 | // =================================================================================== 116 | 117 | // Init and start automatic wake-up timer 118 | void AWU_init(void) { 119 | AWU->CSR = 0x02; // enable automatic wake-up timer 120 | EXTI->EVENR |= ((uint32_t)1<<27); // enable AWU event 121 | EXTI->RTENR |= ((uint32_t)1<<27); // enable AWU rising edge triggering 122 | } 123 | 124 | // Stop automatic wake-up timer 125 | void AWU_stop(void) { 126 | AWU->CSR = 0x00; // disable automatic wake-up timer 127 | EXTI->EVENR &= ~((uint32_t)1<<27); // disable AWU event 128 | EXTI->RTENR &= ~((uint32_t)1<<27); // disable AWU rising edge triggering 129 | } 130 | 131 | // =================================================================================== 132 | // Sleep Functions 133 | // =================================================================================== 134 | 135 | // Put device into sleep, wake up by interrupt 136 | void SLEEP_WFI_now(void) { 137 | PFIC->SCTLR &= ~PFIC_SLEEPDEEP; // set power-down mode to SLEEP 138 | __WFI(); // wait for interrupt 139 | } 140 | 141 | // Put device into sleep, wake up by event 142 | void SLEEP_WFE_now(void) { 143 | PFIC->SCTLR &= ~PFIC_SLEEPDEEP; // set power-down mode to SLEEP 144 | __WFE(); // wait for event 145 | } 146 | 147 | // Put device into stop, wake up interrupt 148 | void STOP_WFI_now(void) { 149 | PFIC->SCTLR |= PFIC_SLEEPDEEP; // set power-down mode to STOP 150 | __WFI(); // wait for interrupt 151 | } 152 | 153 | // Put device into stop, wake up event 154 | void STOP_WFE_now(void) { 155 | PFIC->SCTLR |= PFIC_SLEEPDEEP; // set power-down mode to STOP 156 | __WFE(); // wait for event 157 | } 158 | 159 | // Put device into standby (deep sleep), wake up interrupt 160 | void STDBY_WFI_now(void) { 161 | RCC->APB1PCENR |= RCC_PWREN; // enable power module 162 | PWR->CTLR |= PWR_CTLR_PDDS; // set power-down mode to STANDBY 163 | PFIC->SCTLR |= PFIC_SLEEPDEEP; 164 | __WFI(); // wait for interrupt 165 | PWR->CTLR &= ~PWR_CTLR_PDDS; // reset power-down mode 166 | } 167 | 168 | // Put device into standby (deep sleep), wake up event 169 | void STDBY_WFE_now(void) { 170 | RCC->APB1PCENR |= RCC_PWREN; // enable power module 171 | PWR->CTLR |= PWR_CTLR_PDDS; // set power-down mode to STANDBY 172 | PFIC->SCTLR |= PFIC_SLEEPDEEP; 173 | __WFE(); // wait for event 174 | PWR->CTLR &= ~PWR_CTLR_PDDS; // reset power-down mode 175 | } 176 | 177 | // =================================================================================== 178 | // C++ Support 179 | // =================================================================================== 180 | #ifdef __cplusplus 181 | extern void __cxa_pure_virtual() { while (1); } 182 | extern void (*__preinit_array_start[]) (void) __attribute__((weak)); 183 | extern void (*__preinit_array_end[]) (void) __attribute__((weak)); 184 | extern void (*__init_array_start[]) (void) __attribute__((weak)); 185 | extern void (*__init_array_end[]) (void) __attribute__((weak)); 186 | 187 | void __libc_init_array(void) { 188 | uint32_t count, i; 189 | count = __preinit_array_end - __preinit_array_start; 190 | for (i = 0; i < count; i++) __preinit_array_start[i](); 191 | count = __init_array_end - __init_array_start; 192 | for (i = 0; i < count; i++) __init_array_start[i](); 193 | } 194 | #endif 195 | 196 | // =================================================================================== 197 | // C version of CH32X035 Startup .s file from WCH 198 | // =================================================================================== 199 | extern uint32_t _sbss; 200 | extern uint32_t _ebss; 201 | extern uint32_t _data_lma; 202 | extern uint32_t _data_vma; 203 | extern uint32_t _edata; 204 | 205 | // Prototypes 206 | int main(void) __attribute__((section(".text.main"), used)); 207 | void jump_reset(void) __attribute__((section(".init.jump"), naked, used)); 208 | void (*const vectors[])(void) __attribute__((section(".vector"), used)); 209 | void reset_handler(void) __attribute__((section(".text.reset_handler"), naked, used)); 210 | 211 | #if SYS_USE_VECTORS > 0 212 | // Unless a specific handler is overridden, it just spins forever 213 | void default_handler(void) __attribute__((section(".text.vector_handler"), naked, used)); 214 | void default_handler(void) { while(1); } 215 | 216 | // All interrupt handlers are aliased to default_handler unless overridden individually 217 | #define DUMMY_HANDLER __attribute__((section(".text.vector_handler"), weak, alias("default_handler"), used)) 218 | DUMMY_HANDLER void NMI_Handler(void); 219 | DUMMY_HANDLER void HardFault_Handler(void); 220 | DUMMY_HANDLER void Ecall_M_Mode_Handler(void); 221 | DUMMY_HANDLER void Ecall_U_Mode_Handler(void); 222 | DUMMY_HANDLER void Break_Point_Handler(void); 223 | DUMMY_HANDLER void SysTick_Handler(void); 224 | DUMMY_HANDLER void SW_Handler(void); 225 | DUMMY_HANDLER void WWDG_IRQHandler(void); 226 | DUMMY_HANDLER void PVD_IRQHandler(void); 227 | DUMMY_HANDLER void FLASH_IRQHandler(void); 228 | DUMMY_HANDLER void EXTI7_0_IRQHandler(void); 229 | DUMMY_HANDLER void AWU_IRQHandler(void); 230 | DUMMY_HANDLER void DMA1_Channel1_IRQHandler(void); 231 | DUMMY_HANDLER void DMA1_Channel2_IRQHandler(void); 232 | DUMMY_HANDLER void DMA1_Channel3_IRQHandler(void); 233 | DUMMY_HANDLER void DMA1_Channel4_IRQHandler(void); 234 | DUMMY_HANDLER void DMA1_Channel5_IRQHandler(void); 235 | DUMMY_HANDLER void DMA1_Channel6_IRQHandler(void); 236 | DUMMY_HANDLER void DMA1_Channel7_IRQHandler(void); 237 | DUMMY_HANDLER void ADC1_IRQHandler(void); 238 | DUMMY_HANDLER void I2C1_EV_IRQHandler(void); 239 | DUMMY_HANDLER void I2C1_ER_IRQHandler(void); 240 | DUMMY_HANDLER void USART1_IRQHandler(void); 241 | DUMMY_HANDLER void SPI1_IRQHandler(void); 242 | DUMMY_HANDLER void TIM1_BRK_IRQHandler(void); 243 | DUMMY_HANDLER void TIM1_UP_IRQHandler(void); 244 | DUMMY_HANDLER void TIM1_TRG_COM_IRQHandler(void); 245 | DUMMY_HANDLER void TIM1_CC_IRQHandler(void); 246 | DUMMY_HANDLER void TIM2_UP_IRQHandler(void); 247 | DUMMY_HANDLER void USART2_IRQHandler(void); 248 | DUMMY_HANDLER void EXTI15_8_IRQHandler(void); 249 | DUMMY_HANDLER void EXTI25_16_IRQHandler(void); 250 | DUMMY_HANDLER void USART3_IRQHandler(void); 251 | DUMMY_HANDLER void USART4_IRQHandler(void); 252 | DUMMY_HANDLER void DMA1_Channel8_IRQHandler(void); 253 | DUMMY_HANDLER void USBFS_IRQHandler(void); 254 | DUMMY_HANDLER void USBFSWakeUp_IRQHandler(void); 255 | DUMMY_HANDLER void PIOC_IRQHandler(void); 256 | DUMMY_HANDLER void OPA_IRQHandler(void); 257 | DUMMY_HANDLER void USBPD_IRQHandler(void); 258 | DUMMY_HANDLER void USBPDWakeUp_IRQHandler(void); 259 | DUMMY_HANDLER void TIM2_CC_IRQHandler(void); 260 | DUMMY_HANDLER void TIM2_TRG_COM_IRQHandler(void); 261 | DUMMY_HANDLER void TIM2_BRK_IRQHandler(void); 262 | DUMMY_HANDLER void TIM3_IRQHandler(void); 263 | #endif // SYS_USE_VECTORS > 0 264 | 265 | // FLASH starts with a jump to the reset handler 266 | void jump_reset(void) { asm volatile("j reset_handler"); } 267 | 268 | // Interrupt vector table 269 | void (* const vectors[])(void) = { 270 | // RISC-V handlers 271 | jump_reset, // 0 - Reset 272 | #if SYS_USE_VECTORS > 0 273 | 0, // 1 - Reserved 274 | NMI_Handler, // 2 - NMI Handler 275 | HardFault_Handler, // 3 - Hard Fault Handler 276 | 0, // 4 - Reserved 277 | Ecall_M_Mode_Handler, // 5 - Ecall M Mode Handler 278 | 0, // 6 - Reserved 279 | 0, // 7 - Reserved 280 | Ecall_U_Mode_Handler, // 8 - Ecall U Mode Handler 281 | Break_Point_Handler, // 9 - Break Point Handler 282 | 0, // 10 - Reserved 283 | 0, // 11 - Reserved 284 | SysTick_Handler, // 12 - SysTick Handler 285 | 0, // 13 - Reserved 286 | SW_Handler, // 14 - SW Handler 287 | 0, // 15 - Reserved 288 | 289 | // Peripheral handlers 290 | WWDG_IRQHandler, // 16 - Window Watchdog 291 | PVD_IRQHandler, // 17 - PVD through EXTI Line detect 292 | FLASH_IRQHandler, // 18 - Flash 293 | 0, // 19 - Reserved 294 | EXTI7_0_IRQHandler, // 20 - EXTI Line 7..0 295 | AWU_IRQHandler, // 21 - Auto Wake-up 296 | DMA1_Channel1_IRQHandler, // 22 - DMA1 Channel 1 297 | DMA1_Channel2_IRQHandler, // 23 - DMA1 Channel 2 298 | DMA1_Channel3_IRQHandler, // 24 - DMA1 Channel 3 299 | DMA1_Channel4_IRQHandler, // 25 - DMA1 Channel 4 300 | DMA1_Channel5_IRQHandler, // 26 - DMA1 Channel 5 301 | DMA1_Channel6_IRQHandler, // 27 - DMA1 Channel 6 302 | DMA1_Channel7_IRQHandler, // 28 - DMA1 Channel 7 303 | ADC1_IRQHandler, // 29 - ADC1 304 | I2C1_EV_IRQHandler, // 30 - I2C1 Event 305 | I2C1_ER_IRQHandler, // 31 - I2C1 Error 306 | USART1_IRQHandler, // 32 - USART1 307 | SPI1_IRQHandler, // 33 - SPI1 308 | TIM1_BRK_IRQHandler, // 34 - TIM1 Break 309 | TIM1_UP_IRQHandler, // 35 - TIM1 Update 310 | TIM1_TRG_COM_IRQHandler, // 36 - TIM1 Trigger and Commutation 311 | TIM1_CC_IRQHandler, // 37 - TIM1 Capture Compare 312 | TIM2_UP_IRQHandler, // 38 - TIM2 Update 313 | USART2_IRQHandler, // 39 - USART2 314 | EXTI15_8_IRQHandler, // 40 - EXTI Line 15..8 315 | EXTI25_16_IRQHandler, // 41 - EXTI Line 25..16 316 | USART3_IRQHandler, // 42 - USART3 317 | USART4_IRQHandler, // 43 - USART4 318 | DMA1_Channel8_IRQHandler, // 44 - DMA1 Channel8 319 | USBFS_IRQHandler, // 45 - USBFS Break 320 | USBFSWakeUp_IRQHandler, // 46 - USBFS Wake up from suspend 321 | PIOC_IRQHandler, // 47 - PIOC 322 | OPA_IRQHandler, // 48 - OPA 323 | USBPD_IRQHandler, // 49 - USBPD 324 | USBPDWakeUp_IRQHandler, // 50 - USBPD Wake-up 325 | TIM2_CC_IRQHandler, // 51 - TIM2 Capture Compare 326 | TIM2_TRG_COM_IRQHandler, // 52 - TIM2 Trigger and Commutation 327 | TIM2_BRK_IRQHandler, // 53 - TIM2 Break 328 | TIM3_IRQHandler // 54 - TIM3 329 | #endif // SYS_USE_VECTORS > 0 330 | }; 331 | 332 | // Reset handler 333 | void reset_handler(void) { 334 | uint32_t *src, *dst; 335 | 336 | // Set pointers, vectors, processor status, and interrupts 337 | asm volatile( 338 | " .option push \n\ 339 | .option norelax \n\ 340 | la gp, __global_pointer$ \n\ 341 | .option pop \n\ 342 | la sp, _eusrstack \n" 343 | #if __GNUC__ > 10 344 | ".option arch, +zicsr \n" 345 | #endif 346 | " li a0, 0x1f \n\ 347 | csrw 0xbc0, a0 \n\ 348 | li a0, 0x88 \n\ 349 | csrs mstatus, a0 \n\ 350 | li a1, 0x3 \n\ 351 | csrw 0x804, a1 \n\ 352 | la a0, vectors \n\ 353 | or a0, a0, a1 \n\ 354 | csrw mtvec, a0 \n\ 355 | csrw mepc, %[main] \n" 356 | : : [main] "r" (main) : "a0", "a1" , "memory" 357 | ); 358 | 359 | // Copy data from FLASH to RAM 360 | src = &_data_lma; 361 | dst = &_data_vma; 362 | while(dst < &_edata) *dst++ = *src++; 363 | 364 | // Clear uninitialized variables 365 | #if SYS_CLEAR_BSS > 0 366 | dst = &_sbss; 367 | while(dst < &_ebss) *dst++ = 0; 368 | #endif 369 | 370 | // C++ Support 371 | #ifdef __cplusplus 372 | __libc_init_array(); 373 | #endif 374 | 375 | // Init system 376 | SYS_init(); 377 | 378 | // Return 379 | asm volatile("mret"); 380 | } 381 | -------------------------------------------------------------------------------- /software/pd_tester/src/usbpd_sink.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB PD SINK Handler for CH32X035 * v1.5 * 3 | // =================================================================================== 4 | // 5 | // Reference: https://github.com/openwch/ch32x035 6 | // 2023 by Stefan Wagner: https://github.com/wagiminator 7 | 8 | #include "usbpd_sink.h" 9 | 10 | // Variables 11 | static pd_control_t PD_control = { 12 | .CC_State = CC_IDLE, 13 | .CC1_ConnectTimes = 0, 14 | .CC2_ConnectTimes = 0, 15 | }; 16 | 17 | FixedSourceCap_t PD_SC_fixed[5]; 18 | PPSSourceCap_t PD_SC_PPS[2]; 19 | 20 | // Buffers 21 | __attribute__ ((aligned(4))) uint8_t PD_TR_buffer[34]; // PD transmit/receive buffer 22 | __attribute__ ((aligned(4))) uint8_t PD_SC_buffer[28]; // PD Source Cap buffer 23 | 24 | // =================================================================================== 25 | // USB PD SINK Front End Functions 26 | // =================================================================================== 27 | 28 | // Prototype 29 | void PD_update(void); 30 | 31 | // Negotiate current settings and wait until finished (return 1) or timeout (return 0) 32 | uint8_t PD_negotiate(void) { 33 | uint8_t counter = 255; 34 | PD_control.LastSetVoltage = 0; 35 | PD_control.USBPD_READY = 0; 36 | while((!PD_control.USBPD_READY) && (--counter)) { 37 | DLY_ms(5); 38 | PD_update(); 39 | } 40 | return(counter > 0); 41 | } 42 | 43 | // Get total number of PDOs (fixed and programmable) 44 | uint8_t PD_getPDONum(void) { 45 | return PD_control.SourcePDONum; 46 | } 47 | 48 | // Get number of fixed power PDOs 49 | uint8_t PD_getFixedNum(void) { 50 | return PD_control.SourcePDONum - PD_control.SourcePPSNum; 51 | } 52 | 53 | // Get number of programmable power PDOs 54 | uint8_t PD_getPPSNum(void) { 55 | return PD_control.SourcePPSNum; 56 | } 57 | 58 | // Get voltage of specified fixed power PDO 59 | uint16_t PD_getPDOVoltage(uint8_t pdonum) { 60 | return PD_control.FixedSourceCap[pdonum - 1].Voltage; 61 | } 62 | 63 | // Get minimum voltage of specified PDO (fixed and programmable) 64 | uint16_t PD_getPDOMinVoltage(uint8_t pdonum) { 65 | uint8_t ppspos = PD_control.SourcePDONum - PD_control.SourcePPSNum; 66 | if(pdonum <= ppspos) 67 | return PD_control.FixedSourceCap[pdonum - 1].Voltage; 68 | else return PD_control.PPSSourceCap[pdonum - ppspos - 1].MinVoltage; 69 | } 70 | 71 | // Get maximum voltage of specified PDO (fixed and programmable) 72 | uint16_t PD_getPDOMaxVoltage(uint8_t pdonum) { 73 | uint8_t ppspos = PD_control.SourcePDONum - PD_control.SourcePPSNum; 74 | if(pdonum <= ppspos) 75 | return PD_control.FixedSourceCap[pdonum - 1].Voltage; 76 | else return PD_control.PPSSourceCap[pdonum - ppspos - 1].MaxVoltage; 77 | } 78 | 79 | // Get max current of specified PDO (fixed and programmable) 80 | uint16_t PD_getPDOMaxCurrent(uint8_t pdonum) { 81 | uint8_t ppspos = PD_control.SourcePDONum - PD_control.SourcePPSNum; 82 | if(pdonum <= ppspos) 83 | return PD_control.FixedSourceCap[pdonum - 1].Current; 84 | else return PD_control.PPSSourceCap[pdonum - ppspos - 1].Current; 85 | } 86 | 87 | // Set specified PDO and voltage; returns 0:failed, 1:success 88 | uint8_t PD_setPDO(uint8_t pdonum, uint16_t voltage) { 89 | PD_control.SetPDONum = pdonum; 90 | PD_control.SetVoltage = voltage; 91 | return PD_negotiate(); 92 | } 93 | 94 | // Set specified voltage (in millivolts) if available; returns 0:failed, 1:success 95 | uint8_t PD_setVoltage(uint16_t voltage) { 96 | uint8_t i; 97 | uint8_t ppspos = PD_control.SourcePDONum - PD_control.SourcePPSNum; 98 | for(i=0; i= voltage)) { 107 | return PD_setPDO(i + 1, voltage); 108 | } 109 | } 110 | } 111 | return 0; 112 | } 113 | 114 | // Get active PDO 115 | uint8_t PD_getPDO(void) { 116 | return PD_control.SetPDONum; 117 | } 118 | 119 | // Get active voltage 120 | uint16_t PD_getVoltage(void) { 121 | return PD_control.SetVoltage; 122 | } 123 | 124 | // Get active max current 125 | uint16_t PD_getCurrent(void) { 126 | uint8_t pdonum = PD_control.SetPDONum; 127 | uint8_t ppspos = PD_control.SourcePDONum - PD_control.SourcePPSNum; 128 | if(pdonum <= ppspos) 129 | return PD_control.FixedSourceCap[pdonum - 1].Current; 130 | else return PD_control.PPSSourceCap[pdonum - ppspos - 1].Current; 131 | } 132 | 133 | // Initialize PD registers and states, then connect 134 | uint8_t PD_connect(void) { 135 | RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPCEN; 136 | RCC->AHBPCENR |= RCC_USBPD; 137 | GPIOB->CFGHR = (GPIOB->CFGHR & ~( (uint32_t)0b1111<<(((14)&7)<<2) | (uint32_t)0b1111<<(((15)&7)<<2))) 138 | | ( (uint32_t)0b0100<<(((14)&7)<<2) | (uint32_t)0b0100<<(((15)&7)<<2)); 139 | #ifdef USB_VDD 140 | #if USB_VDD > 0 141 | AFIO->CTLR |= USBPD_IN_HVT; 142 | #else 143 | AFIO->CTLR |= USBPD_IN_HVT | USBPD_PHY_V33; 144 | #endif 145 | #else 146 | RCC->APB1PCENR |= RCC_PWREN; 147 | PWR->CTLR |= PWR_CTLR_PLS; 148 | if(PWR->CSR & PWR_CSR_PVDO) AFIO->CTLR |= USBPD_IN_HVT | USBPD_PHY_V33; 149 | else AFIO->CTLR |= USBPD_IN_HVT; 150 | #endif 151 | 152 | USBPD->DMA = (uint32_t)PD_TR_buffer; 153 | USBPD->CONFIG = USBPD_IE_RX_ACT | USBPD_IE_RX_RESET | USBPD_IE_TX_END | USBPD_PD_DMA_EN; 154 | USBPD->STATUS = USBPD_BUF_ERR | USBPD_IF_RX_BIT | USBPD_IF_RX_BYTE 155 | | USBPD_IF_RX_ACT | USBPD_IF_RX_RESET | USBPD_IF_TX_END; 156 | return PD_negotiate(); 157 | } 158 | 159 | // =================================================================================== 160 | // USB PD SINK Back End Functions 161 | // =================================================================================== 162 | 163 | // Enter reception mode 164 | void PD_RX_mode(void) { 165 | USBPD->BMC_CLK_CNT = USBPD_TMR_RX; 166 | USBPD->CONTROL = (USBPD->CONTROL & ~USBPD_PD_TX_EN) | USBPD_BMC_START; 167 | } 168 | 169 | // Reset PD 170 | void PD_reset(void) { 171 | USBPD->PORT_CC1 = USBPD_CC_CMP_66 | USBPD_CC_PD; 172 | USBPD->PORT_CC2 = USBPD_CC_CMP_66 | USBPD_CC_PD; 173 | PD_control.CC1_ConnectTimes = 0; 174 | PD_control.CC2_ConnectTimes = 0; 175 | PD_control.CC_NoneTimes = 0; 176 | PD_control.SourcePDONum = 0; 177 | PD_control.SourcePPSNum = 0; 178 | PD_control.FixedSourceCap = PD_SC_fixed; 179 | PD_control.PPSSourceCap = PD_SC_PPS; 180 | PD_control.CC_State = CC_IDLE; 181 | PD_control.CC_LastState = CC_IDLE; 182 | PD_control.SinkMessageID = 0; 183 | PD_control.SinkGoodCRCOver = 0; 184 | PD_control.SourceGoodCRCOver = 0; 185 | PD_control.PD_Version = USBPD_REVISION_20; 186 | PD_control.USBPD_READY = 0; 187 | PD_control.SetPDONum = 1; 188 | PD_control.LastSetPDONum = 1; 189 | PD_control.SetVoltage = 5000; 190 | PD_control.LastSetVoltage = 5000; 191 | } 192 | 193 | // Copy buffers 194 | void PD_memcpy(uint8_t* dest, const uint8_t* src, uint8_t n) { 195 | while(n--) *dest++ = *src++; 196 | } 197 | 198 | // Send PD data 199 | void PD_sendData(uint8_t length) { 200 | if((USBPD->CONFIG & USBPD_CC_SEL) == USBPD_CC_SEL) USBPD->PORT_CC2 |= USBPD_CC_LVE; 201 | else USBPD->PORT_CC1 |= USBPD_CC_LVE; 202 | 203 | USBPD->BMC_CLK_CNT = USBPD_TMR_TX; 204 | USBPD->TX_SEL = USBPD_TX_SOP0; 205 | USBPD->BMC_TX_SZ = length; 206 | USBPD->STATUS = 0; 207 | USBPD->CONTROL |= USBPD_BMC_START | USBPD_PD_TX_EN; 208 | } 209 | 210 | // Detect CC connection; returns 0:No connection, 1:CC1 connection, 2:CC2 connection 211 | uint8_t PD_checkCC(void) { 212 | uint8_t ccLine = USBPD_CCNONE; 213 | 214 | USBPD->PORT_CC1 &= ~(USBPD_CC_CE | USBPD_PA_CC_AI); 215 | USBPD->PORT_CC1 |= USBPD_CC_CMP_22; 216 | if(USBPD->PORT_CC1 & USBPD_PA_CC_AI) ccLine = USBPD_CC1; 217 | 218 | USBPD->PORT_CC2 &= ~(USBPD_CC_CE | USBPD_PA_CC_AI); 219 | USBPD->PORT_CC2 |= USBPD_CC_CMP_22; 220 | if(USBPD->PORT_CC2 & USBPD_PA_CC_AI) ccLine = USBPD_CC2; 221 | 222 | return ccLine; 223 | } 224 | 225 | void PD_PDO_analyze(void) { 226 | USBPD_PDO_t test; 227 | PD_control.SourcePPSNum = 0; 228 | 229 | for(uint8_t i=0; i (PD_control.SourcePDONum - PD_control.SourcePPSNum)) { 259 | pdo.SinkPPSRDO.ObjectPosition = pdoNum; 260 | pdo.SinkPPSRDO.OutputVoltageIn20mVunits = PD_control.SetVoltage / 20; 261 | pdo.SinkPPSRDO.OperatingCurrentIn50mAunits = PD_SC_PPS[pdoNum+PD_control.SourcePPSNum-PD_control.SourcePDONum-1].Current/50; 262 | pdo.SinkPPSRDO.NoUSBSuspend = 1u; 263 | pdo.SinkPPSRDO.USBCommunicationsCapable = 1u; 264 | } 265 | else { 266 | pdo.SinkFixedVariableRDO.ObjectPosition = pdoNum; 267 | pdo.SinkFixedVariableRDO.MaxOperatingCurrent10mAunits = PD_SC_fixed[pdoNum-1].Current/10; 268 | pdo.SinkFixedVariableRDO.OperatingCurrentIn10mAunits = PD_SC_fixed[pdoNum-1].Current/10; 269 | pdo.SinkFixedVariableRDO.USBCommunicationsCapable = 1u; 270 | pdo.SinkFixedVariableRDO.NoUSBSuspend = 1u; 271 | } 272 | 273 | *(uint16_t*)&PD_TR_buffer[0] = mh.d16; 274 | *(uint32_t*)&PD_TR_buffer[2] = pdo.d32; 275 | PD_sendData(6); 276 | } 277 | 278 | void PD_process(void) { 279 | cc_state_t temp = PD_control.CC_State; 280 | USBPD_MessageHeader_t mh; 281 | 282 | switch (PD_control.CC_State) { 283 | 284 | case CC_IDLE: 285 | NVIC_DisableIRQ(USBPD_IRQn); 286 | PD_reset(); 287 | PD_control.CC_State = CC_CHECK_CONNECT; 288 | break; 289 | 290 | case CC_CHECK_CONNECT: 291 | break; 292 | 293 | case CC_CONNECT: 294 | if(PD_control.CC_LastState != PD_control.CC_State) { 295 | PD_RX_mode(); 296 | NVIC_SetPriority(USBPD_IRQn, 0x00); 297 | NVIC_EnableIRQ(USBPD_IRQn); 298 | } 299 | break; 300 | 301 | case CC_SOURCE_CAP: 302 | if(PD_control.SinkGoodCRCOver) { 303 | PD_control.SinkGoodCRCOver = 0; 304 | NVIC_DisableIRQ(USBPD_IRQn); 305 | PD_PDO_analyze(); 306 | NVIC_EnableIRQ(USBPD_IRQn); 307 | PD_control.CC_State = CC_SEND_REQUEST; 308 | } 309 | break; 310 | 311 | case CC_SEND_REQUEST: 312 | if(PD_control.CC_LastState != PD_control.CC_State) { 313 | PD_PDO_request(); 314 | } 315 | if(PD_control.SourceGoodCRCOver) { 316 | PD_control.SourceGoodCRCOver = 0; 317 | PD_control.CC_State = CC_WAIT_ACCEPT; 318 | } 319 | break; 320 | 321 | case CC_WAIT_PS_RDY: 322 | break; 323 | 324 | case CC_PS_RDY: 325 | if(PD_control.SinkGoodCRCOver) { 326 | PD_control.SinkGoodCRCOver = 0; 327 | PD_control.CC_State = CC_GET_SOURCE_CAP; 328 | PD_control.WaitTime = 0; 329 | } 330 | break; 331 | 332 | case CC_GET_SOURCE_CAP: 333 | PD_control.USBPD_READY = 1; 334 | if((PD_control.SetPDONum != PD_control.LastSetPDONum) || 335 | (PD_control.SetVoltage != PD_control.LastSetVoltage)) { 336 | PD_control.LastSetPDONum = PD_control.SetPDONum; 337 | PD_control.LastSetVoltage = PD_control.SetVoltage; 338 | PD_control.USBPD_READY = 0; 339 | 340 | mh.d16 = 0u; 341 | mh.MessageHeader.MessageID = PD_control.SinkMessageID; 342 | mh.MessageHeader.MessageType = USBPD_CONTROL_MSG_GET_SRC_CAP; 343 | mh.MessageHeader.NumberOfDataObjects = 0u; 344 | mh.MessageHeader.SpecificationRevision = PD_control.PD_Version; 345 | *(uint16_t*)&PD_TR_buffer[0] = mh.d16; 346 | PD_sendData(2); 347 | PD_control.CC_State = CC_GET_SOURCE_CAP+1; 348 | } 349 | break; 350 | 351 | default: 352 | break; 353 | } 354 | PD_control.CC_LastState = temp; 355 | } 356 | 357 | void PD_update(void) { 358 | uint8_t ccLine = PD_checkCC(); 359 | PD_control.WaitTime++; 360 | 361 | if(PD_control.CC_State == CC_CHECK_CONNECT) { 362 | if(ccLine == USBPD_CC1) { 363 | PD_control.CC2_ConnectTimes = 0; 364 | PD_control.CC1_ConnectTimes++; 365 | if(PD_control.CC1_ConnectTimes > 5) { 366 | PD_control.CC1_ConnectTimes = 0; 367 | PD_control.CC_State = CC_CONNECT; 368 | USBPD->CONFIG &= ~USBPD_CC_SEL; 369 | } 370 | } 371 | else if(ccLine == USBPD_CC2) { 372 | PD_control.CC1_ConnectTimes = 0; 373 | PD_control.CC2_ConnectTimes++; 374 | if(PD_control.CC2_ConnectTimes > 5) { 375 | PD_control.CC2_ConnectTimes = 0; 376 | PD_control.CC_State = CC_CONNECT; 377 | USBPD->CONFIG |= USBPD_CC_SEL; 378 | } 379 | } 380 | else { 381 | PD_control.CC1_ConnectTimes = 0; 382 | PD_control.CC2_ConnectTimes = 0; 383 | } 384 | } 385 | 386 | if(PD_control.CC_State > CC_CHECK_CONNECT) { 387 | if(ccLine == USBPD_CCNONE) { 388 | PD_control.CC_NoneTimes++; 389 | if(PD_control.CC_NoneTimes > 5) { 390 | PD_control.CC_NoneTimes = 0; 391 | PD_control.CC_State = CC_IDLE; 392 | NVIC_DisableIRQ(USBPD_IRQn); 393 | } 394 | } 395 | else PD_control.CC_NoneTimes = 0; 396 | } 397 | 398 | PD_process(); 399 | } 400 | 401 | // Analyze received data 402 | void PD_RX_analyze(void) { 403 | uint8_t sendGoodCRCFlag = 1; 404 | USBPD_MessageHeader_t mh; 405 | mh.d16 = *(uint16_t*)PD_TR_buffer; 406 | 407 | if(mh.MessageHeader.Extended == 0u) { 408 | if(mh.MessageHeader.NumberOfDataObjects == 0u) { 409 | switch(mh.MessageHeader.MessageType) { 410 | 411 | case USBPD_CONTROL_MSG_GOODCRC: 412 | sendGoodCRCFlag = 0; 413 | PD_control.SourceGoodCRCOver = 1; 414 | PD_control.SinkMessageID++; 415 | break; 416 | 417 | case USBPD_CONTROL_MSG_ACCEPT: 418 | PD_control.CC_State = CC_WAIT_PS_RDY; 419 | break; 420 | 421 | case USBPD_CONTROL_MSG_PS_RDY: 422 | PD_control.CC_State = CC_PS_RDY; 423 | break; 424 | 425 | default: 426 | break; 427 | } 428 | } 429 | else { 430 | switch(mh.MessageHeader.MessageType) { 431 | 432 | case USBPD_DATA_MSG_SRC_CAP: 433 | PD_control.CC_State = CC_SOURCE_CAP; 434 | PD_control.SourcePDONum = mh.MessageHeader.NumberOfDataObjects; 435 | PD_control.PD_Version = mh.MessageHeader.SpecificationRevision; 436 | PD_memcpy(PD_SC_buffer, &PD_TR_buffer[2], 28); 437 | break; 438 | 439 | default: 440 | break; 441 | } 442 | } 443 | } 444 | 445 | if(sendGoodCRCFlag) { 446 | DLY_us(30); 447 | PD_control.SinkGoodCRCOver = 0; 448 | USBPD_MessageHeader_t my_mh; 449 | my_mh.d16 = 0u; 450 | my_mh.MessageHeader.MessageID = mh.MessageHeader.MessageID; 451 | my_mh.MessageHeader.MessageType = USBPD_CONTROL_MSG_GOODCRC; 452 | my_mh.MessageHeader.SpecificationRevision = PD_control.PD_Version; 453 | *(uint16_t*)&PD_TR_buffer[0] = my_mh.d16; 454 | PD_sendData(2); 455 | } 456 | } 457 | 458 | // =================================================================================== 459 | // USB PD Interrupt Service Routine 460 | // =================================================================================== 461 | void USBPD_IRQHandler(void) __attribute__((interrupt)); 462 | void USBPD_IRQHandler(void) { 463 | 464 | // Receive complete interrupt 465 | if(USBPD->STATUS & USBPD_IF_RX_ACT) { 466 | if((USBPD->STATUS & USBPD_BMC_AUX) == USBPD_BMC_AUX_SOP0) { 467 | if(USBPD->BMC_BYTE_CNT >= 6) { 468 | PD_RX_analyze(); 469 | } 470 | } 471 | USBPD->STATUS |= USBPD_IF_RX_ACT; 472 | } 473 | 474 | // Transmit complete interrupt (GoodCRC only) 475 | if(USBPD->STATUS & USBPD_IF_TX_END) { 476 | USBPD->PORT_CC1 &= ~USBPD_CC_LVE; 477 | USBPD->PORT_CC2 &= ~USBPD_CC_LVE; 478 | PD_RX_mode(); 479 | PD_control.SinkGoodCRCOver = 1; 480 | USBPD->STATUS |= USBPD_IF_TX_END; 481 | } 482 | 483 | // Reset interrupt 484 | if(USBPD->STATUS & USBPD_IF_RX_RESET) { 485 | USBPD->STATUS |= USBPD_IF_RX_RESET; 486 | PD_reset(); 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /software/pd_tester/src/ssd1306_txt.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // SSD1306/SH1106/SH1107 I2C OLED Text Functions * v1.3 * 3 | // =================================================================================== 4 | // 5 | // Collection of the most necessary functions for controlling an SSD1306/SH1106 I2C 6 | // OLED for the display of simple text. 7 | // 8 | // References: 9 | // ----------- 10 | // - Neven Boyanov: https://github.com/tinusaur/ssd1306xled 11 | // - Stephen Denne: https://github.com/datacute/Tiny4kOLED 12 | // - David Johnson-Davies: http://www.technoblogy.com/show?TV4 13 | // - TinyOLEDdemo: https://github.com/wagiminator/attiny13-tinyoleddemo 14 | // - TinyTerminal: https://github.com/wagiminator/ATtiny85-TinyTerminal 15 | // - OLED Font Editor: http://sourpuss.net/projects/fontedit/ 16 | // 17 | // 2022 by Stefan Wagner: https://github.com/wagiminator 18 | 19 | #include "ssd1306_txt.h" 20 | 21 | // =================================================================================== 22 | // Standard ASCII 5x8 Font (chars 32 - 127) 23 | // =================================================================================== 24 | const uint8_t OLED_FONT[] = { 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 26 | 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 27 | 0x36, 0x49, 0x55, 0x22, 0x50, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, 28 | 0x00, 0x41, 0x22, 0x1C, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x08, 0x08, 0x3E, 0x08, 0x08, 29 | 0x00, 0x80, 0x60, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x60, 0x60, 0x00, 0x00, 30 | 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x44, 0x42, 0x7F, 0x40, 0x40, 31 | 0x42, 0x61, 0x51, 0x49, 0x46, 0x22, 0x41, 0x49, 0x49, 0x36, 0x18, 0x14, 0x12, 0x7F, 0x10, 32 | 0x2F, 0x49, 0x49, 0x49, 0x31, 0x3E, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x71, 0x09, 0x07, 33 | 0x36, 0x49, 0x49, 0x49, 0x36, 0x26, 0x49, 0x49, 0x49, 0x3E, 0x00, 0x36, 0x36, 0x00, 0x00, 34 | 0x00, 0x80, 0x68, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 35 | 0x00, 0x22, 0x14, 0x08, 0x00, 0x02, 0x01, 0x51, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x55, 0x5E, 36 | 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41, 0x22, 37 | 0x7F, 0x41, 0x41, 0x22, 0x1C, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 38 | 0x3E, 0x41, 0x49, 0x49, 0x3A, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x41, 0x41, 0x7F, 0x41, 0x41, 39 | 0x20, 0x40, 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40, 40 | 0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, 0x41, 0x41, 0x41, 0x3E, 41 | 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x41, 0xC1, 0xBE, 0x7F, 0x09, 0x19, 0x29, 0x46, 42 | 0x26, 0x49, 0x49, 0x49, 0x32, 0x01, 0x01, 0x7F, 0x01, 0x01, 0x3F, 0x40, 0x40, 0x40, 0x3F, 43 | 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63, 44 | 0x07, 0x08, 0x70, 0x08, 0x07, 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x7F, 0x41, 0x41, 0x00, 45 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x7F, 0x00, 0x08, 0x04, 0x02, 0x04, 0x08, 46 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x06, 0x09, 0x09, 0x06, 0x20, 0x54, 0x54, 0x54, 0x78, 47 | 0x7F, 0x44, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x44, 0x7F, 48 | 0x38, 0x54, 0x54, 0x54, 0x18, 0x08, 0xFE, 0x09, 0x01, 0x02, 0x18, 0xA4, 0xA4, 0xA4, 0x78, 49 | 0x7F, 0x04, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00, 0x80, 0x84, 0x7D, 0x00, 50 | 0x41, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x7C, 0x04, 0x78, 51 | 0x7C, 0x04, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x24, 0x24, 0x24, 0x18, 52 | 0x18, 0x24, 0x24, 0x24, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x08, 0x54, 0x54, 0x54, 0x20, 53 | 0x04, 0x3F, 0x44, 0x40, 0x20, 0x3C, 0x40, 0x40, 0x40, 0x3C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 54 | 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x44, 0x28, 0x10, 0x28, 0x44, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, 55 | 0x44, 0x64, 0x54, 0x4C, 0x44, 0x08, 0x08, 0x36, 0x41, 0x41, 0x00, 0x00, 0xFF, 0x00, 0x00, 56 | 0x41, 0x41, 0x36, 0x08, 0x08, 0x08, 0x04, 0x08, 0x10, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF 57 | }; 58 | 59 | // =================================================================================== 60 | // 13x32 7-Segment Font (0 - 9) 61 | // =================================================================================== 62 | #if OLED_SEG_FONT == 1 63 | const uint8_t OLED_FONT_SEG[] = { 64 | 0xFC, 0xF9, 0xF3, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xF3, 0xF9, 0xFC, // 0 65 | 0x7F, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x7F, 66 | 0xFF, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 67 | 0x1F, 0x4F, 0x67, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x67, 0x4F, 0x1F, 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF8, 0xFC, // 1 69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x7F, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x1F, 72 | 0x00, 0x01, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xF3, 0xF9, 0xFC, // 2 73 | 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x9F, 0x3F, 0x7F, 74 | 0xFF, 0xFE, 0xFC, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 75 | 0x1F, 0x4F, 0x67, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x60, 0x40, 0x00, 76 | 0x00, 0x01, 0x03, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xF3, 0xF9, 0xFC, // 3 77 | 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x9F, 0x3F, 0x7F, 78 | 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0xFE, 0xFF, 79 | 0x00, 0x40, 0x60, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x67, 0x4F, 0x1F, 80 | 0xFC, 0xF8, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF8, 0xFC, // 4 81 | 0x7F, 0x3F, 0x9F, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x9F, 0x3F, 0x7F, 82 | 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0xFE, 0xFF, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x1F, 84 | 0xFC, 0xF9, 0xF3, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x01, 0x00, // 5 85 | 0x7F, 0x3F, 0x9F, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0xFE, 0xFF, 87 | 0x00, 0x40, 0x60, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x67, 0x4F, 0x1F, 88 | 0xFC, 0xF9, 0xF3, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x01, 0x00, // 6 89 | 0x7F, 0x3F, 0x9F, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 90 | 0xFF, 0xFE, 0xFC, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0xFE, 0xFF, 91 | 0x1F, 0x4F, 0x67, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x67, 0x4F, 0x1F, 92 | 0xFC, 0xF9, 0xF3, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xF3, 0xF9, 0xFC, // 7 93 | 0x7F, 0x3F, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x7F, 94 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFE, 0xFF, 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x1F, 96 | 0xFC, 0xF9, 0xF3, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xF3, 0xF9, 0xFC, // 8 97 | 0x7F, 0x3F, 0x9F, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x9F, 0x3F, 0x7F, 98 | 0xFF, 0xFE, 0xFC, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0xFE, 0xFF, 99 | 0x1F, 0x4F, 0x67, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x67, 0x4F, 0x1F, 100 | 0xFC, 0xF9, 0xF3, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xF3, 0xF9, 0xFC, // 9 101 | 0x7F, 0x3F, 0x9F, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x9F, 0x3F, 0x7F, 102 | 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0xFE, 0xFF, 103 | 0x00, 0x40, 0x60, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x67, 0x4F, 0x1F 104 | }; 105 | 106 | const uint8_t OLED_FONT_POINT[] = { 107 | 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 109 | 0x00, 0x00, 0x00, 110 | 0x70, 0x70, 0x70 111 | }; 112 | #endif 113 | 114 | // =================================================================================== 115 | // 5x16 7-Segment Font (0 - 9) 116 | // =================================================================================== 117 | #if OLED_SEG_FONT == 2 118 | const uint8_t OLED_FONT_SEG[] = { 119 | 0x7C, 0x02, 0x02, 0x02, 0x7C, 0x1F, 0x20, 0x20, 0x20, 0x1F, // 0 120 | 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x1F, // 1 121 | 0x00, 0x82, 0x82, 0x82, 0x7C, 0x1F, 0x20, 0x20, 0x20, 0x00, // 2 122 | 0x00, 0x82, 0x82, 0x82, 0x7C, 0x00, 0x20, 0x20, 0x20, 0x1F, // 3 123 | 0x7C, 0x80, 0x80, 0x80, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x1F, // 4 124 | 0x7C, 0x82, 0x82, 0x82, 0x00, 0x00, 0x20, 0x20, 0x20, 0x1F, // 5 125 | 0x7C, 0x82, 0x82, 0x82, 0x00, 0x1F, 0x20, 0x20, 0x20, 0x1F, // 6 126 | 0x7C, 0x02, 0x02, 0x02, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x1F, // 7 127 | 0x7C, 0x82, 0x82, 0x82, 0x7C, 0x1F, 0x20, 0x20, 0x20, 0x1F, // 8 128 | 0x7C, 0x82, 0x82, 0x82, 0x7C, 0x00, 0x20, 0x20, 0x20, 0x1F // 9 129 | }; 130 | 131 | const uint8_t OLED_FONT_POINT[] = { 132 | 0x00, 0x00, 133 | 0x30, 0x30 134 | }; 135 | #endif 136 | 137 | // =================================================================================== 138 | // OLED Control Functions 139 | // =================================================================================== 140 | 141 | // Screen offsets 142 | #if OLED_SH1106 == 1 143 | #define OLED_XOFF ((128 - OLED_WIDTH) / 2) + 2 144 | #else 145 | #define OLED_XOFF ((128 - OLED_WIDTH) / 2) 146 | #endif 147 | 148 | // OLED initialisation sequence 149 | const uint8_t OLED_INIT_CMD[] = { 150 | OLED_MULTIPLEX, OLED_HEIGHT - 1, // set multiplex ratio 151 | OLED_CHARGEPUMP, 0x14, // set DC-DC enable 152 | OLED_MEMORYMODE, 0x00, // set horizontal addressing mode 153 | #if OLED_WIDTH == 128 && OLED_HEIGHT == 32 154 | OLED_COMPINS, 0x02, // set com pins 155 | #else 156 | OLED_COMPINS, 0x12, // set com pins 157 | #endif 158 | #if OLED_XFLIP > 0 159 | OLED_XFLIP_ON, // flip screen in X-direction 160 | #endif 161 | #if OLED_YFLIP > 0 162 | OLED_YFLIP_ON, // flip screen in Y-direction 163 | #endif 164 | #if OLED_INVERT > 0 165 | OLED_INVERT_ON, // invert screen 166 | #endif 167 | OLED_DISPLAY_ON // display on 168 | }; 169 | 170 | // OLED init function 171 | void OLED_init(void) { 172 | #if OLED_INIT_I2C > 0 173 | I2C_init(); // initialize I2C first 174 | #endif 175 | #if OLED_BOOT_TIME > 0 176 | DLY_ms(OLED_BOOT_TIME); // time for the OLED to boot up 177 | #endif 178 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 179 | I2C_write(OLED_CMD_MODE); // set command mode 180 | I2C_writeBuffer((uint8_t*)OLED_INIT_CMD, sizeof(OLED_INIT_CMD)); // send the command bytes 181 | } 182 | 183 | // Switch display on/off (0: display off, 1: display on) 184 | void OLED_display(uint8_t val) { 185 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 186 | I2C_write(OLED_CMD_MODE); // set command mode 187 | I2C_write(val ? OLED_DISPLAY_ON : OLED_DISPLAY_OFF); // set display power 188 | I2C_stop(); // stop transmission 189 | } 190 | 191 | // Set display contrast (0-255) 192 | void OLED_contrast(uint8_t val) { 193 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 194 | I2C_write(OLED_CMD_MODE); // set command mode 195 | I2C_write(OLED_CONTRAST); // contrast command 196 | I2C_write(val); // set contrast value 197 | I2C_stop(); // stop transmission 198 | } 199 | 200 | // Invert display (0: inverse off, 1: inverse on) 201 | void OLED_invert(uint8_t val) { 202 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 203 | I2C_write(OLED_CMD_MODE); // set command mode 204 | I2C_write(val ? OLED_INVERT_ON : OLED_INVERT_OFF); // set invert mode 205 | I2C_stop(); // stop transmission 206 | } 207 | 208 | // Flip display (0: flip off, 1: flip on) 209 | void OLED_flip(uint8_t xflip, uint8_t yflip) { 210 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 211 | I2C_write(OLED_CMD_MODE); // set command mode 212 | I2C_write(xflip ? OLED_XFLIP_ON : OLED_XFLIP_OFF); // set x-flip 213 | I2C_write(yflip ? OLED_YFLIP_ON : OLED_YFLIP_OFF); // set y-flip 214 | I2C_stop(); // stop transmission 215 | } 216 | 217 | // Scroll display vertically 218 | void OLED_vscroll(uint8_t y) { 219 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 220 | I2C_write(OLED_CMD_MODE); // set command mode 221 | I2C_write(OLED_OFFSET); // offset command 222 | I2C_write(y); // set y-scroll 223 | I2C_stop(); // stop transmission 224 | } 225 | 226 | // =================================================================================== 227 | // OLED Text Functions 228 | // =================================================================================== 229 | 230 | // OLED global variables 231 | uint8_t OLED_x, OLED_y, OLED_i; 232 | 233 | // OLED clear line 234 | void OLED_clearLine(uint8_t y) { 235 | uint8_t i; 236 | OLED_cursor(0, y); // set cursor to line start 237 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 238 | I2C_write(OLED_DAT_MODE); // set data mode 239 | for(i=OLED_WIDTH; i; i--) I2C_write(0x00); // clear line 240 | I2C_stop(); // stop transmission 241 | OLED_cursor(0, y); // re-set cursor to line start 242 | } 243 | 244 | // OLED clear screen 245 | void OLED_clear(void) { 246 | uint8_t y = OLED_HEIGHT / 8; 247 | while(y--) OLED_clearLine(y); // clear all lines 248 | } 249 | 250 | // OLED set cursor to specified position 251 | void OLED_cursor(uint8_t x, uint8_t y) { 252 | if(y >= OLED_HEIGHT / 8) y = 0; // limit y 253 | OLED_x = x; OLED_y = y; // set cursor variables 254 | x += OLED_XOFF; // add offset 255 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 256 | I2C_write(OLED_CMD_MODE); // set command mode 257 | I2C_write(OLED_PAGE + y); // set line 258 | I2C_write(x & 0xf); // set column 259 | I2C_write((x >> 4) | 0x10); 260 | I2C_stop(); // stop transmission 261 | } 262 | 263 | // OLED set text invert 264 | void OLED_textinvert(uint8_t yes) { 265 | OLED_i = yes; 266 | } 267 | 268 | #if OLED_BIGCHARS > 0 269 | 270 | // Character buffer 271 | uint8_t OLED_buf[2*10]; 272 | uint8_t OLED_sz; 273 | 274 | // Converts bit pattern abcdefgh into aabbccddeeffgghh 275 | uint16_t OLED_stretch(uint16_t x) { 276 | x = (x & 0xF0)<<4 | (x & 0x0F); 277 | x = (x<<2 | x) & 0x3333; 278 | x = (x<<1 | x) & 0x5555; 279 | return x | x<<1; 280 | } 281 | 282 | // Set character size 283 | void OLED_textsize(uint8_t size) { 284 | OLED_sz = size; 285 | } 286 | 287 | #endif // OLED_BIGCHARS > 0 288 | 289 | // OLED plot a single character 290 | void OLED_plotChar(char c) { 291 | uint16_t ptr = c - 32; // character pointer 292 | ptr += ptr << 2; // -> ptr = (ch - 32) * 5; 293 | #if OLED_BIGCHARS > 0 294 | if(OLED_sz == 0) { // normal character (5x8) 295 | #endif 296 | if(OLED_x > OLED_WIDTH - 6) OLED_cursor(0, OLED_y + 1); 297 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 298 | I2C_write(OLED_DAT_MODE); // set data mode 299 | I2C_write(OLED_i ? 0xff : 0x00); // write space between characters 300 | for(uint8_t i=5; i; i--) I2C_write(OLED_i ? ~OLED_FONT[ptr++] : OLED_FONT[ptr++]); 301 | I2C_stop(); 302 | OLED_x += 6; // move cursor 303 | #if OLED_BIGCHARS > 0 304 | } 305 | else if(OLED_sz == 1) { // v-stretched character (5x16) 306 | if(OLED_x > OLED_WIDTH - 6) OLED_cursor(0, OLED_y + 2); 307 | for(uint8_t i=0; i<5; i++) { 308 | uint16_t ch = OLED_stretch(OLED_FONT[ptr++]); 309 | OLED_buf[i] = ch; OLED_buf[i+5] = ch >> 8; 310 | } 311 | OLED_drawBitmap(OLED_buf, 5, 2); 312 | OLED_clearRect(1, 2); 313 | } 314 | else { // double-sized smoothed character (10x16) 315 | uint16_t col0L, col0R, col1L, col1R; // David Johnson-Davies' Smooth Big Text algorithm 316 | uint8_t col0 = OLED_FONT[ptr++]; 317 | if(OLED_x > OLED_WIDTH - 12) OLED_cursor(0, OLED_y + 2); 318 | col0L = OLED_stretch(col0); 319 | col0R = col0L; 320 | for(uint8_t col=0; col<10; col+=2) { 321 | uint8_t col1 = OLED_FONT[ptr++]; 322 | if(col == 8) col1 = 0; 323 | col1L = OLED_stretch(col1); 324 | col1R = col1L; 325 | for(int8_t i=6; i>=0; i--) { 326 | for(int8_t j=1; j<3; j++) { 327 | if(((col0>>i & 0b11) == (3 - j)) && ((col1>>i & 0b11) == j)) { 328 | col0R = col0R | 1<<((i << 1) + j); 329 | col1L = col1L | 1<<((i << 1) + 3 - j); 330 | } 331 | } 332 | } 333 | OLED_buf[col] = col0L; OLED_buf[col + 1] = col0R; 334 | OLED_buf[col + 10] = col0L >> 8; OLED_buf[col + 11] = col0R >> 8; 335 | col0 = col1; col0L = col1L; col0R = col1R; 336 | } 337 | OLED_drawBitmap(OLED_buf, 10, 2); 338 | OLED_clearRect(2, 2); 339 | } 340 | #endif 341 | } 342 | 343 | // OLED write a character or handle control characters 344 | void OLED_write(char c) { 345 | c &= 0x7f; // ignore top bit 346 | if(c >= 32) OLED_plotChar(c); // normal character 347 | #if OLED_BIGCHARS > 0 348 | else if(c == '\n') OLED_cursor(0, OLED_y + (OLED_sz ? 2 : 1)); 349 | #else 350 | else if(c == '\n') OLED_cursor(0, OLED_y + 1); // new line 351 | #endif 352 | else if(c == '\r') OLED_cursor(0, OLED_y); // carriage return 353 | } 354 | 355 | // OLED print a string 356 | void OLED_print(char* str) { 357 | while(*str) OLED_write(*str++); 358 | } 359 | 360 | // =================================================================================== 361 | // OLED Bitmap Functions 362 | // =================================================================================== 363 | 364 | // Draw bitmap (pointer *bmp) at cursor position width (w) in pixels, hight (h) in 8-pixel lines 365 | void OLED_drawBitmap(const uint8_t* bmp, uint8_t w, uint8_t h) { 366 | uint8_t y = OLED_y; 367 | while(h--) { 368 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 369 | I2C_write(OLED_DAT_MODE); // set data mode 370 | for(uint8_t i=w; i; i--) I2C_write(OLED_i ? ~(*bmp++) : *bmp++); 371 | I2C_stop(); 372 | OLED_cursor(OLED_x, OLED_y + 1); // set next line 373 | } 374 | OLED_cursor(OLED_x + w, y); // move cursor 375 | } 376 | 377 | // =================================================================================== 378 | // OLED 7-Segment Functions 379 | // =================================================================================== 380 | 381 | // Clear a rectangle starting from cursor position 382 | void OLED_clearRect(uint8_t w, uint8_t h) { 383 | uint8_t y = OLED_y; 384 | while(h--) { 385 | I2C_start(OLED_ADDR << 1); // start transmission to OLED 386 | I2C_write(OLED_DAT_MODE); // set data mode 387 | for(uint8_t i=w; i; i--) I2C_write(OLED_i ? 0xff : 0x00); // clear line 388 | I2C_stop(); // stop transmission 389 | OLED_cursor(OLED_x, OLED_y + 1); // set next line 390 | } 391 | OLED_cursor(OLED_x + w, y); // move cursor 392 | } 393 | 394 | // Print value as 7-segment digits (BCD conversion by substraction method) 395 | void OLED_printSegment(uint16_t value, uint8_t digits, uint8_t lead, uint8_t decimal) { 396 | static const uint16_t DIVIDER[] = {1, 10, 100, 1000, 10000}; 397 | uint8_t leadflag = 0; // flag for leading spaces 398 | while(digits--) { // for all digits digits 399 | uint8_t digitval = 0; // start with digit value 0 400 | uint16_t divider = DIVIDER[digits]; // read current divider 401 | while(value >= divider) { // if current divider fits into the value 402 | leadflag = 1; // end of leading spaces 403 | digitval++; // increase digit value 404 | value -= divider; // decrease value by divider 405 | } 406 | if(digits == decimal) leadflag++; // end leading characters before decimal 407 | if(leadflag || !lead) { 408 | #if OLED_SEG_FONT == 0 409 | OLED_write(digitval + '0'); 410 | #elif OLED_SEG_FONT == 1 411 | uint16_t ptr = (uint16_t)digitval; // character pointer 412 | ptr = (ptr << 5) + (ptr << 4) + (ptr << 2); // -> ptr = c * 13 * 4; 413 | OLED_drawBitmap((uint8_t*)&OLED_FONT_SEG[ptr], 13, 4); 414 | #elif OLED_SEG_FONT == 2 415 | uint16_t ptr = (uint16_t)digitval; // character pointer 416 | ptr = (ptr << 3) + (ptr << 1); // -> ptr = c * 5 * 2; 417 | OLED_drawBitmap((uint8_t*)&OLED_FONT_SEG[ptr], 5, 2); 418 | #endif 419 | } 420 | else { 421 | #if OLED_SEG_FONT == 0 422 | OLED_write(' '); 423 | #elif OLED_SEG_FONT == 1 424 | OLED_clearRect(13, 4); 425 | #elif OLED_SEG_FONT == 2 426 | OLED_clearRect( 5, 2); 427 | #endif 428 | } 429 | #if OLED_SEG_FONT == 1 430 | OLED_clearRect(OLED_SEG_SPACE, 4); 431 | #elif OLED_SEG_FONT == 2 432 | OLED_clearRect(OLED_SEG_SPACE, 2); 433 | #endif 434 | if(decimal && (digits == decimal)) { 435 | #if OLED_SEG_FONT == 0 436 | OLED_write('.'); 437 | #elif OLED_SEG_FONT == 1 438 | OLED_drawBitmap(OLED_FONT_POINT, 3, 4); 439 | OLED_clearRect(OLED_SEG_SPACE, 4); 440 | #elif OLED_SEG_FONT == 2 441 | OLED_drawBitmap(OLED_FONT_POINT, 2, 2); 442 | OLED_clearRect(OLED_SEG_SPACE, 2); 443 | #endif 444 | } 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /software/pd_tester/src/system.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Basic System Functions for CH32X035/X034/X033 * v1.1 * 3 | // =================================================================================== 4 | // 5 | // This file must be included!!! The system configuration and the system clock are 6 | // set up automatically on system start. 7 | // 8 | // System clock functions available: 9 | // --------------------------------- 10 | // CLK_init() init system clock according to F_CPU 11 | // CLK_reset() reset system clock to default state 12 | // 13 | // HSI_enable() enable internal 48 MHz high-speed clock (HSI) 14 | // HSI_disable() disable HSI 15 | // HSI_ready() check if HSI is stable 16 | // 17 | // MCO_init() init clock output to pin PB9 18 | // MCO_setSYS() output SYS_CLK on pin PB9 19 | // MCO_setHSI() output internal oscillator on pin PB9 20 | // MCO_stop() stop clock output 21 | // 22 | // Delay (DLY) functions available: 23 | // -------------------------------- 24 | // DLY_ticks(n) delay n system clock cycles 25 | // DLY_us(n) delay n microseconds 26 | // DLY_ms(n) delay n milliseconds 27 | // 28 | // Reset (RST) and Bootloader (BOOT) functions available: 29 | // ------------------------------------------------------ 30 | // BOOT_now() conduct software reset and jump to bootloader 31 | // RST_now() conduct software reset 32 | // RST_clearFlags() clear all reset flags 33 | // RST_wasLowPower() check if last reset was caused by low power 34 | // RST_wasWWDG() check if last reset was caused by window watchdog 35 | // RST_wasIWDG() check if last reset was caused by independent watchdog 36 | // RST_wasSoftware() check if last reset was caused by software 37 | // RST_wasPower() check if last reset was caused by power up 38 | // RST_wasPin() check if last reset was caused by RST pin low 39 | // RST_wasOPA() check if last reset was caused by OPA 40 | // 41 | // Independent Watchdog Timer (IWDG) functions available: 42 | // ------------------------------------------------------ 43 | // IWDG_start(n) start independent watchdog timer, n milliseconds, n<=5591 44 | // IWDG_reload(n) reload watchdog counter with n milliseconds, n<=5591 45 | // IWDG_feed() feed the dog (reload last time) 46 | // 47 | // Automatic Wake-up Timer (AWU) functions available: 48 | // -------------------------------------------------- 49 | // AWU_start(n) start AWU with n milliseconds period and event trigger 50 | // AWU_stop() stop AWU and event trigger 51 | // AWU_set(n) set AWU period to n milliseconds 52 | // 53 | // AWU_enable() enable AWU 54 | // AWU_disable() disable AWU 55 | // AWU_RT_enable() enable AWU rising edge trigger 56 | // AWU_RT_disable() disable AWU rising edge trigger 57 | // AWU_EV_enable() enable AWU event 58 | // AWU_EV_disable() disable AWU event 59 | // AWU_INT_enable() enable AWU interrupt (without NVIC) 60 | // AWU_INT_disable() disable AWU interrupt (without NVIC) 61 | // 62 | // Sleep functions available: 63 | // -------------------------- 64 | // SLEEP_WFI_now() put device into SLEEP, wake up by interrupt 65 | // SLEEP_WFE_now() put device into SLEEP, wake up by event 66 | // STOP_WFI_now() put device into STOP, wake up by interrupt 67 | // STOP_WFE_now() put device into STOP, wake up by event 68 | // STDBY_WFI_now() put device into STANDBY (deep sleep), wake by interrupt 69 | // STDBY_WFE_now() put device into STANDBY (deep sleep), wake by event 70 | // 71 | // SLEEP_ms(n) put device into SLEEP for n milliseconds (uses AWU) 72 | // STOP_ms(n) put device into STOP for n milliseconds (uses AWU) 73 | // STDBY_ms(n) put device into STANDBY for n milliseconds (uses AWU) 74 | // 75 | // Programmable Voltage Detector (PVD) functions available: 76 | // -------------------------------------------------------- 77 | // PVD_enable() enable PVD (power module clock) 78 | // PVD_set_2V1() set detection level to 2.1V 79 | // PVD_set_2V3() set detection level to 2.3V 80 | // PVD_set_3V0() set detection level to 3.0V 81 | // PVD_set_4V0() set detection level to 4.0V 82 | // PVD_isLow() check if VDD is below detection level 83 | // 84 | // Interrupt (INT) functions available: 85 | // ------------------------------------ 86 | // INT_enable() global interrupt enable 87 | // INT_disable() global interrupt disable 88 | // INT_ATOMIC_BLOCK { } execute block without being interrupted 89 | // 90 | // References: 91 | // ----------- 92 | // - WCH Nanjing Qinheng Microelectronics: http://wch.cn 93 | // 94 | // 2023 by Stefan Wagner: https://github.com/wagiminator 95 | 96 | #pragma once 97 | 98 | #ifdef __cplusplus 99 | extern "C" { 100 | #endif 101 | 102 | #include "ch32x035.h" 103 | 104 | // =================================================================================== 105 | // System Options (set "1" to activate) 106 | // =================================================================================== 107 | #define SYS_CLK_INIT 1 // 1: init system clock on startup 108 | #define SYS_TICK_INIT 1 // 1: init and start SYSTICK on startup 109 | #define SYS_GPIO_EN 1 // 1: enable GPIO ports on startup 110 | #define SYS_CLEAR_BSS 1 // 1: clear uninitialized variables 111 | #define SYS_USE_VECTORS 1 // 1: create interrupt vector table 112 | 113 | // =================================================================================== 114 | // Sytem Clock Defines 115 | // =================================================================================== 116 | // Set system clock frequency 117 | #ifndef F_CPU 118 | #define F_CPU 8000000 // 8 Mhz if not otherwise defined 119 | #endif 120 | 121 | // Calculate system clock settings 122 | #if F_CPU == 48000000 123 | #define SYS_CLK_DIV RCC_HPRE_DIV1 124 | #elif F_CPU == 24000000 125 | #define SYS_CLK_DIV RCC_HPRE_DIV2 126 | #elif F_CPU == 16000000 127 | #define SYS_CLK_DIV RCC_HPRE_DIV3 128 | #elif F_CPU == 12000000 129 | #define SYS_CLK_DIV RCC_HPRE_DIV4 130 | #elif F_CPU == 9600000 131 | #define SYS_CLK_DIV RCC_HPRE_DIV5 132 | #elif F_CPU == 8000000 133 | #define SYS_CLK_DIV RCC_HPRE_DIV6 134 | #elif F_CPU == 6000000 135 | #define SYS_CLK_DIV RCC_HPRE_DIV8 136 | #elif F_CPU == 3000000 137 | #define SYS_CLK_DIV RCC_HPRE_DIV16 138 | #elif F_CPU == 1500000 139 | #define SYS_CLK_DIV RCC_HPRE_DIV32 140 | #elif F_CPU == 750000 141 | #define SYS_CLK_DIV RCC_HPRE_DIV64 142 | #elif F_CPU == 375000 143 | #define SYS_CLK_DIV RCC_HPRE_DIV128 144 | #elif F_CPU == 187500 145 | #define SYS_CLK_DIV RCC_HPRE_DIV256 146 | #else 147 | #warning Unsupported system clock frequency, using internal 8 MHz 148 | #define SYS_CLK_DIV RCC_HPRE_DIV6 149 | #undef F_CPU 150 | #define F_CPU 8000000 151 | #endif 152 | 153 | // =================================================================================== 154 | // System Clock Functions 155 | // =================================================================================== 156 | void CLK_reset(void); // reset system clock to default state 157 | void MCO_init(void); // init clock output to pin PB9 158 | 159 | // Internal 48MHz high-speed clock (HSI) functions 160 | #define CLK_init() RCC->CFGR0 = SYS_CLK_DIV // init system clock 161 | #define HSI_enable() RCC->CTLR |= RCC_HSION // enable HSI 162 | #define HSI_disable() RCC->CTLR &= ~RCC_HSION // disable HSI 163 | #define HSI_ready() (RCC->CTLR & RCC_HSIRDY) // check if HSI is stable 164 | 165 | // Clock output functions (pin PA8) 166 | #define MCO_setSYS() RCC->CFGR0 = (RCC->CFGR0 & ~RCC_CFGR0_MCO) | RCC_CFGR0_MCO_SYSCLK 167 | #define MCO_setHSI() RCC->CFGR0 = (RCC->CFGR0 & ~RCC_CFGR0_MCO) | RCC_CFGR0_MCO_HSI 168 | #define MCO_stop() RCC->CFGR0 &= ~RCC_CFGR0_MCO // stop clock output to pin PB9 169 | 170 | // =================================================================================== 171 | // Delay (DLY) Functions 172 | // =================================================================================== 173 | void DLY_ticks(uint32_t n); // delay n system ticks 174 | 175 | #define STK_init() STK->CTLR = STK_CTLR_STE | STK_CTLR_STCLK // init SYSTICK @ F_CPU 176 | #define DLY_US_TIME (F_CPU / 1000000) // system ticks per us 177 | #define DLY_MS_TIME (F_CPU / 1000) // system ticks per ms 178 | #define DLY_us(n) DLY_ticks((n) * DLY_US_TIME) // delay n microseconds 179 | #define DLY_ms(n) DLY_ticks((n) * DLY_MS_TIME) // delay n milliseconds 180 | #define DLY_cycles DLY_ticks // alias 181 | 182 | // =================================================================================== 183 | // Reset (RST) Functions 184 | // =================================================================================== 185 | #define RST_now() PFIC->CFGR = PFIC_RESETSYS | PFIC_KEY3 186 | #define RST_clearFlags() RCC->RSTSCKR |= RCC_RMVF 187 | #define RST_wasLowPower() (RCC->RSTSCKR & RCC_LPWRRSTF) 188 | #define RST_wasWWDG() (RCC->RSTSCKR & RCC_WWDGRSTF) 189 | #define RST_wasIWDG() (RCC->RSTSCKR & RCC_IWDGRSTF) 190 | #define RST_wasSoftware() (RCC->RSTSCKR & RCC_SFTRSTF) 191 | #define RST_wasPower() (RCC->RSTSCKR & RCC_PORRSTF) 192 | #define RST_wasPin() (RCC->RSTSCKR & RCC_PINRSTF) 193 | #define RST_wasOPA() (RCC->RSTSCKR & RCC_OPARSTF) 194 | 195 | // =================================================================================== 196 | // Bootloader (BOOT) Functions 197 | // =================================================================================== 198 | void BOOT_now(void); // perform software reset and jump to bootloader 199 | 200 | // =================================================================================== 201 | // Independent Watchdog Timer (IWDG) Functions 202 | // =================================================================================== 203 | void IWDG_start_t(uint16_t ticks); // start IWDG with n ticks 204 | void IWDG_reload_t(uint16_t ticks); // reload IWDG with n ticks 205 | 206 | #define IWDG_start(n) IWDG_start_t((HSI_VALUE/1000)*(n)/65536) 207 | #define IWDG_reload(n) IWDG_reload_t((HSI_VALUE/1000)*(n)/65536) 208 | #define IWDG_feed() IWDG->CTLR = 0xAAAA 209 | #define IWDG_reset() IWDG->CTLR = 0xAAAA // alias 210 | 211 | // =================================================================================== 212 | // Automatic Wake-up Timer (AWU) Functions 213 | // =================================================================================== 214 | void AWU_init(void); 215 | void AWU_stop(void); 216 | 217 | #define AWU_start(n) {AWU_init(); AWU_set(n);} 218 | #define AWU_enable() AWU->CSR = 0x02 219 | #define AWU_disable() AWU->CSR = 0x00 220 | #define AWU_RT_enable() EXTI->RTENR |= ((uint32_t)1 << 27) 221 | #define AWU_RT_disable() EXTI->RTENR &= ~((uint32_t)1 << 27) 222 | #define AWU_EV_enable() EXTI->EVENR |= ((uint32_t)1 << 27) 223 | #define AWU_EV_disable() EXTI->EVENR &= ~((uint32_t)1 << 27) 224 | #define AWU_INT_enable() EXTI->INTENR |= ((uint32_t)1 << 27) 225 | #define AWU_INT_disable() EXTI->INTENR &= ~((uint32_t)1 << 27) 226 | 227 | #define AWU_WRVAL(n) ((HSI_VALUE/1000)*(n)/1024) 228 | #define AWU_LIMIT(n) (63*(n)*1024/(HSI_VALUE/1000)) 229 | 230 | #define AWU_set(n) \ 231 | (n <= AWU_LIMIT( 32) ? ({AWU->PSC = 0b0110; AWU->WR = AWU_WRVAL(n) / 32;}) : \ 232 | (n <= AWU_LIMIT( 64) ? ({AWU->PSC = 0b0111; AWU->WR = AWU_WRVAL(n) / 64;}) : \ 233 | (n <= AWU_LIMIT( 128) ? ({AWU->PSC = 0b1000; AWU->WR = AWU_WRVAL(n) / 128;}) : \ 234 | (n <= AWU_LIMIT( 256) ? ({AWU->PSC = 0b1001; AWU->WR = AWU_WRVAL(n) / 256;}) : \ 235 | (n <= AWU_LIMIT( 512) ? ({AWU->PSC = 0b1010; AWU->WR = AWU_WRVAL(n) / 512;}) : \ 236 | (n <= AWU_LIMIT( 1024) ? ({AWU->PSC = 0b1011; AWU->WR = AWU_WRVAL(n) / 1024;}) : \ 237 | (n <= AWU_LIMIT( 2048) ? ({AWU->PSC = 0b1100; AWU->WR = AWU_WRVAL(n) / 2048;}) : \ 238 | (n <= AWU_LIMIT( 4096) ? ({AWU->PSC = 0b1101; AWU->WR = AWU_WRVAL(n) / 4096;}) : \ 239 | (n <= AWU_LIMIT(10240) ? ({AWU->PSC = 0b1110; AWU->WR = AWU_WRVAL(n) / 10240;}) : \ 240 | (n <= AWU_LIMIT(61440) ? ({AWU->PSC = 0b1111; AWU->WR = AWU_WRVAL(n) / 61440;}) : \ 241 | (0))))))))))) 242 | 243 | #define AWU_SLEEP(n) {AWU_set(n); SLEEP_WFE_now();} 244 | #define AWU_STOP(n) {AWU_set(n); STOP_WFE_now();} 245 | #define AWU_STDBY(n) {AWU_set(n); STDBY_WFE_now();} 246 | 247 | // =================================================================================== 248 | // Sleep Functions 249 | // =================================================================================== 250 | void SLEEP_WFI_now(void); // put device into sleep, wake up by interrupt 251 | void SLEEP_WFE_now(void); // put device into sleep, wake up by event 252 | void STOP_WFI_now(void); // put device into stop, wake up by interrupt 253 | void STOP_WFE_now(void); // put device into stop, wake up by event 254 | void STDBY_WFI_now(void); // put device into standby (deep sleep), wake up interrupt 255 | void STDBY_WFE_now(void); // put device into standby (deep sleep), wake up event 256 | 257 | #define SLEEP_ms(n) {AWU_start(n); SLEEP_WFE_now(); AWU_stop();} 258 | #define STOP_ms(n) {AWU_start(n); STOP_WFE_now(); AWU_stop();} 259 | #define STDBY_ms(n) {AWU_start(n); STDBY_WFE_now(); AWU_stop();} 260 | 261 | // =================================================================================== 262 | // Programmable Voltage Detector (PVD) Functions 263 | // =================================================================================== 264 | #define PVD_enable() RCC->APB1PCENR |= RCC_PWREN 265 | #define PVD_set_2V1() PWR->CTLR &= ~PWR_CTLR_PLS 266 | #define PVD_set_2V3() PWR->CTLR = (PWR->CTLR & ~PWR_CTLR_PLS) | PWR_CTLR_PLS_2V3 267 | #define PVD_set_3V0() PWR->CTLR = (PWR->CTLR & ~PWR_CTLR_PLS) | PWR_CTLR_PLS_3V0 268 | #define PVD_set_4V0() PWR->CTLR |= PWR_CTLR_PLS 269 | #define PVD_isLow() (PWR->CSR & PWR_CSR_PVDO) 270 | 271 | #define PVD_RT_enable() EXTI->RTENR |= ((uint32_t)1 << 26) 272 | #define PVD_RT_disable() EXTI->RTENR &= ~((uint32_t)1 << 26) 273 | #define PVD_FT_enable() EXTI->FTENR |= ((uint32_t)1 << 26) 274 | #define PVD_FT_disable() EXTI->FTENR &= ~((uint32_t)1 << 26) 275 | #define PVD_EV_enable() EXTI->EVENR |= ((uint32_t)1 << 26) 276 | #define PVD_EV_disable() EXTI->EVENR &= ~((uint32_t)1 << 26) 277 | #define PVD_INT_enable() EXTI->INTENR |= ((uint32_t)1 << 26) 278 | #define PVD_INT_disable() EXTI->INTENR &= ~((uint32_t)1 << 26) 279 | 280 | // =================================================================================== 281 | // Interrupt (INT) Functions 282 | // =================================================================================== 283 | #define INT_enable() __enable_irq() 284 | #define INT_disable() __disable_irq() 285 | #define INT_ATOMIC_BLOCK for(INT_ATOMIC_RESTORE, __ToDo = 1; __ToDo; __ToDo = 0) 286 | #define INT_ATOMIC_RESTORE uint32_t __reg_save __attribute__((__cleanup__(__iRestore))) = __iSave() 287 | 288 | // Save interrupt status and disable interrupts 289 | static inline uint32_t __iSave(void) { 290 | uint32_t result; 291 | __asm volatile( 292 | #if __GNUC__ > 10 293 | ".option arch, +zicsr \n" 294 | #endif 295 | "csrr %0, 0x800 \n" 296 | "csrw 0x800, %1 \n" 297 | : "=&r" (result) : "r" (0x6000) 298 | ); 299 | return result; 300 | } 301 | 302 | // Restore interrupt status 303 | static inline void __iRestore(const uint32_t *__s) { 304 | __asm volatile( 305 | #if __GNUC__ > 10 306 | ".option arch, +zicsr \n" 307 | #endif 308 | "csrw 0x800, %0" : : "r" (*__s) 309 | ); 310 | } 311 | 312 | // =================================================================================== 313 | // Device Electronic Signature (ESIG) 314 | // =================================================================================== 315 | #define ESIG_FLASHSIZE (*(__I uint16_t*)(0x1FFFF7E0)) 316 | #define ESIG_UID1 (*(__I uint32_t*)(0x1FFFF7E8)) 317 | #define ESIG_UID2 (*(__I uint32_t*)(0x1FFFF7EC)) 318 | #define ESIG_UID3 (*(__I uint32_t*)(0x1FFFF7F0)) 319 | 320 | // =================================================================================== 321 | // Imported System Functions 322 | // =================================================================================== 323 | // Enable Global Interrupt 324 | __attribute__((always_inline)) static inline void __enable_irq(void) { 325 | __asm volatile( 326 | #if __GNUC__ > 10 327 | ".option arch, +zicsr \n" 328 | #endif 329 | "csrw 0x800, %0" : : "r" (0x6088) 330 | ); 331 | } 332 | 333 | // Disable Global Interrupt 334 | __attribute__((always_inline)) static inline void __disable_irq(void) { 335 | __asm volatile( 336 | #if __GNUC__ > 10 337 | ".option arch, +zicsr \n" 338 | #endif 339 | "csrw 0x800, %0" : : "r" (0x6000) 340 | ); 341 | } 342 | 343 | // Return the Machine Status Register 344 | __attribute__((always_inline)) static inline uint32_t __get_MSTATUS(void) { 345 | uint32_t result; 346 | __asm volatile( 347 | #if __GNUC__ > 10 348 | ".option arch, +zicsr \n" 349 | #endif 350 | "csrr %0, mstatus" : "=r"(result) 351 | ); 352 | return (result); 353 | } 354 | 355 | // Set the Machine Status Register 356 | __attribute__((always_inline)) static inline void __set_MSTATUS(uint32_t value) { 357 | __asm volatile( 358 | #if __GNUC__ > 10 359 | ".option arch, +zicsr \n" 360 | #endif 361 | "csrw mstatus, %0" : : "r"(value) 362 | ); 363 | } 364 | 365 | // Return the Machine ISA Register 366 | __attribute__((always_inline)) static inline uint32_t __get_MISA(void) { 367 | uint32_t result; 368 | __asm volatile( 369 | #if __GNUC__ > 10 370 | ".option arch, +zicsr \n" 371 | #endif 372 | "csrr %0, misa" : "=r"(result) 373 | ); 374 | return (result); 375 | } 376 | 377 | // Set the Machine ISA Register 378 | __attribute__((always_inline)) static inline void __set_MISA(uint32_t value) { 379 | __asm volatile( 380 | #if __GNUC__ > 10 381 | ".option arch, +zicsr \n" 382 | #endif 383 | "csrw misa, %0" : : "r"(value) 384 | ); 385 | } 386 | 387 | // Return the Machine Trap-Vector Base-Address Register 388 | __attribute__((always_inline)) static inline uint32_t __get_MTVEC(void) { 389 | uint32_t result; 390 | __asm volatile( 391 | #if __GNUC__ > 10 392 | ".option arch, +zicsr \n" 393 | #endif 394 | "csrr %0, mtvec": "=r"(result) 395 | ); 396 | return (result); 397 | } 398 | 399 | // Set the Machine Trap-Vector Base-Address Register 400 | __attribute__((always_inline)) static inline void __set_MTVEC(uint32_t value) { 401 | __asm volatile( 402 | #if __GNUC__ > 10 403 | ".option arch, +zicsr \n" 404 | #endif 405 | "csrw mtvec, %0" : : "r"(value) 406 | ); 407 | } 408 | 409 | // Return the Machine Seratch Register 410 | __attribute__((always_inline)) static inline uint32_t __get_MSCRATCH(void) { 411 | uint32_t result; 412 | __asm volatile( 413 | #if __GNUC__ > 10 414 | ".option arch, +zicsr \n" 415 | #endif 416 | "csrr %0, mscratch" : "=r"(result) 417 | ); 418 | return (result); 419 | } 420 | 421 | // Set the Machine Seratch Register 422 | __attribute__((always_inline)) static inline void __set_MSCRATCH(uint32_t value) { 423 | __asm volatile( 424 | #if __GNUC__ > 10 425 | ".option arch, +zicsr \n" 426 | #endif 427 | "csrw mscratch, %0" : : "r"(value) 428 | ); 429 | } 430 | 431 | // Return the Machine Exception Program Register 432 | __attribute__((always_inline)) static inline uint32_t __get_MEPC(void) { 433 | uint32_t result; 434 | __asm volatile( 435 | #if __GNUC__ > 10 436 | ".option arch, +zicsr \n" 437 | #endif 438 | "csrr %0, mepc" : "=r"(result) 439 | ); 440 | return (result); 441 | } 442 | 443 | // Set the Machine Exception Program Register 444 | __attribute__((always_inline)) static inline void __set_MEPC(uint32_t value) { 445 | __asm volatile( 446 | #if __GNUC__ > 10 447 | ".option arch, +zicsr \n" 448 | #endif 449 | "csrw mepc, %0" : : "r"(value) 450 | ); 451 | } 452 | 453 | // Return the Machine Cause Register 454 | __attribute__((always_inline)) static inline uint32_t __get_MCAUSE(void) { 455 | uint32_t result; 456 | __asm volatile( 457 | #if __GNUC__ > 10 458 | ".option arch, +zicsr \n" 459 | #endif 460 | "csrr %0, mcause": "=r"(result) 461 | ); 462 | return (result); 463 | } 464 | 465 | // Set the Machine Cause Register 466 | __attribute__((always_inline)) static inline void __set_MCAUSE(uint32_t value) { 467 | __asm volatile( 468 | #if __GNUC__ > 10 469 | ".option arch, +zicsr \n" 470 | #endif 471 | "csrw mcause, %0" : : "r"(value) 472 | ); 473 | } 474 | 475 | // Return Vendor ID Register 476 | __attribute__((always_inline)) static inline uint32_t __get_MVENDORID(void) { 477 | uint32_t result; 478 | __asm volatile( 479 | #if __GNUC__ > 10 480 | ".option arch, +zicsr \n" 481 | #endif 482 | "csrr %0, mvendorid" : "=r"(result) 483 | ); 484 | return (result); 485 | } 486 | 487 | // Return Machine Architecture ID Register 488 | __attribute__((always_inline)) static inline uint32_t __get_MARCHID(void) { 489 | uint32_t result; 490 | __asm volatile( 491 | #if __GNUC__ > 10 492 | ".option arch, +zicsr \n" 493 | #endif 494 | "csrr %0, marchid" : "=r"(result) 495 | ); 496 | return (result); 497 | } 498 | 499 | // Return Machine Implementation ID Register 500 | __attribute__((always_inline)) static inline uint32_t __get_MIMPID(void) { 501 | uint32_t result; 502 | __asm volatile( 503 | #if __GNUC__ > 10 504 | ".option arch, +zicsr \n" 505 | #endif 506 | "csrr %0, mimpid" : "=r"(result) 507 | ); 508 | return (result); 509 | } 510 | 511 | // Return Hart ID Register 512 | __attribute__((always_inline)) static inline uint32_t __get_MHARTID(void) { 513 | uint32_t result; 514 | __asm volatile( 515 | #if __GNUC__ > 10 516 | ".option arch, +zicsr \n" 517 | #endif 518 | "csrr %0, mhartid" : "=r"(result) 519 | ); 520 | return (result); 521 | } 522 | 523 | // Return SP Register 524 | __attribute__((always_inline)) static inline uint32_t __get_SP(void) { 525 | uint32_t result; 526 | __asm volatile("mv %0, sp" : "=r"(result):); 527 | return (result); 528 | } 529 | 530 | // No OPeration 531 | __attribute__((always_inline)) static inline void __NOP(void) { 532 | __asm volatile("nop"); 533 | } 534 | 535 | // Enable NVIC interrupt (interrupt numbers) 536 | __attribute__((always_inline)) static inline void NVIC_EnableIRQ(IRQn_Type IRQn) { 537 | NVIC->IENR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); 538 | } 539 | 540 | // Disable NVIC interrupt (interrupt numbers) 541 | __attribute__((always_inline)) static inline void NVIC_DisableIRQ(IRQn_Type IRQn) { 542 | NVIC->IRER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); 543 | } 544 | 545 | // Get Interrupt Enable State 546 | __attribute__((always_inline)) static inline uint32_t NVIC_GetStatusIRQ(IRQn_Type IRQn) { 547 | return((uint32_t) ((NVIC->ISR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); 548 | } 549 | 550 | // Get Interrupt Pending State 551 | __attribute__((always_inline)) static inline uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) { 552 | return((uint32_t) ((NVIC->IPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); 553 | } 554 | 555 | // Set Interrupt Pending 556 | __attribute__((always_inline)) static inline void NVIC_SetPendingIRQ(IRQn_Type IRQn) { 557 | NVIC->IPSR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); 558 | } 559 | 560 | // Clear Interrupt Pending 561 | __attribute__((always_inline)) static inline void NVIC_ClearPendingIRQ(IRQn_Type IRQn) { 562 | NVIC->IPRR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); 563 | } 564 | 565 | // Get Interrupt Active State 566 | __attribute__((always_inline)) static inline uint32_t NVIC_GetActive(IRQn_Type IRQn) { 567 | return((uint32_t)((NVIC->IACTR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); 568 | } 569 | 570 | // Set Interrupt Priority 571 | __attribute__((always_inline)) static inline void NVIC_SetPriority(IRQn_Type IRQn, uint8_t priority) { 572 | NVIC->IPRIOR[(uint32_t)(IRQn)] = priority; 573 | } 574 | 575 | // Wait for Interrupt 576 | __attribute__((always_inline)) static inline void __WFI(void) { 577 | NVIC->SCTLR &= ~(1<<3); // wfi 578 | asm volatile ("wfi"); 579 | } 580 | 581 | // Set Event 582 | __attribute__((always_inline)) static inline void _SEV(void) { 583 | uint32_t t; 584 | t = NVIC->SCTLR; 585 | NVIC->SCTLR |= (1<<3)|(1<<5); 586 | NVIC->SCTLR = (NVIC->SCTLR & ~(1<<5)) | ( t & (1<<5)); 587 | } 588 | 589 | // Wait for Events 590 | __attribute__((always_inline)) static inline void _WFE(void) { 591 | NVIC->SCTLR |= (1<<3); 592 | __asm volatile ("wfi"); 593 | } 594 | 595 | // Wait for Events 596 | __attribute__((always_inline)) static inline void __WFE(void) { 597 | _SEV(); 598 | _WFE(); 599 | _WFE(); 600 | } 601 | 602 | // Set VTF Interrupt 603 | __attribute__((always_inline)) static inline void SetVTFIRQ(uint32_t addr, IRQn_Type IRQn, uint8_t num, FunctionalState NewState) { 604 | if(num > 3) return; 605 | if(NewState != DISABLE) { 606 | NVIC->VTFIDR[num] = IRQn; 607 | NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)|0x1); 608 | } 609 | else { 610 | NVIC->VTFIDR[num] = IRQn; 611 | NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)&(~0x1)); 612 | } 613 | } 614 | 615 | // Initiate a system reset request 616 | __attribute__((always_inline)) static inline void NVIC_SystemReset(void) { 617 | NVIC->CFGR = NVIC_KEY3|(1<<7); 618 | } 619 | 620 | // Atomic Add with 32bit value 621 | __attribute__((always_inline)) static inline int32_t __AMOADD_W(volatile int32_t *addr, int32_t value) { 622 | int32_t result; 623 | __asm volatile ("amoadd.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 624 | return *addr; 625 | } 626 | 627 | // Atomic And with 32bit value 628 | __attribute__((always_inline)) static inline int32_t __AMOAND_W(volatile int32_t *addr, int32_t value) { 629 | int32_t result; 630 | __asm volatile ("amoand.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 631 | return *addr; 632 | } 633 | 634 | // Atomic signed MAX with 32bit value 635 | __attribute__((always_inline)) static inline int32_t __AMOMAX_W(volatile int32_t *addr, int32_t value) { 636 | int32_t result; 637 | __asm volatile ("amomax.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 638 | return *addr; 639 | } 640 | 641 | // Atomic unsigned MAX with 32bit value 642 | __attribute__((always_inline)) static inline uint32_t __AMOMAXU_W(volatile uint32_t *addr, uint32_t value) { 643 | uint32_t result; 644 | __asm volatile ("amomaxu.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 645 | return *addr; 646 | } 647 | 648 | // Atomic signed MIN with 32bit value 649 | __attribute__((always_inline)) static inline int32_t __AMOMIN_W(volatile int32_t *addr, int32_t value) { 650 | int32_t result; 651 | __asm volatile ("amomin.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 652 | return *addr; 653 | } 654 | 655 | // Atomic unsigned MIN with 32bit value 656 | __attribute__((always_inline)) static inline uint32_t __AMOMINU_W(volatile uint32_t *addr, uint32_t value) { 657 | uint32_t result; 658 | __asm volatile ("amominu.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 659 | return *addr; 660 | } 661 | 662 | // Atomic OR with 32bit value 663 | __attribute__((always_inline)) static inline int32_t __AMOOR_W(volatile int32_t *addr, int32_t value) { 664 | int32_t result; 665 | __asm volatile ("amoor.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 666 | return *addr; 667 | } 668 | 669 | // Atomically swap new 32bit value into memory using amoswap.d 670 | __attribute__((always_inline)) static inline uint32_t __AMOSWAP_W(volatile uint32_t *addr, uint32_t newval) { 671 | uint32_t result; 672 | __asm volatile ("amoswap.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(newval) : "memory"); 673 | return result; 674 | } 675 | 676 | // Atomic XOR with 32bit value 677 | __attribute__((always_inline)) static inline int32_t __AMOXOR_W(volatile int32_t *addr, int32_t value) { 678 | int32_t result; 679 | __asm volatile ("amoxor.w %0, %2, %1" : "=r"(result), "+A"(*addr) : "r"(value) : "memory"); 680 | return *addr; 681 | } 682 | 683 | #ifdef __cplusplus 684 | }; 685 | #endif 686 | -------------------------------------------------------------------------------- /software/pd_tester/src/gpio.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Basic GPIO Functions for CH32X035/X034/X033 * v0.4 * 3 | // =================================================================================== 4 | // 5 | // Pins must be defined as PA0, PA1, .., PB0, PB1, .. - e.g.: 6 | // #define PIN_LED PC0 // LED on pin PC0 7 | // 8 | // PIN functions available: 9 | // ------------------------ 10 | // PIN_input(PIN) set PIN as INPUT (floating, no pullup/pulldown) 11 | // PIN_input_PU(PIN) set PIN as INPUT with internal PULLUP resistor 12 | // PIN_input_PD(PIN) set PIN as INPUT with internal PULLDOWN resistor 13 | // PIN_input_AN(PIN) set PIN as INPUT for analog peripherals (e.g. ADC) (*) 14 | // PIN_output(PIN) set PIN as OUTPUT (push-pull) 15 | // PIN_alternate(PIN) set PIN as alternate output mode 16 | // 17 | // PIN_low(PIN) set PIN output value to LOW (*) 18 | // PIN_high(PIN) set PIN output value to HIGH 19 | // PIN_toggle(PIN) TOGGLE PIN output value 20 | // PIN_read(PIN) read PIN input value 21 | // PIN_write(PIN, val) write PIN output value (0 = LOW / 1 = HIGH) 22 | // 23 | // PIN interrupt and event functions available: 24 | // -------------------------------------------- 25 | // PIN_EVT_set(PIN,TYPE) Setup PIN event TYPE: 26 | // PIN_EVT_OFF, PIN_EVT_RISING, PIN_EVT_FALLING, PIN_EVT_BOTH 27 | // PIN_INT_set(PIN,TYPE) Setup PIN interrupt TYPE: 28 | // PIN_INT_OFF, PIN_INT_RISING, PIN_INT_FALLING, PIN_INT_BOTH 29 | // PIN_INT_enable() Enable PIN interrupts 30 | // PIN_INT_disable() Disable PIN interrupts 31 | // PIN_INTFLAG_read(PIN) Read interrupt flag of PIN 32 | // PIN_INTFLAG_clear(PIN) Clear interrupt flag of PIN 33 | // PIN_INT_ISR { } Pin interrupt service routine 34 | // 35 | // PORT functions available: 36 | // ------------------------- 37 | // PORT_enable(PIN) enable GPIO PORT of PIN 38 | // PORTA_enable() enable GPIO PORT A 39 | // PORTB_enable() enable GPIO PORT B 40 | // PORTC_enable() enable GPIO PORT C 41 | // PORTS_enable() enable all GPIO PORTS 42 | // 43 | // PORT_disable(PIN) disable GPIO PORT of PIN 44 | // PORTA_disable() disable GPIO PORT A 45 | // PORTB_disable() disable GPIO PORT B 46 | // PORTC_disable() disable GPIO PORT C 47 | // PORTS_disable() disable all GPIO PORTS 48 | // 49 | // Analog-to-Digital Converter (ADC) functions available: 50 | // ------------------------------------------------------ 51 | // ADC_init() init and enable ADC (must be called first) 52 | // ADC_enable() enable ADC (power-up) 53 | // ADC_disable() disable ADC (power-down) (*) 54 | // ADC_fast() set fast mode (fast speed, least accurate) 55 | // ADC_slow() set slow mode (slow speed, most accurate) (*) 56 | // ADC_medium() set medium mode (medium speed, medium accurate) 57 | // 58 | // ADC_input(PIN) set PIN as ADC input 59 | // ADC_input_VREF() set internal voltage referece (Vref) as ADC input 60 | // 61 | // ADC_read() sample and read 12-bit ADC value (0..4095) 62 | // ADC_read_VDD() sample and read supply voltage (VDD) in millivolts (mV) 63 | // 64 | // Analog Comparator (CMP) functions available: 65 | // -------------------------------------------- 66 | // CMP_lock() lock comparators (*) 67 | // CMP_unlock() unlock comparators 68 | // 69 | // CMPx_enable() enable CMPx (x = 1..3) 70 | // CMPx_disable() disable CMPx (*) 71 | // CMPx_HYS_enable() enable CMPx hysteresis 72 | // CMPx_HYS_disable() disable CMPx hysteresis (*) 73 | // 74 | // CMP1_OUT_PA1() set CMP1 output to pin PA1 75 | // CMP1_OUT_T2C1() set CMP1 output to timer2, channel1 (*) 76 | // CMP1_NEG_PA23() set CMP1 negative input to pin PA23 77 | // CMP1_NEG_PC3() set CMP1 negative input to pin PC3 (*) 78 | // CMP1_POS_PA0() set CMP1 positive input to pin PA0 79 | // CMP1_POS_PC19() set CMP1 positive input to pin PC19 (*) 80 | // 81 | // CMP2_OUT_PB2() set CMP2 output to pin PB2 82 | // CMP2_OUT_T2C2() set CMP2 output to timer2, channel2 (*) 83 | // CMP2_NEG_PC3() set CMP2 negative input to pin PC3 84 | // CMP2_NEG_PA22() set CMP2 negative input to pin PA22 (*) 85 | // CMP2_POS_PA11() set CMP2 positive input to pin PA11 86 | // CMP2_POS_PA12() set CMP2 positive input to pin PA12 (*) 87 | // 88 | // CMP3_OUT_PB3() set CMP3 output to pin PB3 89 | // CMP3_OUT_T2C3() set CMP3 output to timer2, channel3 (*) 90 | // CMP3_NEG_PC3() set CMP3 negative input to pin PC3 91 | // CMP3_NEG_PA2() set CMP3 negative input to pin PA2 (*) 92 | // CMP3_POS_PA14() set CMP3 positive input to pin PA14 93 | // CMP3_POS_PA13() set CMP3 positive input to pin PA13 (*) 94 | // 95 | // Operational Amplifier (OPA) functions available: 96 | // ------------------------------------------------ 97 | // not yet implemented 98 | // 99 | // Touch Key (TK) functions available: 100 | // ----------------------------------- 101 | // TK_init() init and enable touch key functions (must be called first) 102 | // TK_input(PIN) set PIN as touch key input 103 | // TK_read() returns TRUE if touch key is pressed 104 | // 105 | // Notes: 106 | // ------ 107 | // - (*) default state 108 | // - For interrupts and events: Each PIN number can only be used once simultaneously. 109 | // (For example, PA1 and PC1 cannot be used simultaneously, but PA1 and PC2). 110 | // - Pins used for ADC must be set with PIN_input_AN beforehand. ADC input pins are: 111 | // PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PB0, PB1, PC0, PC1, PC2, PC3. 112 | // 113 | // 2023 by Stefan Wagner: https://github.com/wagiminator 114 | 115 | #pragma once 116 | 117 | #ifdef __cplusplus 118 | extern "C" { 119 | #endif 120 | 121 | #include "system.h" 122 | 123 | // =================================================================================== 124 | // Enumerate PIN Designators (use these designators to define pins) 125 | // =================================================================================== 126 | enum{ 127 | PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15, 128 | PA16, PA17, PA18, PA19, PA20, PA21, PA22, PA23, PA24, PA25, PA26, PA27, PA28, PA29, PA30, PA31, 129 | PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15, 130 | PB16, PB17, PB18, PB19, PB20, PB21, PB22, PB23, PB24, PB25, PB26, PB27, PB28, PB29, PB30, PB31, 131 | PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15, 132 | PC16, PC17, PC18, PC19, PC20, PC21, PC22, PC23 133 | }; 134 | 135 | // =================================================================================== 136 | // Set PIN as INPUT (high impedance, no pullup/pulldown) 137 | // =================================================================================== 138 | #define PIN_input(PIN) \ 139 | ((PIN>=PA0)&&(PIN<=PA7) ? ( GPIOA->CFGLR = (GPIOA->CFGLR \ 140 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 141 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 142 | ((PIN>=PA8)&&(PIN<=PA15) ? ( GPIOA->CFGHR = (GPIOA->CFGHR \ 143 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 144 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 145 | ((PIN>=PA16)&&(PIN<=PA23) ? ( GPIOA->CFGXR = (GPIOA->CFGXR \ 146 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 147 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 148 | ((PIN>=PB0)&&(PIN<=PB7) ? ( GPIOB->CFGLR = (GPIOB->CFGLR \ 149 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 150 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 151 | ((PIN>=PB8)&&(PIN<=PB15) ? ( GPIOB->CFGHR = (GPIOB->CFGHR \ 152 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 153 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 154 | ((PIN>=PB16)&&(PIN<=PB23) ? ( GPIOB->CFGXR = (GPIOB->CFGXR \ 155 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 156 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 157 | ((PIN>=PC0)&&(PIN<=PC7) ? ( GPIOC->CFGLR = (GPIOC->CFGLR \ 158 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 159 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 160 | ((PIN>=PC8)&&(PIN<=PC15) ? ( GPIOC->CFGHR = (GPIOC->CFGHR \ 161 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 162 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 163 | ((PIN>=PC16)&&(PIN<=PC23) ? ( GPIOC->CFGXR = (GPIOC->CFGXR \ 164 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 165 | | ((uint32_t)0b0100<<(((PIN)&7)<<2))) : \ 166 | (0)))))))))) 167 | #define PIN_input_HI PIN_input 168 | #define PIN_input_FL PIN_input 169 | 170 | // =================================================================================== 171 | // Set PIN as INPUT with internal PULLUP resistor 172 | // =================================================================================== 173 | #define PIN_input_PU(PIN) \ 174 | ((PIN>=PA0)&&(PIN<=PA7) ? ({GPIOA->CFGLR = (GPIOA->CFGLR \ 175 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 176 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 177 | GPIOA->BSHR = ((uint32_t)1<<((PIN)&15)); }) : \ 178 | ((PIN>=PA8)&&(PIN<=PA15) ? ({GPIOA->CFGHR = (GPIOA->CFGHR \ 179 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 180 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 181 | GPIOA->BSHR = ((uint32_t)1<<((PIN)&15)); }) : \ 182 | ((PIN>=PA16)&&(PIN<=PA23) ? ({GPIOA->CFGXR = (GPIOA->CFGXR \ 183 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 184 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 185 | GPIOA->BSXR = ((uint32_t)1<<((PIN)&15)); }) : \ 186 | ((PIN>=PB0)&&(PIN<=PB7) ? ({GPIOB->CFGLR = (GPIOB->CFGLR \ 187 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 188 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 189 | GPIOB->BSHR = ((uint32_t)1<<((PIN)&15)); }) : \ 190 | ((PIN>=PB8)&&(PIN<=PB15) ? ({GPIOB->CFGHR = (GPIOB->CFGHR \ 191 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 192 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 193 | GPIOB->BSHR = ((uint32_t)1<<((PIN)&15)); }) : \ 194 | ((PIN>=PB16)&&(PIN<=PB23) ? ({GPIOB->CFGXR = (GPIOB->CFGXR \ 195 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 196 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 197 | GPIOB->BSXR = ((uint32_t)1<<((PIN)&15)); }) : \ 198 | ((PIN>=PC0)&&(PIN<=PC7) ? ({GPIOC->CFGLR = (GPIOC->CFGLR \ 199 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 200 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 201 | GPIOC->BSHR = ((uint32_t)1<<((PIN)&15)); }) : \ 202 | ((PIN>=PC8)&&(PIN<=PC15) ? ({GPIOC->CFGHR = (GPIOC->CFGHR \ 203 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 204 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 205 | GPIOC->BSHR = ((uint32_t)1<<((PIN)&15)); }) : \ 206 | ((PIN>=PC16)&&(PIN<=PC23) ? ({GPIOC->CFGXR = (GPIOC->CFGXR \ 207 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 208 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 209 | GPIOC->BSXR = ((uint32_t)1<<((PIN)&15)); }) : \ 210 | (0)))))))))) 211 | 212 | // =================================================================================== 213 | // Set PIN as INPUT with internal PULLDOWN resistor 214 | // =================================================================================== 215 | #define PIN_input_PD(PIN) \ 216 | ((PIN>=PA0)&&(PIN<=PA7) ? ({GPIOA->CFGLR = (GPIOA->CFGLR \ 217 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 218 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 219 | GPIOA->BSHR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 220 | ((PIN>=PA8)&&(PIN<=PA15) ? ({GPIOA->CFGHR = (GPIOA->CFGHR \ 221 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 222 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 223 | GPIOA->BSHR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 224 | ((PIN>=PA16)&&(PIN<=PA23) ? ({GPIOA->CFGXR = (GPIOA->CFGXR \ 225 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 226 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 227 | GPIOA->BSXR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 228 | ((PIN>=PB0)&&(PIN<=PB7) ? ({GPIOB->CFGLR = (GPIOB->CFGLR \ 229 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 230 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 231 | GPIOB->BSHR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 232 | ((PIN>=PB8)&&(PIN<=PB15) ? ({GPIOB->CFGHR = (GPIOB->CFGHR \ 233 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 234 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 235 | GPIOB->BSHR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 236 | ((PIN>=PB16)&&(PIN<=PB23) ? ({GPIOB->CFGXR = (GPIOB->CFGXR \ 237 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 238 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 239 | GPIOB->BSXR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 240 | ((PIN>=PC0)&&(PIN<=PC7) ? ({GPIOC->CFGLR = (GPIOC->CFGLR \ 241 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 242 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 243 | GPIOC->BSHR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 244 | ((PIN>=PC8)&&(PIN<=PC15) ? ({GPIOC->CFGHR = (GPIOC->CFGHR \ 245 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 246 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 247 | GPIOC->BSHR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 248 | ((PIN>=PC16)&&(PIN<=PC23) ? ({GPIOC->CFGXR = (GPIOC->CFGXR \ 249 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 250 | | ((uint32_t)0b1000<<(((PIN)&7)<<2)); \ 251 | GPIOC->BSXR = (((uint32_t)1<<16)<<((PIN)&15));}) : \ 252 | (0)))))))))) 253 | 254 | // =================================================================================== 255 | // Set PIN as INPUT for analog peripherals (e.g. ADC) 256 | // =================================================================================== 257 | #define PIN_input_AN(PIN) \ 258 | ((PIN>=PA0)&&(PIN<=PA7) ? ( GPIOA->CFGLR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 259 | ((PIN>=PA8)&&(PIN<=PA15) ? ( GPIOA->CFGHR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 260 | ((PIN>=PA16)&&(PIN<=PA23) ? ( GPIOA->CFGXR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 261 | ((PIN>=PB0)&&(PIN<=PB7) ? ( GPIOB->CFGLR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 262 | ((PIN>=PB8)&&(PIN<=PB15) ? ( GPIOB->CFGHR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 263 | ((PIN>=PB16)&&(PIN<=PB23) ? ( GPIOB->CFGXR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 264 | ((PIN>=PC0)&&(PIN<=PC7) ? ( GPIOC->CFGLR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 265 | ((PIN>=PC8)&&(PIN<=PC15) ? ( GPIOC->CFGHR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 266 | ((PIN>=PC16)&&(PIN<=PC23) ? ( GPIOC->CFGXR &= ~((uint32_t)0b1111<<(((PIN)&7)<<2)) ) : \ 267 | (0)))))))))) 268 | #define PIN_input_AD PIN_input_AN 269 | #define PIN_input_ADC PIN_input_AN 270 | 271 | // =================================================================================== 272 | // Set PIN as OUTPUT (push-pull) 273 | // =================================================================================== 274 | #define PIN_output(PIN) \ 275 | ((PIN>=PA0)&&(PIN<=PA7) ? ( GPIOA->CFGLR = (GPIOA->CFGLR \ 276 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 277 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 278 | ((PIN>=PA8)&&(PIN<=PA15) ? ( GPIOA->CFGHR = (GPIOA->CFGHR \ 279 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 280 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 281 | ((PIN>=PA16)&&(PIN<=PA23) ? ( GPIOA->CFGXR = (GPIOA->CFGXR \ 282 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 283 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 284 | ((PIN>=PB0)&&(PIN<=PB7) ? ( GPIOB->CFGLR = (GPIOB->CFGLR \ 285 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 286 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 287 | ((PIN>=PB8)&&(PIN<=PB15) ? ( GPIOB->CFGHR = (GPIOB->CFGHR \ 288 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 289 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 290 | ((PIN>=PB16)&&(PIN<=PB23) ? ( GPIOB->CFGXR = (GPIOB->CFGXR \ 291 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 292 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 293 | ((PIN>=PC0)&&(PIN<=PC7) ? ( GPIOC->CFGLR = (GPIOC->CFGLR \ 294 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 295 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 296 | ((PIN>=PC8)&&(PIN<=PC15) ? ( GPIOC->CFGHR = (GPIOC->CFGHR \ 297 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 298 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 299 | ((PIN>=PC16)&&(PIN<=PC23) ? ( GPIOC->CFGXR = (GPIOC->CFGXR \ 300 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 301 | | ((uint32_t)0b0011<<(((PIN)&7)<<2))) : \ 302 | (0)))))))))) 303 | #define PIN_output_PP PIN_output 304 | 305 | // =================================================================================== 306 | // Set PIN as alternate output mode 307 | // =================================================================================== 308 | #define PIN_alternate(PIN) \ 309 | ((PIN>=PA0)&&(PIN<=PA7) ? ( GPIOA->CFGLR = (GPIOA->CFGLR \ 310 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 311 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 312 | ((PIN>=PA8)&&(PIN<=PA15) ? ( GPIOA->CFGHR = (GPIOA->CFGHR \ 313 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 314 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 315 | ((PIN>=PA16)&&(PIN<=PA23) ? ( GPIOA->CFGXR = (GPIOA->CFGXR \ 316 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 317 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 318 | ((PIN>=PB0)&&(PIN<=PB7) ? ( GPIOB->CFGLR = (GPIOB->CFGLR \ 319 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 320 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 321 | ((PIN>=PB8)&&(PIN<=PB15) ? ( GPIOB->CFGHR = (GPIOB->CFGHR \ 322 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 323 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 324 | ((PIN>=PB16)&&(PIN<=PB23) ? ( GPIOB->CFGXR = (GPIOB->CFGXR \ 325 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 326 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 327 | ((PIN>=PC0)&&(PIN<=PC7) ? ( GPIOC->CFGLR = (GPIOC->CFGLR \ 328 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 329 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 330 | ((PIN>=PC8)&&(PIN<=PC15) ? ( GPIOC->CFGHR = (GPIOC->CFGHR \ 331 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 332 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 333 | ((PIN>=PC16)&&(PIN<=PC23) ? ( GPIOC->CFGXR = (GPIOC->CFGXR \ 334 | & ~((uint32_t)0b1111<<(((PIN)&7)<<2))) \ 335 | | ((uint32_t)0b1011<<(((PIN)&7)<<2))) : \ 336 | (0)))))))))) 337 | 338 | // =================================================================================== 339 | // Set PIN output value to LOW 340 | // =================================================================================== 341 | #define PIN_low(PIN) \ 342 | ((PIN>=PA0 )&&(PIN<=PA15) ? ( GPIOA->BCR = 1<<((PIN)&15) ) : \ 343 | ((PIN>=PB0 )&&(PIN<=PB15) ? ( GPIOB->BCR = 1<<((PIN)&15) ) : \ 344 | ((PIN>=PC0 )&&(PIN<=PC15) ? ( GPIOC->BCR = 1<<((PIN)&15) ) : \ 345 | ((PIN>=PA16)&&(PIN<=PA23) ? ( GPIOA->BSXR = ((1<<16)<<((PIN)&15)) ) : \ 346 | ((PIN>=PB16)&&(PIN<=PB23) ? ( GPIOB->BSXR = ((1<<16)<<((PIN)&15)) ) : \ 347 | ((PIN>=PC16)&&(PIN<=PC23) ? ( GPIOC->BSXR = ((1<<16)<<((PIN)&15)) ) : \ 348 | (0))))))) 349 | 350 | // =================================================================================== 351 | // Set PIN output value to HIGH 352 | // =================================================================================== 353 | #define PIN_high(PIN) \ 354 | ((PIN>=PA0 )&&(PIN<=PA15) ? ( GPIOA->BSHR = 1<<((PIN)&15) ) : \ 355 | ((PIN>=PA16)&&(PIN<=PA23) ? ( GPIOA->BSXR = 1<<((PIN)&15) ) : \ 356 | ((PIN>=PB0 )&&(PIN<=PB15) ? ( GPIOB->BSHR = 1<<((PIN)&15) ) : \ 357 | ((PIN>=PB16)&&(PIN<=PB23) ? ( GPIOB->BSXR = 1<<((PIN)&15) ) : \ 358 | ((PIN>=PC0 )&&(PIN<=PC15) ? ( GPIOC->BSHR = 1<<((PIN)&15) ) : \ 359 | ((PIN>=PC16)&&(PIN<=PC23) ? ( GPIOC->BSXR = 1<<((PIN)&15) ) : \ 360 | (0))))))) 361 | 362 | // =================================================================================== 363 | // Toggle PIN output value 364 | // =================================================================================== 365 | #define PIN_toggle(PIN) \ 366 | ((PIN>=PA0)&&(PIN<=PA23) ? ( GPIOA->OUTDR ^= 1<<((PIN)&31) ) : \ 367 | ((PIN>=PB0)&&(PIN<=PB23) ? ( GPIOB->OUTDR ^= 1<<((PIN)&31) ) : \ 368 | ((PIN>=PC0)&&(PIN<=PC23) ? ( GPIOC->OUTDR ^= 1<<((PIN)&31) ) : \ 369 | (0)))) 370 | 371 | // =================================================================================== 372 | // Read PIN input value (returns 0 for LOW, 1 for HIGH) 373 | // =================================================================================== 374 | #define PIN_read(PIN) \ 375 | ((PIN>=PA0)&&(PIN<=PA23) ? ( (GPIOA->INDR>>((PIN)&31))&1 ) : \ 376 | ((PIN>=PB0)&&(PIN<=PB23) ? ( (GPIOB->INDR>>((PIN)&31))&1 ) : \ 377 | ((PIN>=PC0)&&(PIN<=PC23) ? ( (GPIOC->INDR>>((PIN)&31))&1 ) : \ 378 | (0)))) 379 | 380 | // =================================================================================== 381 | // Write PIN output value (0 = LOW / 1 = HIGH) 382 | // =================================================================================== 383 | #define PIN_write(PIN, val) (val)?(PIN_high(PIN)):(PIN_low(PIN)) 384 | 385 | // =================================================================================== 386 | // Setup PIN interrupt 387 | // =================================================================================== 388 | enum{PIN_INT_OFF, PIN_INT_RISING, PIN_INT_FALLING, PIN_INT_BOTH}; 389 | 390 | #define EXTICR1 EXTICR[0] 391 | #define EXTICR2 EXTICR[1] 392 | 393 | #define PIN_INT_set(PIN, TYPE) { \ 394 | ((PIN>=PA0 )&&(PIN<=PA15) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPAEN; \ 395 | AFIO->EXTICR1 &= ~((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 396 | ((PIN>=PA16)&&(PIN<=PA23) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPAEN; \ 397 | AFIO->EXTICR2 &= ~((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 398 | ((PIN>=PB0 )&&(PIN<=PB15) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPBEN; \ 399 | AFIO->EXTICR1 = (AFIO->EXTICR1 \ 400 | & ~((uint32_t)3<<(((PIN)&15)<<1))) \ 401 | | ((uint32_t)2<<(((PIN)&15)<<1)); }) : \ 402 | ((PIN>=PB16)&&(PIN<=PB23) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPBEN; \ 403 | AFIO->EXTICR2 = (AFIO->EXTICR2 \ 404 | & ~((uint32_t)3<<(((PIN)&15)<<1))) \ 405 | | ((uint32_t)2<<(((PIN)&15)<<1)); }) : \ 406 | ((PIN>=PC0 )&&(PIN<=PC15) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPCEN; \ 407 | AFIO->EXTICR1 |= ((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 408 | ((PIN>=PC16)&&(PIN<=PC23) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPCEN; \ 409 | AFIO->EXTICR2 |= ((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 410 | (0))))))); \ 411 | (TYPE & 3) ? (EXTI->INTENR |= (uint32_t)1<<((PIN)&31)) : \ 412 | (EXTI->INTENR &= ~((uint32_t)1<<((PIN)&31))); \ 413 | (TYPE & 1) ? (EXTI->RTENR |= (uint32_t)1<<((PIN)&31)) : \ 414 | (EXTI->RTENR &= ~((uint32_t)1<<((PIN)&31))); \ 415 | (TYPE & 2) ? (EXTI->FTENR |= (uint32_t)1<<((PIN)&31)) : \ 416 | (EXTI->FTENR &= ~((uint32_t)1<<((PIN)&31))); \ 417 | } 418 | 419 | #define PIN_INT_enable() {NVIC_EnableIRQ(EXTI7_0_IRQn); \ 420 | NVIC_EnableIRQ(EXTI15_8_IRQn); \ 421 | NVIC_EnableIRQ(EXTI25_16_IRQn); } 422 | #define PIN_INT_disable() {NVIC_DisableIRQ(EXTI7_0_IRQn); \ 423 | NVIC_DisableIRQ(EXTI15_8_IRQn); \ 424 | NVIC_DisableIRQ(EXTI25_16_IRQn);} 425 | 426 | #define PIN_INTFLAG_read(PIN) (EXTI->INTFR & ((uint32_t)1 << ((PIN) & 31))) 427 | #define PIN_INTFLAG_clear(PIN) EXTI->INTFR = ((uint32_t)1 << ((PIN) & 31)) 428 | 429 | #define PIN_INT_ISR \ 430 | void PIN_INT_IRQHandler(void) __attribute__((interrupt)); \ 431 | void EXTI7_0_IRQHandler(void) __attribute__((alias("PIN_INT_IRQHandler"))); \ 432 | void EXTI15_8_IRQHandler(void) __attribute__((alias("PIN_INT_IRQHandler"))); \ 433 | void EXTI25_16_IRQHandler(void) __attribute__((alias("PIN_INT_IRQHandler"))); \ 434 | void PIN_INT_IRQHandler(void) 435 | 436 | // =================================================================================== 437 | // Setup PIN event 438 | // =================================================================================== 439 | enum{PIN_EVT_OFF, PIN_EVT_RISING, PIN_EVT_FALLING, PIN_EVT_BOTH}; 440 | 441 | #define PIN_EVT_set(PIN, TYPE) { \ 442 | ((PIN>=PA0 )&&(PIN<=PA15) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPAEN; \ 443 | AFIO->EXTICR1 &= ~((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 444 | ((PIN>=PA16)&&(PIN<=PA23) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPAEN; \ 445 | AFIO->EXTICR2 &= ~((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 446 | ((PIN>=PB0 )&&(PIN<=PB15) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPBEN; \ 447 | AFIO->EXTICR1 = (AFIO->EXTICR1 \ 448 | & ~((uint32_t)3<<(((PIN)&15)<<1))) \ 449 | | ((uint32_t)2<<(((PIN)&15)<<1)); }) : \ 450 | ((PIN>=PB16)&&(PIN<=PB23) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPBEN; \ 451 | AFIO->EXTICR2 = (AFIO->EXTICR2 \ 452 | & ~((uint32_t)3<<(((PIN)&15)<<1))) \ 453 | | ((uint32_t)2<<(((PIN)&15)<<1)); }) : \ 454 | ((PIN>=PC0 )&&(PIN<=PC15) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPCEN; \ 455 | AFIO->EXTICR1 |= ((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 456 | ((PIN>=PC16)&&(PIN<=PC23) ? ({RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPCEN; \ 457 | AFIO->EXTICR2 |= ((uint32_t)3<<(((PIN)&15)<<1)); }) : \ 458 | (0))))))); \ 459 | (TYPE & 3) ? (EXTI->EVENR |= (uint32_t)1<<((PIN)&31)) : \ 460 | (EXTI->EVENR &= ~((uint32_t)1<<((PIN)&31))); \ 461 | (TYPE & 1) ? (EXTI->RTENR |= (uint32_t)1<<((PIN)&31)) : \ 462 | (EXTI->RTENR &= ~((uint32_t)1<<((PIN)&31))); \ 463 | (TYPE & 2) ? (EXTI->FTENR |= (uint32_t)1<<((PIN)&31)) : \ 464 | (EXTI->FTENR &= ~((uint32_t)1<<((PIN)&31))); \ 465 | } 466 | 467 | // =================================================================================== 468 | // Enable GPIO PORTS 469 | // =================================================================================== 470 | #define PORTA_enable() RCC->APB2PCENR |= RCC_IOPAEN; 471 | #define PORTB_enable() RCC->APB2PCENR |= RCC_IOPBEN; 472 | #define PORTC_enable() RCC->APB2PCENR |= RCC_IOPCEN; 473 | #define PORTS_enable() RCC->APB2PCENR |= RCC_IOPAEN | RCC_IOPBEN | RCC_IOPCEN 474 | 475 | #define PORT_enable(PIN) \ 476 | ((PIN>=PA0)&&(PIN<=PA15) ? ( RCC->APB2PCENR |= RCC_IOPAEN ) : \ 477 | ((PIN>=PB0)&&(PIN<=PB15) ? ( RCC->APB2PCENR |= RCC_IOPBEN ) : \ 478 | ((PIN>=PC0)&&(PIN<=PC15) ? ( RCC->APB2PCENR |= RCC_IOPCEN ) : \ 479 | (0)))) 480 | 481 | // =================================================================================== 482 | // Disable GPIO PORTS 483 | // =================================================================================== 484 | #define PORTA_disable() RCC->APB2PCENR &= ~RCC_IOPAEN 485 | #define PORTB_disable() RCC->APB2PCENR &= ~RCC_IOPBEN 486 | #define PORTC_disable() RCC->APB2PCENR &= ~RCC_IOPCEN 487 | #define PORTS_disable() RCC->APB2PCENR &= ~(RCC_IOPAEN | RCC_IOPBEN | RCC_IOPCEN) 488 | 489 | #define PORT_disable(PIN) \ 490 | ((PIN>=PA0)&&(PIN<=PA15) ? ( RCC->APB2PCENR &= ~RCC_IOPAEN ) : \ 491 | ((PIN>=PB0)&&(PIN<=PB15) ? ( RCC->APB2PCENR &= ~RCC_IOPBEN ) : \ 492 | ((PIN>=PC0)&&(PIN<=PC15) ? ( RCC->APB2PCENR &= ~RCC_IOPCEN ) : \ 493 | (0)))) 494 | 495 | // =================================================================================== 496 | // Analog-to-Digital Converter (ADC) Functions 497 | // =================================================================================== 498 | #define ADC_enable() ADC1->CTLR2 |= ADC_ADON 499 | #define ADC_disable() ADC1->CTLR2 &= ~ADC_ADON 500 | 501 | #define ADC_fast() { ADC1->CTLR3 = 0b00000000000000000000000000000101; \ 502 | ADC1->SAMPTR1 = 0b00000000000000000000000000000000; \ 503 | ADC1->SAMPTR2 = 0b00000000000000000000000000000000; } 504 | #define ADC_slow() { ADC1->CTLR3 = 0b00000000000000000000000000001011; \ 505 | ADC1->SAMPTR1 = 0b00111111111111111111111111111111; \ 506 | ADC1->SAMPTR2 = 0b00111111111111111111111111111111; } 507 | #define ADC_medium() { ADC1->CTLR3 = 0b00000000000000000000000000001000; \ 508 | ADC1->SAMPTR1 = 0b00011011011011011011011011011011; \ 509 | ADC1->SAMPTR2 = 0b00011011011011011011011011011011; } 510 | 511 | #define ADC_input_VREF() ADC1->RSQR3 = 15 512 | 513 | #define ADC_input(PIN) \ 514 | ((PIN>=PA0)&&(PIN<=PA7) ? (ADC1->RSQR3 = (PIN)&7) : \ 515 | ((PIN>=PB0)&&(PIN<=PB1) ? (ADC1->RSQR3 = ((PIN)&7)+8) : \ 516 | ((PIN>=PC0)&&(PIN<=PC3) ? (ADC1->RSQR3 = ((PIN)&7)+10) : \ 517 | (0)))) 518 | 519 | static inline void ADC_init(void) { 520 | RCC->APB2PCENR |= RCC_ADC1EN | RCC_AFIOEN; // enable ADC and AFIO 521 | ADC_slow(); // set slow speed as default 522 | ADC1->CTLR2 = ADC_ADON // turn on ADC 523 | | ADC_EXTSEL; // software triggering 524 | } 525 | 526 | static inline uint16_t ADC_read(void) { 527 | ADC_enable(); // make sure ADC is enabled 528 | ADC1->CTLR2 |= ADC_SWSTART; // start conversion 529 | while(!(ADC1->STATR & ADC_EOC)); // wait until finished 530 | return ADC1->RDATAR; // return result 531 | } 532 | 533 | static inline uint16_t ADC_read_VDD(void) { 534 | ADC_input_VREF(); // set VREF as ADC input 535 | return((uint32_t)1200 * 4095 / ADC_read()); // return VDD in mV 536 | } 537 | 538 | // =================================================================================== 539 | // Analog Comparator (CMP) Functions 540 | // =================================================================================== 541 | #define CMP_lock() OPA->CTLR2 |= OPA_CTLR2_CMP_LOCK 542 | #define CMP_unlock() {CMP->KEY = CMP_KEY1; CMP->KEY = CMP_KEY2;} 543 | 544 | #define CMP1_enable() OPA->CTLR2 |= OPA_CTLR2_EN1 545 | #define CMP1_disable() OPA->CTLR2 &= ~OPA_CTLR2_EN1 546 | #define CMP1_HYS_enable() OPA->CTLR2 |= OPA_CTLR2_HYEN1 547 | #define CMP1_HYS_disable() OPA->CTLR2 &= ~OPA_CTLR2_HYEN1 548 | #define CMP1_OUT_PA1() OPA->CTLR2 |= OPA_CTLR2_MODE1 549 | #define CMP1_OUT_T2C1() OPA->CTLR2 &= ~OPA_CTLR2_MODE1 550 | #define CMP1_NEG_PA23() OPA->CTLR2 |= OPA_CTLR2_NSEL1 551 | #define CMP1_NEG_PC3() OPA->CTLR2 &= ~OPA_CTLR2_NSEL1 552 | #define CMP1_POS_PA0() OPA->CTLR2 |= OPA_CTLR2_PSEL1 553 | #define CMP1_POS_PC19() OPA->CTLR2 &= ~OPA_CTLR2_PSEL1 554 | 555 | #define CMP2_enable() OPA->CTLR2 |= OPA_CTLR2_EN2 556 | #define CMP2_disable() OPA->CTLR2 &= ~OPA_CTLR2_EN2 557 | #define CMP2_HYS_enable() OPA->CTLR2 |= OPA_CTLR2_HYEN2 558 | #define CMP2_HYS_disable() OPA->CTLR2 &= ~OPA_CTLR2_HYEN2 559 | #define CMP2_OUT_PB2() OPA->CTLR2 |= OPA_CTLR2_MODE2 560 | #define CMP2_OUT_T2C2() OPA->CTLR2 &= ~OPA_CTLR2_MODE2 561 | #define CMP2_NEG_PC3() OPA->CTLR2 |= OPA_CTLR2_NSEL2 562 | #define CMP2_NEG_PA22() OPA->CTLR2 &= ~OPA_CTLR2_NSEL2 563 | #define CMP2_POS_PA11() OPA->CTLR2 |= OPA_CTLR2_PSEL2 564 | #define CMP2_POS_PA12() OPA->CTLR2 &= ~OPA_CTLR2_PSEL2 565 | 566 | #define CMP3_enable() OPA->CTLR2 |= OPA_CTLR2_EN3 567 | #define CMP3_disable() OPA->CTLR2 &= ~OPA_CTLR2_EN3 568 | #define CMP3_HYS_enable() OPA->CTLR2 |= OPA_CTLR2_HYEN3 569 | #define CMP3_HYS_disable() OPA->CTLR2 &= ~OPA_CTLR2_HYEN3 570 | #define CMP3_OUT_PB3() OPA->CTLR2 |= OPA_CTLR2_MODE3 571 | #define CMP3_OUT_T2C3() OPA->CTLR2 &= ~OPA_CTLR2_MODE3 572 | #define CMP3_NEG_PC3() OPA->CTLR2 |= OPA_CTLR2_NSEL3 573 | #define CMP3_NEG_PA2() OPA->CTLR2 &= ~OPA_CTLR2_NSEL3 574 | #define CMP3_POS_PA14() OPA->CTLR2 |= OPA_CTLR2_PSEL3 575 | #define CMP3_POS_PA13() OPA->CTLR2 &= ~OPA_CTLR2_PSEL3 576 | 577 | // =================================================================================== 578 | // Operational Amplifier (OPA) Functions 579 | // =================================================================================== 580 | // not yet implemented 581 | 582 | // =================================================================================== 583 | // Touch Key (TK) Functions 584 | // =================================================================================== 585 | #define TK_input(PIN) ADC_input(PIN) 586 | 587 | static inline void TK_init(void) { 588 | ADC_init(); // init ADC 589 | ADC1->CTLR1 |= ADC_TKENABLE; // enable touch key 590 | ADC1->IDATAR1 = 0x80; // TKEY1->CHGOFFSET = 0x80; 591 | } 592 | 593 | static inline uint8_t TK_read(void) { 594 | uint8_t result; 595 | uint16_t value; 596 | ADC_enable(); // (re-)enable ADC 597 | ADC1->RDATAR = 0x08; // (TKEY1->ACT_DCG) set discharge time and start 598 | while(!(ADC1->STATR & ADC_EOC)); // wait until sampling completed 599 | value = ADC1->RDATAR; // read sampling value 600 | result = (value == 2047); // 2047 if pressed 601 | ADC1->RDATAR = 0x08; // second sampling (blind) 602 | while(!(ADC1->STATR & ADC_EOC)); 603 | value = ADC1->RDATAR; 604 | return result; 605 | } 606 | 607 | #ifdef __cplusplus 608 | }; 609 | #endif 610 | --------------------------------------------------------------------------------