Jau ne pirmą kartą randu svetimam softe vieną dalykėlį. Įdedu čia du fragmentus- originalų ir mano rašyta:
Originalus:
for(int8_t bit = 7; bit >= 0; bit--)
{
L(PORTC, SS1306_OLED_CLK);
if((1 < < bit) & data) { H(PORTC, SS1306_OLED_DAT); } else { L(PORTC, SS1306_OLED_DAT); } H(PORTC, SS1306_OLED_CLK); }
Mano:
for(unsigned char bit=0;bit<8;bit++)
{
L(PORTC, SS1306_OLED_CLK);
if(data & 0x80) {H(PORTC,SS1306_OLED_DAT);} else { L(PORTC, SS1306_OLED_DAT); }
data = data < < 1; H(PORTC, SS1306_OLED_CLK); }
H() ir L() procedūros tai ne mano rašytos ir jos neturi įtakos. Pagrindinis skirtumas yra pakeisti “(1<<bit)” į “(data=data1<<1)”. Atrodo paprastas pakeitimas, bet pagalvokim koks bereikalingas darbas stumti tuos bitus per visą ciklą… 7+6+5+4+3+2+1 bitų pastumimai originaliam softe ir 7 pastumimai mano softe. Skirtumas gerai matosi oscilografo ekrane…
Grubiai šnekant, su tuo pačiu MCU greitis padidėjo nuo 22μs iki 9μs vienam perduotam baitui, du su biškiu karto (ir 16 baitų mažesnis softas). O čia gi grafinis ekraniukas!
Ir kaip sakiau, jau ne pirmas kartas randu šitą klaidelę svetimam softe, kai daroma duomenu serializacija programiškai.
Aišku vienas loginis niuansas, mano algoritmas sunaikina kintamąjį data. Tačiau jį galima ir atsiminti, bet dažniausiai jis jau nereikalingas.
Kodėl tokios klaidos? Manau todėl, kad mąstoma šabloniškai- tikrinam duomenų bitus su “maske” ir pagal rezultatą išsiunčiam. Čia labai žmogiška, tačiau reikia galvoti plačiau- kam generuoti “maskę”, jei galima taip pat stumdyti pačius duomenis. O jei tai būtų ciklinis stumimas ROL/ROR, tai netgi duomenys nesusigadintu- galima “apsukti” visą baitą ir vėl viskas bus kaip pradžioje.
[dar galimi kiti variantai, kai “maskę” galima irgi “sukti” ir ciklo sąlygas tikrinti pagal tai. Kodas gausis dar geresnis. Laukiam skaitytojų versijų.]
Sitaip dar greiciau turetu veikti:
uint8_t mask = 0x80;
do {
L(PORTC, SS1306_OLED_CLK);
if (data & mask) H(PORTC, SS1306_OLED_DAT);
else L(PORTC, SS1306_OLED_DAT);
mask = mask >> 1;
H(PORTC, SS1306_OLED_CLK);
} while (mask != 0);
Čia bėda su elementariom žiniom, kaip veikia bitukų pastūmimo operacija ir kad jos našumas yra ne O(1), o O(n).
Praėjo tie laikai, kai tikri vyrai rašydavosi device draiverius patys. O liūdniausia, kad net ir „geri“ gamintojų pavyzdžiai va su tokiais kreivais bitshiftinimais pateikiami…
Algio variantas elegantiškesnis, bet…
Kodėl taip gavosi, reikia klausti gcc… 🙂
Ir kodas palyginus su mano pailgėjo 8 baitais…
Laukiam kitų variantų, netgi hardkoro (primenu tai AVR, asembleris welkomė).
Savel, o kokie LST gabaliukai gaunasi kiekvienu atveju, ir kuri gcc versija, kad taip nekokybiskai kompiliuoja?
gcc: vr-gcc (GCC) 4.8.2 20131010
Mano:
Algio:
Ir kam tam c++ kompileriui do {} while cikle prireike paslepto dar vieno int16_t kintamojo?
Nors tavo atveju didelis + kompileriui buvo fiksuoto data bito tikrinimas, kai mano atveju jam teko daryti bereikalinga kopijavima.
Palyginimui hardwarinis variantas su tuom pačiu MCU (@12MHz):
O jeigu šitaip:
for (unsigned char bit = 0; bit < 8; bit++) {
PORTC &= ~(1<<SS1306_OLED_CLK); // clear
PORTC ^= (-(data & 0x80) ^ PORTC) & (1 << SS1306_OLED_DAT); // set n-th bit to (data & 0x80)
PORTC |= (1<<SS1306_OLED_CLK); // ready
data <<= 1;
}
Korekcija ankstesniam kodui (praleistas r-shift 7):
for (unsigned char bit = 0; bit < 8; bit++) {
PORTC &= ~(1<<SS1306_OLED_CLK);
PORTC ^= (-((data & 0x80) >> 7) ^ PORTC) & (1 << SS1306_OLED_DAT);
PORTC |= (1<<SS1306_OLED_CLK);
data <<= 1;
}
Už HW variantą ne greičiau, tačiau atsisakom if-branch’o.
Kadangi biški pasikeitė kitos softo dalys (išmečiau bereikalingus pradinius nustatymus ir bereikalingus perjungimas, kad ir priverstini CLK LOW), tai perdarau paveiksliukus. Ir dar geriau įžeminau, todėl vaizdelis geresnis. Ir kad tikslesnis matavimas, tai matuojam nuo pirmo LH iki paskutinio HL.
hardwarė. +0 baitų.
mano. +50 baitų.
Tomo. +58 baitai.
Tomui:
nelabai gerai naudoti taip, nes portui daromas read modify write, ir jei ten prikabinta kokia hw funkcija, gali nutikti nenuspėjami dalykai.
Aš kiek suprantu, gcc sukompiliuoja taip, kad nenaudojama procedūra read-modify-write, o naudojama asemblerio komanda SBI (Sets a specified bit in an I/O register.). Čia kaip ir “tradicinis palikimas” suderinamumui su kitais procesoriais, niekur nedaroma PORTn |= x baito lygyje.
XOR be read-modify-write nepadarysi
Dar vienas pastebejimas. Tos pauzės su poslinkiu labai aktualu AVR tipo procesoriams. ARM procesorius turi hardwarinį slinktuvą ir bet koks bitų poslinkis atliekamas per vieną taktą. Kiek pamenu Z80 ir 6502 to nemoka. Kitų procesorių tai giliai nekapsčiau.