Skip to content

Commit f47cb37

Browse files
committed
Fixec #1158 correctly.
1 parent 1790529 commit f47cb37

File tree

1 file changed

+380
-1
lines changed

1 file changed

+380
-1
lines changed

megaavr/libraries/Wire/src/twi_pins.c

Lines changed: 380 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,4 +365,383 @@ bool TWI0_swap(uint8_t state) {
365365
}
366366
#endif
367367
return false;
368-
#endif
368+
}
369+
370+
371+
void TWI0_usePullups() {
372+
// make sure we don't get errata'ed: Clear the pins
373+
// and then turn on the pullups.
374+
#if defined(PORTMUX_TWIROUTEA) // Dx or Ex-series
375+
uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI0_gm;
376+
PORT_t *port;
377+
// PORTA and PORTC are present on all parts with a TWIROUTEA register
378+
if(portmux == PORTMUX_TWI0_ALT2_gc) {
379+
port = &PORTC;
380+
} else {
381+
port = &PORTA;
382+
}
383+
#if !defined(__AVR_DA__) && !defined(__AVR_DB__) //DD and EA, and presumably later parts, have an extra mux option.
384+
if (portmux == 3) {
385+
port->OUTCLR = 0x03; // bits 0 and 1
386+
port->PIN0CTRL |= PORT_PULLUPEN_bm;
387+
port->PIN1CTRL |= PORT_PULLUPEN_bm;
388+
} else
389+
#endif
390+
{
391+
port->OUTCLR = 0x0C; // bits 2 and 3
392+
port->PIN2CTRL |= PORT_PULLUPEN_bm;
393+
port->PIN3CTRL |= PORT_PULLUPEN_bm;
394+
}
395+
#if defined(TWI0_DUALCTRL) //Also handle slave pins, if enabled
396+
if (TWI0.DUALCTRL & TWI_ENABLE_bm) {
397+
#if !(defined(__AVR_DA__) || defined(__AVR_DB__))
398+
if (portmux == PORTMUX_TWI0_DEFAULT_gc ||
399+
portmux == PORTMUX_TWI0_ALT3_gc) {
400+
PORTC.OUTCLR = 0x0C;
401+
PORTC.PIN2CTRL |= PORT_PULLUPEN_bm;
402+
PORTC.PIN3CTRL |= PORT_PULLUPEN_bm;
403+
}
404+
#else
405+
if (portmux == PORTMUX_TWI0_DEFAULT_gc) {
406+
PORTC.OUTCLR = 0x0C; // bits 2 and 3
407+
PORTC.PIN2CTRL |= PORT_PULLUPEN_bm;
408+
PORTC.PIN3CTRL |= PORT_PULLUPEN_bm;
409+
} else {
410+
PORTC.OUTCLR = 0xC0; // bits 6 and 7
411+
PORTC.PIN6CTRL |= PORT_PULLUPEN_bm;
412+
PORTC.PIN7CTRL |= PORT_PULLUPEN_bm;
413+
}
414+
#endif
415+
}
416+
#endif
417+
#elif defined(PORTMUX_TWISPIROUTEA) // megaAVR 0-series
418+
uint8_t portmux = PORTMUX.TWISPIROUTEA & PORTMUX_TWI0_gm;
419+
PORT_t *port;
420+
// Note that all megaAVR 0-series parts have a PORTA, PORTC and PORTF!
421+
if(portmux == PORTMUX_TWI0_ALT2_gc) {
422+
port = &PORTC;
423+
} else {
424+
port = &PORTA;
425+
}
426+
port->OUTCLR = 0x0C; // bits 2 and 3
427+
port->PIN2CTRL |= PORT_PULLUPEN_bm;
428+
port->PIN3CTRL |= PORT_PULLUPEN_bm;
429+
#if defined(TWI0_DUALCTRL) //Also handle slave pins, if enabled
430+
if (TWI0.DUALCTRL & TWI_ENABLE_bm) {
431+
if (portmux == PORTMUX_TWI0_DEFAULT_gc) {
432+
port = &PORTC;
433+
} else {
434+
port = &PORTF;
435+
}
436+
port->OUTCLR = 0x0C; // bits 2 and 3
437+
port->PIN2CTRL |= PORT_PULLUPEN_bm;
438+
port->PIN3CTRL |= PORT_PULLUPEN_bm;
439+
}
440+
#endif
441+
#elif defined(MEGATINYCORE) /* tinyAVR 0/1-series */
442+
#if defined(PORTMUX_TWI0_bm) // 1-series with remappable TWI
443+
if (PORTMUX.CTRLB & PORTMUX_TWI0_bm) {
444+
PORTA.OUTCLR = 0x06;
445+
PORTA.PIN2CTRL |= PORT_PULLUPEN_bm;
446+
PORTA.PIN1CTRL |= PORT_PULLUPEN_bm;
447+
} else {
448+
PORTB.OUTCLR = 0x03; // bits 1 and 0.
449+
PORTB.PIN1CTRL |= PORT_PULLUPEN_bm;
450+
PORTB.PIN0CTRL |= PORT_PULLUPEN_bm;
451+
}
452+
#elif defined(__AVR_ATtinyxy2__) // 8-pin 0/1-series part
453+
PORTA.OUTCLR = 0x06; // bits 2 and 1.
454+
PORTA.PIN2CTRL |= PORT_PULLUPEN_bm;
455+
PORTA.PIN1CTRL |= PORT_PULLUPEN_bm;
456+
#else // 0-series or 2-series part with no remapping options
457+
PORTB.OUTCLR = 0x03; // bits 1 and 0.
458+
PORTB.PIN1CTRL |= PORT_PULLUPEN_bm;
459+
PORTB.PIN0CTRL |= PORT_PULLUPEN_bm;
460+
#endif
461+
#endif
462+
}
463+
464+
//Check if TWI0 Master pins have a HIGH level: Bit0 = SDA, Bit 1 = SCL
465+
uint8_t TWI0_checkPinLevel(void) {
466+
#if defined(PORTMUX_TWIROUTEA) /* Dx-series */
467+
uint8_t portmux = (PORTMUX.TWIROUTEA & PORTMUX_TWI0_gm);
468+
VPORT_t *vport;
469+
if (portmux == PORTMUX_TWI0_ALT2_gc) {
470+
vport = &VPORTC;
471+
} else {
472+
vport = &VPORTA;
473+
}
474+
475+
#if !defined(__AVR_DA__) && !defined(__AVR_DB__) //DD and EA, and presumably later parts, have an extra mux option.
476+
if (portmux == 3) {
477+
return (vport->IN & 0x03);
478+
} else
479+
#endif
480+
{
481+
return ((vport->IN & 0x0C) >> 2);
482+
}
483+
#elif defined(PORTMUX_TWISPIROUTEA)
484+
uint8_t portmux = (PORTMUX.TWISPIROUTEA & PORTMUX_TWI0_gm);
485+
VPORT_t *vport;
486+
if (portmux == PORTMUX_TWI0_ALT2_gc) {
487+
vport = &VPORTC;
488+
} else {
489+
vport = &VPORTA;
490+
}
491+
return ((vport->IN & 0x0C) >> 2);
492+
#elif defined(MEGATINYCORE) /* tinyAVR 0/1-series */
493+
#if defined(PORTMUX_TWI0_bm) // Has a pin multiplexer
494+
if (PORTMUX.CTRLB & PORTMUX_TWI0_bm) {
495+
return ((VPORTA.IN & 0x06) >> 1);
496+
} else {
497+
return (VPORTB.IN & 0x03);
498+
}
499+
#elif defined(__AVR_ATtinyxy2__) // No PORTMUX for 8-pin parts, they always use PA1/2
500+
return ((VPORTA.IN & 0x06) >> 1);
501+
#else //it uses PB0/1
502+
return (VPORTB.IN & 0x03);
503+
#endif
504+
#else
505+
#error "Only modern AVR parts are supported by this version of Wire: tinyAVR 0/1/2-series, megaAVR 0-series, AVR Dx-series or AVR Ex-series."
506+
return 0;
507+
#endif
508+
}
509+
510+
#if defined(TWI0_DUALCTRL) // full version for parts with dual mode and likely input level too
511+
uint8_t TWI0_setConfig(bool smbuslvl, bool longsetup, uint8_t sda_hold, bool smbuslvl_dual, uint8_t sda_hold_dual) {
512+
uint8_t cfg = TWI0.CTRLA & 0x03;
513+
sda_hold <<= 2; // get these into the right place in the byte
514+
515+
if (smbuslvl) {
516+
cfg |= 0x40;
517+
}
518+
if (longsetup) {
519+
cfg |= 0x10;
520+
}
521+
cfg |= sda_hold;
522+
TWI0.CTRLA = cfg;
523+
#if defined(TWI0_DUALCTRL)
524+
sda_hold_dual <<= 2;
525+
uint8_t cfg_dual = TWI0.DUALCTRL & 0x03;
526+
if (cfg_dual & 1) {
527+
cfg_dual |= sda_hold_dual;
528+
if (smbuslvl_dual) {
529+
cfg_dual |= 0x40;
530+
}
531+
TWI0.DUALCTRL = cfg_dual;
532+
} else if (sda_hold_dual || smbuslvl_dual) {
533+
return 0x04; // dual mode exists on this part, but is not enabled. This signifies a failure to follow documented startup order
534+
}
535+
#endif
536+
return 0; // return success - all other errors are checked for before this is called.
537+
}
538+
#else // very little to do here on tiny, do save flash with a smaller implementation.
539+
uint8_t TWI0_setConfig(bool longsetup, uint8_t sda_hold) {
540+
uint8_t cfg = TWI0.CTRLA & 0x03;
541+
if (longsetup) {
542+
cfg |= 0x10;
543+
}
544+
cfg |= sda_hold;
545+
TWI0.CTRLA = cfg;
546+
return 0; // return success - all other errors are checked for before this is called.
547+
}
548+
#endif
549+
550+
551+
#if defined(TWI1)
552+
void TWI1_ClearPins() {
553+
#if defined(PIN_WIRE1_SDA_PINSWAP_2) || defined(TWI1_DUALCTRL)
554+
uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI1_gm;
555+
#endif
556+
#if defined(PIN_WIRE1_SDA_PINSWAP_2)
557+
if (portmux == PORTMUX_TWI1_ALT2_gc) { // make sure we don't get errata'ed
558+
#if defined(PORTB)
559+
PORTB.OUTCLR = 0x0C; // bits 2 and 3
560+
#endif
561+
} else
562+
#endif
563+
{
564+
PORTF.OUTCLR = 0x0C; // bits 2 and 3
565+
}
566+
#if defined(TWI1_DUALCTRL)
567+
#if defined(PORTB)
568+
if (TWI1.DUALCTRL & TWI_ENABLE_bm) {
569+
if (portmux == PORTMUX_TWI1_DEFAULT_gc) {
570+
PORTB.OUTCLR = 0x0C; // bits 2 and 3
571+
} else {
572+
PORTB.OUTCLR = 0xC0; // bits 6 and 7
573+
}
574+
}
575+
#endif
576+
#endif
577+
(void) portmux; //this is grabbed early, but depending on which part and hence what is conditionally compiled, may not go into the code. It will produce spurious warnings without this line
578+
}
579+
580+
581+
bool TWI1_Pins(uint8_t sda_pin, uint8_t scl_pin) {
582+
#if (defined(PIN_WIRE1_SDA_PINSWAP_1) || defined(PIN_WIRE1_SDA_PINSWAP_2))
583+
// Danger: 'portmux' in this context means all the other settings in portmux, since we're replacing the PORTMUX setting for TWI1, and will bitwise-or with the _gc constants.
584+
// Elsewhere, 'portmux' refers to the setting for this peripheral only, and we compare it to PORTMUX_TWI1_xxx_gc
585+
uint8_t portmux = PORTMUX.TWIROUTEA & ~PORTMUX_TWI1_gm;
586+
#if defined(PIN_WIRE1_SDA_PINSWAP_2)
587+
if (sda_pin == PIN_WIRE1_SDA_PINSWAP_2 && scl_pin == PIN_WIRE1_SCL_PINSWAP_2) {
588+
// Use pin swap
589+
PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI1_ALT2_gc;
590+
return true;
591+
} else
592+
#endif
593+
#if defined(PIN_WIRE1_SDA_PINSWAP_1)
594+
if (sda_pin == PIN_WIRE1_SDA_PINSWAP_1 && scl_pin == PIN_WIRE1_SCL_PINSWAP_1) {
595+
PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI1_ALT1_gc;
596+
return true;
597+
} else
598+
#endif
599+
#if defined(PIN_WIRE1_SDA)
600+
if (sda_pin == PIN_WIRE1_SDA && scl_pin == PIN_WIRE1_SCL) {
601+
// Use default configuration
602+
PORTMUX.TWIROUTEA = portmux;
603+
return true;
604+
} else {
605+
// Assume default configuration
606+
PORTMUX.TWIROUTEA = portmux;
607+
return false;
608+
}
609+
#endif
610+
#else // No TWI1 pin options - why call this?
611+
if (__builtin_constant_p(sda_pin) && __builtin_constant_p(scl_pin)) {
612+
/* constant case - error if there's no swap and the swap attempt is known at compile time */
613+
if (sda_pin != PIN_WIRE1_SDA || scl_pin != PIN_WIRE1_SCL) {
614+
badCall("This part does not support alternate Wire1 pins, if Wire1.pins() is called, it must be passed the default pins");
615+
return false;
616+
} else {
617+
return true;
618+
}
619+
} else { /* Non-constant case */
620+
return (sda_pin == PIN_WIRE1_SDA && scl_pin == PIN_WIRE1_SCL);
621+
}
622+
#endif
623+
return false;
624+
}
625+
626+
627+
628+
bool TWI1_swap(uint8_t state) {
629+
// Danger: 'portmux' in this context means all the other settings in portmux, since we're replacing the PORTMUX setting for TWI1, and will bitwise-or with the _gc constants.
630+
// Elsewhere, 'portmux' refers to the setting for this peripheral only, and we compare it to PORTMUX_TWI1_xxx_gc
631+
uint8_t portmux = PORTMUX.TWIROUTEA & (~PORTMUX_TWI1_gm);
632+
#if defined(PIN_WIRE1_SDA_PINSWAP_2)
633+
if (state == 2) {
634+
// Use pin swap
635+
PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI1_ALT2_gc;
636+
return true;
637+
} else
638+
#endif
639+
#if defined(PIN_WIRE1_SDA_PINSWAP_1)
640+
if (state == 1) {
641+
// Use pin swap
642+
PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI1_ALT1_gc;
643+
return true;
644+
} else
645+
#endif
646+
#if defined(PIN_WIRE1_SDA)
647+
if (state == 0) {
648+
// Use default configuration
649+
PORTMUX.TWIROUTEA = portmux;
650+
return true;
651+
} else {
652+
// Assume default configuration
653+
PORTMUX.TWIROUTEA = portmux;
654+
return false;
655+
}
656+
#else
657+
{
658+
// Assume default configuration
659+
PORTMUX.TWIROUTEA = portmux | PORTMUX_TWI1_ALT2_gc;
660+
return false;
661+
}
662+
#endif
663+
return false;
664+
}
665+
666+
667+
void TWI1_usePullups() {
668+
#if defined(PIN_WIRE1_SDA_PINSWAP_2) || defined(TWI1_DUALCTRL)
669+
uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI1_gm;
670+
#endif
671+
PORT_t *port;
672+
#if defined(PORTB) //All parts with a TWI1 have a PORTF
673+
if (portmux == PORTMUX_TWI1_ALT2_gc) {
674+
port = &PORTB;
675+
} else {
676+
port = &PORTF;
677+
}
678+
#else
679+
port = &PORTF;
680+
#endif
681+
682+
port->OUTCLR = 0x0C; // bits 2 and 3
683+
port->PIN2CTRL |= PORT_PULLUPEN_bm;
684+
port->PIN3CTRL |= PORT_PULLUPEN_bm;
685+
686+
#if defined(TWI0_DUALCTRL) && defined(PORTB)
687+
if (TWI1.DUALCTRL & TWI_ENABLE_bm) {
688+
if (portmux == PORTMUX_TWI1_DEFAULT_gc) {
689+
PORTB.OUTCLR = 0x0C; // bits 2 and 3
690+
PORTB.PIN2CTRL |= PORT_PULLUPEN_bm;
691+
PORTB.PIN3CTRL |= PORT_PULLUPEN_bm;
692+
} else {
693+
PORTB.OUTCLR = 0xC0; // bits 6 and 7
694+
PORTB.PIN6CTRL |= PORT_PULLUPEN_bm;
695+
PORTB.PIN7CTRL |= PORT_PULLUPEN_bm;
696+
}
697+
}
698+
#endif
699+
(void) portmux; //this is grabbed early, but depending on which part and hence what is conditionally compiled, may not go into the code. It will produce spurious warnings without this line
700+
}
701+
702+
//Check if TWI1 Master pins have a HIGH level: Bit0 = SDA, Bit 1 = SCL
703+
uint8_t TWI1_checkPinLevel(void) {
704+
// we do it this way because when accessed using a pointer, it's no longer any faster to use VPORTx
705+
#if defined(PORTB) //All parts with a TWI1 have a PORTF
706+
uint8_t portmux = PORTMUX.TWIROUTEA & PORTMUX_TWI1_gm;
707+
if (portmux == PORTMUX_TWI1_ALT2_gc) {
708+
return ((VPORTB.IN & 0x0C) >> 2);
709+
} else {
710+
return ((VPORTF.IN & 0x0C) >> 2);
711+
}
712+
#else
713+
return ((VPORTF.IN & 0x0C) >> 2);
714+
#endif
715+
}
716+
717+
// All devices with TWI1 have dual mode and the most have the smbus levels; the exceptions are caught before this is called
718+
uint8_t TWI1_setConfig(bool smbuslvl, bool longsetup, uint8_t sda_hold, bool smbuslvl_dual, uint8_t sda_hold_dual) {
719+
uint8_t cfg = TWI1.CTRLA & 0x03;
720+
if (smbuslvl) {
721+
cfg |= 0x40;
722+
}
723+
if (longsetup) {
724+
cfg |= 0x10;
725+
}
726+
cfg |= sda_hold;
727+
TWI1.CTRLA = cfg;
728+
#if defined(TWI1_DUALCTRL)
729+
uint8_t cfg_dual = TWI1.DUALCTRL & 0x03;
730+
if (cfg_dual & 1) {
731+
cfg_dual |= sda_hold_dual;
732+
if (smbuslvl_dual) {
733+
cfg_dual = 0x40;
734+
}
735+
TWI1.DUALCTRL = cfg_dual;
736+
} else if (sda_hold_dual || smbuslvl_dual) {
737+
return 0x04;
738+
}
739+
#endif
740+
return 0;
741+
}
742+
743+
744+
745+
#endif /* defined(TWI1) */
746+
747+
#endif /* TWI_PINS_H */

0 commit comments

Comments
 (0)