SAMC21 Mapping a value across PORT bits - Page 1
Forum section: electronics / general

author: NivagSwerdna
author_rank: Super Contributor
timestamp: March 03, 2023, 09:15:54 pm
content: Following on from my other thread regarding mapping PORT bits to a value given that I got many interesting suggestions I thought I might share the other half of the puzzle...So sticking with my data bits for now...Code:******************** Data ********************31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0PA - - - - - - - - - - - - - - - - - - - - - - - D3 - - D5 D6 D0 D7 D2 -PB - - - - - - - - - - - - - - - - - - - - - D4 - - - - - D1 - - - -I need to map from a uint8_t to the various bits of PORTA and PORTB...I do need to do this fast as I have tight timing constraints and my ATSAMC21 is only running at 48MHz...Here's my attempt...Code:void setData(uint8_t data) {PORT_REGS->GROUP.PORT_OUTSET = D_PA_lookup;PORT_REGS->GROUP.PORT_OUTCLR = (~D_PA_lookup) & D_PA_mask_bits;PORT_REGS->GROUP.PORT_OUTSET = D_PB_lookup;PORT_REGS->GROUP.PORT_OUTCLR = (~D_PB_lookup) & D_PB_mask_bits;}whereCode:#define D_PA_mask_bits 0x0000013e#define D_PA_mask_other (D_PA_mask_bits ^ 0xffffffff)#define D_PB_mask_bits 0x00000410#define D_PB_mask_other (D_PB_mask_bits ^ 0xffffffff)const uint32_t D_PA_lookup[] = {0x00000000, 0x00000008, 0x00000000, 0x00000008, 0x00000002, 0x0000000a, 0x00000002, 0x0000000a, 0x00000100, 0x00000108, 0x00000100, 0x00000108, 0x00000102, 0x0000010a, 0x00000102, 0x0000010a,0x00000000, 0x00000008, 0x00000000, 0x00000008, 0x00000002, 0x0000000a, 0x00000002, 0x0000000a, 0x00000100, 0x00000108, 0x00000100, 0x00000108, 0x00000102, 0x0000010a, 0x00000102, 0x0000010a,0x00000020, 0x00000028, 0x00000020, 0x00000028, 0x00000022, 0x0000002a, 0x00000022, 0x0000002a, 0x00000120, 0x00000128, 0x00000120, 0x00000128, 0x00000122, 0x0000012a, 0x00000122, 0x0000012a,0x00000020, 0x00000028, 0x00000020, 0x00000028, 0x00000022, 0x0000002a, 0x00000022, 0x0000002a, 0x00000120, 0x00000128, 0x00000120, 0x00000128, 0x00000122, 0x0000012a, 0x00000122, 0x0000012a,0x00000010, 0x00000018, 0x00000010, 0x00000018, 0x00000012, 0x0000001a, 0x00000012, 0x0000001a, 0x00000110, 0x00000118, 0x00000110, 0x00000118, 0x00000112, 0x0000011a, 0x00000112, 0x0000011a,0x00000010, 0x00000018, 0x00000010, 0x00000018, 0x00000012, 0x0000001a, 0x00000012, 0x0000001a, 0x00000110, 0x00000118, 0x00000110, 0x00000118, 0x00000112, 0x0000011a, 0x00000112, 0x0000011a,0x00000030, 0x00000038, 0x00000030, 0x00000038, 0x00000032, 0x0000003a, 0x00000032, 0x0000003a, 0x00000130, 0x00000138, 0x00000130, 0x00000138, 0x00000132, 0x0000013a, 0x00000132, 0x0000013a,0x00000030, 0x00000038, 0x00000030, 0x00000038, 0x00000032, 0x0000003a, 0x00000032, 0x0000003a, 0x00000130, 0x00000138, 0x00000130, 0x00000138, 0x00000132, 0x0000013a, 0x00000132, 0x0000013a,0x00000004, 0x0000000c, 0x00000004, 0x0000000c, 0x00000006, 0x0000000e, 0x00000006, 0x0000000e, 0x00000104, 0x0000010c, 0x00000104, 0x0000010c, 0x00000106, 0x0000010e, 0x00000106, 0x0000010e,0x00000004, 0x0000000c, 0x00000004, 0x0000000c, 0x00000006, 0x0000000e, 0x00000006, 0x0000000e, 0x00000104, 0x0000010c, 0x00000104, 0x0000010c, 0x00000106, 0x0000010e, 0x00000106, 0x0000010e,0x00000024, 0x0000002c, 0x00000024, 0x0000002c, 0x00000026, 0x0000002e, 0x00000026, 0x0000002e, 0x00000124, 0x0000012c, 0x00000124, 0x0000012c, 0x00000126, 0x0000012e, 0x00000126, 0x0000012e,0x00000024, 0x0000002c, 0x00000024, 0x0000002c, 0x00000026, 0x0000002e, 0x00000026, 0x0000002e, 0x00000124, 0x0000012c, 0x00000124, 0x0000012c, 0x00000126, 0x0000012e, 0x00000126, 0x0000012e,0x00000014, 0x0000001c, 0x00000014, 0x0000001c, 0x00000016, 0x0000001e, 0x00000016, 0x0000001e, 0x00000114, 0x0000011c, 0x00000114, 0x0000011c, 0x00000116, 0x0000011e, 0x00000116, 0x0000011e,0x00000014, 0x0000001c, 0x00000014, 0x0000001c, 0x00000016, 0x0000001e, 0x00000016, 0x0000001e, 0x00000114, 0x0000011c, 0x00000114, 0x0000011c, 0x00000116, 0x0000011e, 0x00000116, 0x0000011e,0x00000034, 0x0000003c, 0x00000034, 0x0000003c, 0x00000036, 0x0000003e, 0x00000036, 0x0000003e, 0x00000134, 0x0000013c, 0x00000134, 0x0000013c, 0x00000136, 0x0000013e, 0x00000136, 0x0000013e,0x00000034, 0x0000003c, 0x00000034, 0x0000003c, 0x00000036, 0x0000003e, 0x00000036, 0x0000003e, 0x00000134, 0x0000013c, 0x00000134, 0x0000013c, 0x00000136, 0x0000013e, 0x00000136, 0x0000013e,};const uint32_t D_PB_lookup[] = {0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000000, 0x00000000, 0x00000010, 0x00000010,0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410, 0x00000400, 0x00000400, 0x00000410, 0x00000410,};For address, I have 16 bits but consider them as 2 8-bit values for lookup... so have to do the arrangement above twice.Reasonable?(UPDATED: Fixed use of complement)

