DIRECT PORT MANIPULATION help #474
-
Hey everyone. I have a small setup where I have 2-2x7 segment displays (Common Anode configuration). The pins are connected to ATTINY1607 as such:
Now I'm trying to write a code, something like, with functions, as below, running from
I hope it is understandable what I'm trying to do here. 😶 Here's the full code I wrote so far (after reading directPortManipulation ):
I see an unexpected behaviour. Any good way to write a proper port manipulation method for this task? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 2 replies
-
You only have a delay after showing your last number, so it is showing the first 3 numbers for 0,00000001 second and then the fourth number |
Beta Was this translation helpful? Give feedback.
-
As per other response, the other digits are not switched on long enough to be visible. A 'scope should see the small on signal they are getting. I've historically used display drivers, but for a variety of reasons have explored alternatives recently. There are some readily-found libraries that will save you time and they mostly require you to "refresh" the display often enough for the user to see. One example here - have a read through to understand this strategy: https://github.com/DeanIsMe/SevSeg/blob/master/SevSeg.cpp |
Beta Was this translation helpful? Give feedback.
-
Others have covered the issue - the time between when you set the segments and when clear them for the next digit is ... 3 clock cycles (LDI followed by STS. |
Beta Was this translation helpful? Give feedback.
-
byte c = 0; //byte (uint8_t ) not int, int takes two instructions to act on instead of 1, even though you're eventually assigning it to an 8-bit register that will truncate it to 8 bits. uint16_t period = 100; //1 us is not going to happen, that's 20 instructions :-P 32 if you overclock that thing as hard as you can without busting out the dry ice & acetone bath or liquid nitrogen. So use something sane. Never do this, it's silly and inefficient:
That generates: Really never do something like THIS:
Not only is it the same slow read-modify-write sequence, they also turn off every bit in the port instead of the ones you asked for because the OUT____ registers read the same value as the OUT register!!! Assuming that you make a point of initializing the top half of PORTB to what it is when c = 0 (if you increment c first) or when c = 3 (if you increment c after) this is likely fastest:
Load or assignment of VPORTs is 1 clock, not 3 or 2 respectively, as is |= when all bits except one are 0, and this value is known at compile time and constant and the inverse, &= when all but one bit is a 1 - those two end up as the atomic sbi (set bit index) and cbi (clear bit index) when used on one of 32 registers in the low IO space (which on modern AVRs is just the VPORTs and the 4 GPRs - on classic AVRs they would sometimes throw other registers in there because they thought they were useful - but it was also an unpredictable mess, and didn't jive with the systematic consistentency and coherent register structure they were going for here. It looked like they chose register locations with a blindfold and a dart board sometimes. The low and high IO space (consisting of the first 64 registers) can be loaded from and stored to in a single clock with a single word instruction (IN or OUT instead of LDS or STS). The classic AVRs also used those for "useful" registers, even though they couldn't fit all the special function registers in there. I think modern AVRs only use 4 or 5 of the 32 high IO addresses; It would have been nice if they had mirrored some frequently accessed registers in the unused ones like how the VPORTs are done; my list would have included either the data registers for the USART/SPI/TWI interfaces, or timer/counter CNT registers. I'm sure there were discussions about that and they decided against it for reasons that even if I don't like, I would recognize as reasonable - it might simply be that everything for which that would be worth doing was already complicated enough) Anyway - I would be tempted to put TCB0 into periodic interrupt mode and put the update of the digits into the ISR;If you don't have some higher priority use for TCB0 that's definitely the way to go I think (TCB1 is used for millis() timekeeping, and TCA gets used for any PWM) |
Beta Was this translation helpful? Give feedback.
byte c = 0; //byte (uint8_t ) not int, int takes two instructions to act on instead of 1, even though you're eventually assigning it to an 8-bit register that will truncate it to 8 bits.
uint16_t period = 100;
//1 us is not going to happen, that's 20 instructions :-P 32 if you overclock that thing as hard as you can without busting out the dry ice & acetone bath or liquid nitrogen. So use something sane.
Never do this, it's silly and inefficient:
That generates:
LDS (3 clock cycles) to load the value into a working register (let's say r0, because that's a perfectly reasonable one for it to pick)
OR/AND what it just read with the anything.
…