SOP8 WARS!
Recently Atul Ravi of PICUNO fame asked me why I "switched" from ATtiny13 to the PFS154. I haven't really - in fact the ATtiny13 is still near and dear to my heart, not least because I spent six months writing assembly code snippets for this little microcomputer. You can read all about it by clicking on this link.
However, I do use the PFS154 for the candle project (among others) for the following reasons:
- Exotic new tech interests me
- Lower power requirements
- "Free" programmer (which cost me months of pain)
- Three PWM channels, versus two
- Ability to wind down the voltage and current requirements by running the IC as low as 15kHz!
- Challenging to learn a new toolchain
In the great comparison, here is what ChatGPT had to say (usual caveats apply):
It's not all beer and skittles though, and one missing feature that rankled was the lack of an analogue to digital converter (ADC) on the PFS154. I mean - why!? It's such a basic requirement to take an analogue signal (e.g. distance from object using an ultrasonic signal).
Anyway, I'm not bitter at all.
And so enter stage left the previously shelved PFS173, on the bench at 14 cents each from LCSC ordered in June 2020 (now discontinued for some reason).
Originally I just wanted to talk to it (e.g. blinky), but in the end not only did I get some PWM action, but also the ADC - this in itself is interesting as to date I have not seen any code available for linking ADC to PWM on this chip. See for instance the github site for examples for these chips:
I do have one embarrassing admission despite the win. My final code does not use interrupts but rather gallops along around 100kHz and checks every single time it loops for the ADC conversion, and the "time" for the LED to blink. It's positively neanderthal, but nonetheless effective.
I will aim to find out more about the PFS173 interrupt landscape and update this travesty in the future. In the meantime, here is the code for the blink and PWM version.
/* Test Code for PFS173 Blink and PWM +-\/-+ VDD 1| |8 GND PA7/X1 2| |7 PA0/AD10/CO/INT0/PG0PWM PA6/X2 3| |6 PA4/AD9/CIN+/CIN1-/INT1/PG1PWM PA5/PRSTB/PG2PWM 4| |5 PA3/AD8/CIN0-/TM2PWM/PG2PWM +----+ Tue 23 Apr 2024 17:35:37 AES https://www.youtube.com/c/onecircuit-as https://onecircuit.blogspot.com/ */ #include "../device.h" #include "../easy-pdk/calibrate.h" #include "../auto_sysclock.h" #include "../delay.h" #include <stdbool.h> #define LED4_BIT 4 // PWM #define LED3_BIT 3 // Blinky #define turnLedOn() PA &= ~(1 << LED3_BIT) #define turnLedOff() PA |= (1 << LED3_BIT) bool ledon = true; // crude timing for blinky long mytimer = 30; volatile long counter = 0; // PFS173 PWM #define PWM_MAX 255 // check in here to see if LED is on or off void checkled() { counter = counter + 1; if (counter > mytimer) { counter = 0; if (ledon) { turnLedOff(); ledon = false; } else { turnLedOn(); ledon = true; } } } // Main program void main() { // Initialize hardware PAC |= (1 << LED4_BIT); PAC |= (1 << LED3_BIT); PWMGCUBL = PWM_MAX << 5; // Set the PWM upper bound (lower 3 bits) PWMGCUBH = PWM_MAX >> 3; // (upper 5 bits) PWMG1DTL = 0x00; // Clear the LED PWM duty value PWMG1DTH = 0x00; PWMGCLK = (uint8_t)(PWMGCLK_PWMG_ENABLE | PWMGCLK_CLK_IHRC); PWMG1C = (uint8_t)(PWMG1C_INVERT_OUT | PWMG1C_OUT_PWMG1 | PWMG1C_OUT_PA4); ledon = false; // Main loop while (1) { uint8_t fadeValue; // Fade in from min to max in increments of 5 for (fadeValue = 0; fadeValue < PWM_MAX; fadeValue += 5) { PWMG1DTL = fadeValue << 5; // Set the LED PWM duty value (lower 3 bits) PWMG1DTH = fadeValue >> 3; // (upper 8 bits) _delay_ms(30); // wait for 30 milliseconds to see the dimming effect checkled(); } // Fade out from max to min in increments of 5 for (fadeValue = PWM_MAX; fadeValue > 0; fadeValue -= 5) { PWMG1DTL = fadeValue << 5; // Set the LED PWM duty value (lower 3 bits) PWMG1DTH = fadeValue >> 3; // (upper 8 bits) _delay_ms(30); // wait for 30 milliseconds to see the dimming effect checkled(); } } } // Startup code - Setup/calibrate system clock unsigned char _sdcc_external_startup(void) { AUTO_INIT_SYSCLOCK(); AUTO_CALIBRATE_SYSCLOCK(TARGET_VDD_MV); return 0; // Return 0 to inform SDCC to continue with normal initialization. }
And here is the code which does Blink and ADC linked to PWM (a triumph!):
/* Test Code for PFS173 ADC and PWM +-\/-+ VDD 1| |8 GND PA7/X1 2| |7 PA0/AD10/CO/INT0/PG0PWM PA6/X2 3| |6 PA4/AD9/CIN+/CIN1-/INT1/PG1PWM PA5/PRSTB/PG2PWM 4| |5 PA3/AD8/CIN0-/TM2PWM/PG2PWM +----+ Wed 24 Apr 2024 22:38:05 AEST https://www.youtube.com/c/onecircuit-as https://onecircuit.blogspot.com/ */ #include "../device.h" #include "../easy-pdk/calibrate.h" #include "../auto_sysclock.h" #include "../delay.h" #include "../device/pfs173.h" #include <stdbool.h> #define LED4_BIT 4 // PWM #define LED3_BIT 3 // Blinky #define LED0_BIT 0 // ADC #define turnLedOn() PA &= ~(1 << LED3_BIT) #define turnLedOff() PA |= (1 << LED3_BIT) bool ledon = true; // crude timing for blinky long mytimer = 45; volatile long counter = 0; volatile int PWM_MAX = 255; // will be set by ADC // check in here to see if LED is on or off void checkled() { counter = counter + 1; if (counter > mytimer) { counter = 0; if (ledon) { turnLedOff(); ledon = false; } else { turnLedOn(); ledon = true; } } } // Main program void main() { // Initialize hardware PAC |= (1 << LED4_BIT); PAC |= (1 << LED3_BIT); PAC |= (0 << LED0_BIT); // setup ADC PAPH |= (0 << LED0_BIT); PADIER |= (0 << LED0_BIT); ADCRGC = 0; // VDD is ref ADCC = ADCC_ADC_ENABLE | ADCC_CH_AD10_PA0; //enable ADC and use channel 10 (PA0) // setup PWM PWMGCUBL = PWM_MAX << 5; // Set the PWM upper bound (lower 3 bits) PWMGCUBH = PWM_MAX >> 3; // (upper 5 bits) PWMG1DTL = 0x00; // Clear the LED PWM duty value PWMG1DTH = 0x00; PWMGCLK = (uint8_t)(PWMGCLK_PWMG_ENABLE | PWMGCLK_CLK_IHRC); PWMG1C = (uint8_t)(PWMG1C_INVERT_OUT | PWMG1C_OUT_PWMG1 | PWMG1C_OUT_PA4); ledon = false; // Main loop while (1) { ADCC |= ADCC_ADC_CONV_START; //start ADC conversion while( !(ADCC & ADCC_ADC_CONV_COMPLETE) ); PWM_MAX = ADCR; //read the ADC value PWMG1DTL = PWM_MAX << 5; // Set the LED PWM duty value (lower 3 bits) PWMG1DTH = PWM_MAX >> 3; // (upper 8 bits) _delay_ms(20); // little delay for checkled checkled(); } } // Startup code - Setup/calibrate system clock unsigned char _sdcc_external_startup(void) { AUTO_INIT_SYSCLOCK(); AUTO_CALIBRATE_SYSCLOCK(TARGET_VDD_MV); return 0; // Return 0 to inform SDCC to continue with normal initialization. }
Both of these are in a folder as "main.c" with a Makefile that looks like this:
DEVICE = PFS173 F_CPU = 100000 TARGET_VDD_MV = 5000 TARGET_VDD = 5.0 # --------------------------------------------------------------------- OUTPUT_NAME = BlinkLED_$(DEVICE) include ../arch-from-device.mk ROOT_DIR = .. BUILD_DIR = .build OUTPUT_DIR = .output OUTPUT = $(OUTPUT_DIR)/$(OUTPUT_NAME) SOURCES = main.c OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.rel,$(SOURCES)) # http://sdcc.sourceforge.net/doc/sdccman.pdf COMPILE = sdcc -m$(ARCH) -c --std-sdcc11 --opt-code-size -D$(DEVICE) -DF_CPU=$(F_CPU) -DTARGET_VDD_MV=$(TARGET_VDD_MV) -I. -I$(ROOT_DIR)/include LINK = sdcc -m$(ARCH) EASYPDKPROG = easypdkprog # symbolic targets: all: size print-%: ; @echo $* = $($*) $(BUILD_DIR)/%.rel: %.c @mkdir -p $(dir $@) $(COMPILE) -o $@ $< $(OUTPUT).ihx: $(OBJECTS) @mkdir -p $(dir $(OUTPUT)) $(LINK) --out-fmt-ihx -o $(OUTPUT).ihx $(OBJECTS) $(OUTPUT).bin: $(OUTPUT).ihx makebin -p $(OUTPUT).ihx $(OUTPUT).bin build: $(OUTPUT).bin size: build @echo '---------- Segments ----------' @egrep '(ABS,CON)|(REL,CON)' $(OUTPUT).map | gawk --non-decimal-data '{dec = sprintf("%d","0x" $$2); print dec " " $$0}' | /usr/bin/sort -n -k1 | cut -f2- -d' ' @echo '------------------------------' @stat -L --printf "Size of $(OUTPUT_NAME).bin: %s bytes\n" $(OUTPUT).bin program: size $(EASYPDKPROG) -n $(DEVICE) write $(OUTPUT).ihx run: $(EASYPDKPROG) -r $(TARGET_VDD) start clean: rm -r -f $(BUILD_DIR) $(OUTPUT_DIR)
Finally, here is all the blinky, PWM and ADC action that you could possible need as SSOP8 wars comes to a bench near you.
No comments:
Post a Comment