author: ataradov
author_rank: Super Contributor
timestamp: March 03, 2023, 10:16:18 pm
content: You can do one write to OUT register. Keep the full value in a variable (except the mapped bits), so you don't have to read it out every time. Then just OR with the table lookup and just write the resulting value.All port writes that do not belong to the mapped group must also update the variable.You can also use IOBUS mapping of the PORT, which would do the wire in one cycle instead of 3.

author: _pat_
author_rank: Contributor
timestamp: March 04, 2023, 12:49:29 am
content: Following on from Alex' suggestion, you could keep a copy of the last known state of the bits you know about. You could then EOR your new value with the known value to figure out which bits have changed, then you could write that to the OUTTGL register. That would obviate the need to keep track of other bits elsewhere in the code.Code:void setData(uint8_t ui8Data) {// Keep track of old valuesstatic struct {uint32_t ui32OldA; // Last A LUT lookupuint32_t ui32OldB; // Last B LUT lookup} sOld;uint32_t ui32New;PORT_REGS->GROUP.PORT_OUTTGL = sOld.ui32OldA ^ (ui32New = D_PA_lookup); sOld.ui32OldA = ui32New;PORT_REGS->GROUP.PORT_OUTTGL = sOld.ui32OldB ^ (ui32New = D_PB_lookup); sOld.ui32OldB = ui32New;}You can also save some flash by not storing 32 bits for each LUT entry - highest bit you're toggling is PB10, so these could be uint16_t tables. For the price of a shift per LUT you could drop them down to uint8_t tables.

author: NorthGuy
author_rank: Super Contributor
timestamp: March 04, 2023, 02:13:54 am
content: I would also store both PA and PB in a single 32-bit number. It'll work faster and will take less memory.Code:static uint32_t ui32Old;uint32_t ui32New;uint32_t ui32Tgl;ui32New = D_lookup;ui32Tgl = ui32New^ ui32Old;ui32Old = ui32New;PORT_REGS->GROUP.PORT_OUTTGL = ui32Tgl & 0xffff;PORT_REGS->GROUP.PORT_OUTTGL = ui32Tgl >> 16;

author: cv007
author_rank: Frequent Contributor
timestamp: March 04, 2023, 04:51:31 am
content: Quote! is not, ~ is complement.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: March 04, 2023, 09:19:54 am
content: Interesting. I had wondered why PORTTGL existed.I'm not sure what IOBUS mapping refers to... I will look that up.I quite like the partial operations SET, CLR, TGL as it means that I don't have to keep a complete image of all my bits in one place given I have quite a few signals.Updated the original post for complement operator! Thanks for spotting that!Thanks all.

author: ataradov
author_rank: Super Contributor
timestamp: March 04, 2023, 05:28:39 pm
content: Quote from: NivagSwerdna on March 04, 2023, 09:19:54 amSearch for "IOBUS" in the datasheet. This is basically the same PORT structure mapped at address 0x60000000 instead of a regular 0x41000000. This version is guaranteed to take one cycle, but you only have access to DIR, OUT, IN registers. There are no set/clr/tgl registers.Quote from: NivagSwerdna on March 04, 2023, 09:19:54 amWell, it is up to you to balance how the code looks and performance. If you are going for the maximum performance, then having the variable and IOBUS mapped version is the best approach.

author: cv007
author_rank: Frequent Contributor
timestamp: March 04, 2023, 07:54:58 pm
content: QuoteThe documentation doesn't make it very clear, but I understood that it included the set/clr/tgl registers as the paragraph below eludes to (why would they mention set/clear and especially toggled if no access to these registers on the iobus)-Quote

author: ataradov
author_rank: Super Contributor
timestamp: March 04, 2023, 07:59:23 pm
content: I remember I tested this some time ago, but I don't remember the results. Toggling might actually work. It is worth trying, since IOBUS access is demonstrably faster.For IN reads you do need to enable continuous sampling.Edit: I just tried again and yes, all functions seem to work. Except for PINCFG updates. Writes work fine, but it appears that RMW operations fail, I guess read always returns 0. Anyway, just configure things though the normal address.

author: cv007
author_rank: Frequent Contributor
timestamp: March 04, 2023, 08:24:28 pm
content: QuoteFor a samD10, I have always used the io bus for everything, including configuration (and enable continuous sampling for pins that are inputs). Works for a samD10 anyway.

author: ataradov
author_rank: Super Contributor
timestamp: March 04, 2023, 08:32:10 pm
content: The writes to PINCFG work fine here too. But my default code used |= to enable the pull-up and enable the input buffer in two separate operations, and that did not work. There might be some thing obvious, but I don't have time to investigate that right now.