Skip to content

Commit 6aa1baf

Browse files
committed
Merge branch 'fix4': optimize pinMode()
add an experimental version of pinMode() and a minimal test environment to compare the size of the compiled C version and a hand crafted assembler version. The C version compiles to 270 bytes and uses 8 bytes on the stack while the assembler version is only 147 bytes and uses 1 byte on the stack. The resulting function is not yet integrated into the regular Arduino core files.
2 parents ddecdd5 + 4fd88ad commit 6aa1baf

7 files changed

Lines changed: 460 additions & 0 deletions

File tree

docs/api/migration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ names. The preprocessor does not allow for variable macro names. That means
9696
`_Generic` would work with fixed name like `Serial`, but it wouldn't work
9797
for `SoftwareSerial` with no standard instance name.
9898
99+
[This](https://mort.coffee/home/obscure-c-features/) is a good introduction
100+
into the use of `_Generic`.
99101
100102
101103

test/pinmode/Makefile

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
BASENAME=$(shell basename $$(pwd))
2+
EXECUTEABLE=$(BASENAME).ihx
3+
4+
#SDCCBASE=/usr/local
5+
SDCCBASE=/opt/sdcc
6+
BINDIR=$(SDCCBASE)/bin
7+
CC=$(BINDIR)/sdcc
8+
LD=$(BINDIR)/sdld
9+
10+
SDBASE=../../sduino/hardware/sduino/stm8
11+
LIBBASE=$(SDBASE)/STM8S_StdPeriph_Driver
12+
SDUINO=$(SDBASE)/cores/sduino
13+
14+
CPPFLAGS=-DF_CPU=16000000L -DSTM8S103 -Ddouble=float \
15+
-I. -I$(SDUINO) -I$(SDBASE)/variants/standard \
16+
-I$(LIBBASE)/inc -I$(LIBBASE)/src \
17+
-I$(SDCCBASE)/share/sdcc/include/
18+
CFLAGS= -mstm8 #--debug
19+
LDFLAGS=-L$(LIBBASE)/lib -L$(SDCCBASE)/share/sdcc/lib/stm8 -lSTM8S103
20+
21+
SRCS=pinmode-c.c pinmode-asm.c main.c
22+
23+
BUILDDIR=build
24+
OBJECTS=$(SRCS:%.c=$(BUILDDIR)/%.rel)
25+
26+
.PHONY: all clean flash statistics sim
27+
28+
all: $(EXECUTEABLE) statistics sim
29+
30+
$(EXECUTEABLE): $(OBJECTS)
31+
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
32+
33+
$(OBJECTS) : $(BUILDDIR)/%.rel : %.c
34+
mkdir -p $(BUILDDIR)
35+
$(CC) -c $(CPPFLAGS) $(CFLAGS) $^ -o $@
36+
37+
flash: $(EXECUTABLE)
38+
stm8flash -cstlinkv2 -pstm8s103?3 -w $^
39+
40+
readopt:
41+
stm8flash -c stlinkv2 -p stm8s103?3 -s opt -r opt.bin
42+
43+
44+
# lists the length of functions and constants defined in the primary source
45+
# file
46+
statistics: $(OBJECTS)
47+
cd $(BUILDDIR); awk '/ CODE/ {print FILENAME "\t" strtonum("0x"$$4)}' *.sym
48+
49+
sim: $(EXECUTEABLE)
50+
@. ./sim
51+
52+
# @. ./sim | grep "0x0001 00 00 00 00 00 00"
53+
# . ./sim | grep "0x0001 00 00 00 00 00 00" || echo "fail"
54+
55+
56+
# grep "[0-9] _" $(BASENAME).rst |\
57+
# awk 'BEGIN {print "length\tfunction\n------\t----------";}{ a=strtonum("0x"$$1);if (name) print a-s "\t" name; s=a; name=$$3}'
58+
59+
60+
clean:
61+
rm -rf *.lib *.rst *.rel *.lst *.ihx *.sym *.asm *.lk *.map \
62+
*.cdb *.adb *~ *.bak build
63+
rm -f $(EXECUTABLE)

test/pinmode/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
original version (276 bytes)
2+
0080A0 47 _pinMode1:
3+
0081B4 268 _setup:
4+
0081BE 282 _loop:
5+
after first commit (176 bytes):
6+
0080A0 48 _pinMode1:
7+
0081B4 269 _pinMode_asm:
8+
008264 410 _setup:
9+
00826E 424 _loop:
10+
11+
Reihenfolgen:
12+
INPUT |-CR2, -CR1, -DDR
13+
INPUT_PULLUP |-CR2, -DDR, +CR1
14+
OUTPUT_FAST |+CR1, +DDR, +CR2
15+
OUTPUT_OD_FAST |-CR1, +DDR, +CR2
16+
OUTPUT_OD |-CR1, -CR2, +DDR
17+
OUTPUT |+CR1, -CR2, +DDR
18+
19+
mode CR2
20+
21+
22+
sstm8 -tS003 -S uart=1,port=10000 mini.ihx
23+
24+
25+
CR1: pullup bzw. open-drain
26+
27+
CR2: in: INT, out: fast
28+
- input: als erstes reset
29+
- output: als letztes setzen (nach DDR)
30+
31+
32+
Schritt 1: CR2 (in: INT, out: fast)
33+
- set: OUTPUT_FAST, OUTPUT_OD_FAST
34+
- reset: sonst
35+
36+
37+
## Simulator
38+
39+
-i m: info memory

test/pinmode/main.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Test the optimized Versions of pinMode
3+
*
4+
* After each test the content of the GPIO registers is checked against
5+
* the expected values. The table results[] contains the number of mismatched
6+
* bytes after each test.
7+
*
8+
* Expected result: all bytes in results[] == 0x00
9+
*/
10+
11+
#define ARDUINO_MAIN
12+
#include "Arduino.h"
13+
#include "stm8s_it.h"
14+
15+
void pinMode_c(uint8_t pin, uint8_t mode);
16+
void pinMode_asm(uint8_t pin, uint8_t mode);
17+
18+
// select which function should be used for the test
19+
#define pinMode_test pinMode_asm
20+
21+
uint8_t results[6];
22+
23+
/**
24+
* checks a memory area for an expected content
25+
*
26+
* @returns: number of mismatched bytes (0=ok)
27+
*/
28+
uint8_t checkresult(uint16_t adr, uint8_t *data)
29+
{
30+
uint8_t i,err;
31+
32+
err = 0;
33+
for (i=0; i<3; ++i) {
34+
if (((uint8_t*)adr)[2+i] != data[i]) err++;
35+
}
36+
return err;
37+
}
38+
39+
/**
40+
* fills a memory area with zeros
41+
*
42+
* The simulator does not clear the memory and does not properly
43+
* initialize the IO registers after reset. Use this instead.
44+
*/
45+
void _memset(unsigned char *adr, unsigned int cnt)
46+
{
47+
while (cnt--) *adr++=0;
48+
}
49+
50+
void main(void)
51+
{
52+
unsigned char *r = results;
53+
54+
_memset((unsigned char *)GPIOA_BaseAddress, 20);
55+
56+
// expected result: xx xx 00 00 00
57+
pinMode_test(PA1,INPUT);
58+
*r++ = checkresult(GPIOA_BaseAddress, "\x00\x00\x00");
59+
60+
// expected result: xx xx 20 20 00
61+
pinMode_test(PB5,OUTPUT);
62+
*r++ = checkresult(GPIOB_BaseAddress, "\x20\x20\x00");
63+
64+
// expected result: xx xx 00 10 00
65+
pinMode_test(PC4,INPUT_PULLUP);
66+
*r++ = checkresult(GPIOC_BaseAddress, "\x00\x10\x00");
67+
68+
// expected result: xx xx 20 10 00
69+
pinMode_test(PC5,OUTPUT_OD);
70+
*r++ = checkresult(GPIOC_BaseAddress, "\x20\x10\x00");
71+
72+
// expected result: xx xx 02 02 02
73+
pinMode_test(PD1,OUTPUT_FAST);
74+
*r++ = checkresult(GPIOD_BaseAddress, "\x02\x02\x02");
75+
76+
// expected result: xx xx 42 02 42
77+
pinMode_test(PD6,OUTPUT_OD_FAST);
78+
*r++ = checkresult(GPIOD_BaseAddress, "\x42\x02\x42");
79+
80+
for(;;); // endless loop
81+
}
82+
83+
/*
84+
* empty default IRQ functions to make the linker happy if the
85+
* respective module is not to linked.
86+
*/
87+
88+
void UART1_TX_IRQHandler(void) __interrupt(ITC_IRQ_UART1_TX){}
89+
void UART1_RX_IRQHandler(void) __interrupt(ITC_IRQ_UART1_RX){}
90+
91+
// define as __naked to avoid the "clr a/dix x,a" prolog
92+
#define IMPLEMENT_ISR(vect, interrupt) \
93+
void vect(void) __interrupt((interrupt)>>8) __naked { \
94+
__asm__("iret"); \
95+
}
96+
97+
IMPLEMENT_ISR(EXTI_PORTA_IRQHandler, INT_PORTA) /* EXTI PORTA */
98+
IMPLEMENT_ISR(EXTI_PORTB_IRQHandler, INT_PORTB) /* EXTI PORTB */
99+
IMPLEMENT_ISR(EXTI_PORTC_IRQHandler, INT_PORTC) /* EXTI PORTC */
100+
IMPLEMENT_ISR(EXTI_PORTD_IRQHandler, INT_PORTD) /* EXTI PORTD */
101+
IMPLEMENT_ISR(EXTI_PORTE_IRQHandler, INT_PORTE) /* EXTI PORTE */
102+
IMPLEMENT_ISR(TIM1_CAP_COM_IRQHandler, INT_TIM1_CAPCOM)
103+
//IMPLEMENT_ISR(TIM1_UPD_OVF_TRG_BRK_IRQHandler, INT_TIM1_OVF)
104+
//IMPLEMENT_ISR(TIM2_CAP_COM_IRQHandler, INT_TIM2_CAPCOM)
105+
//IMPLEMENT_ISR(TIM2_UPD_OVF_TRG_BRK_IRQHandler, INT_TIM2_OVF)
106+
107+
void TIM4_UPD_OVF_IRQHandler(void) __interrupt(ITC_IRQ_TIM4_OVF){}

0 commit comments

Comments
 (0)