Search |
 |
|
 |
| Author |
Message |
|
|
Posted: May 22, 2012 - 07:30 AM |
|

Joined: Jul 07, 2007
Posts: 55
|
|
OK. I'm done on this one. I've banged my head enough as it is and I know I'm overlooking something, I just can't figure out what. I want to end up using a 4x4 button matrix in my circuit.
My theory of operation is as follows:
The circuit is currently a 4x2 matrix fitted with 1n4001 or 1n4004 diodes (circuit diagram further down). The rows and diodes are tied to D4-7 respectively. The columns are tied to D0-3. On initialization and while at "rest", Port D is set to 0xFE and DDR D is set to 0x00. When a read is needed, pinD0 is set to output which pulls that pin LOW. A short NOP and the whole port is read, then masked off with 0xF0. Then Port D is set to 0XFD, D0 is set to input, and D1 is set to output and the reading and masking repeats into a different register. Because I have two halves of the port in two different registers. One is swapped, then the values are OR'ed together. Then the rest of the program does whatever with that value.
Every tutorial I've read states that this is mind bogglingly simple. It also uses Port B as the example. Unfortunately, I need Port B free for the SPI features. :\ According to the Atmel references, any port can safely be used so I see no obvious reason why I can't use Port D (not using JTAG).
In the two truth tables below, the column on the left is the switch state. 0 for "off", 1 for "on". Technically, the actual values are inverted so just uh... magically invert them if you can. The right column is the state of the LED's temporarily installed into Port B, strictly for testing. Again, these are inverted. Please note the 3rd line down is marked with an "X". This is because touching the bottom of the breadboard changes the state of the LED. I moved this circuit to a different section, ripped off the back to check for shorts, pulled and swapped the tracks, and poked at the circuit with an multimeter for about two hours trying to track that one down. Noisy circuit? If so, that's something I don't know how to solve on a breadboard.
Astute readers will notice that I'm only testing 1/2 of the 4x2 matrix. Specifically D0 and D1 are being toggled.
Code:
Button LED
- 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
2 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0
3 0 0 0 0 0 1 0 0 0 0 0 0 x 0 0 0
4 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
5 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0
6 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0
7 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
8 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Given that I'm really only testing half the circuit in code, even that should produce the following results:
Code:
Button LED
- 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0
2 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0
3 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
4 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
5 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0
6 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0
7 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
8 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Yeah, I didn't realize it until after writing this entire post that the SWAP kind of screws up the bit ordering. If I can solve the ghosting problems, fixing the bit ordering is going to be trivial.
Here is the simplified circuit breadboarded as follows:
I did not copy the circuit diagram I've designed (too complex) but rebuilt the circuit on the breadboard (twice over) using this diagram (and compared against the original several times). The switches are standard slide switches of the "leave it and forget it" type. This diagram also shows how the wiring is done on the breadboard itself. Every other connection was removed and any circuits (such as reset and indicator LED) are stock with the MT-DB-U4 Atmega32U4 board from Mattairtech.
Here is the ASM boiled down to the barebones as best I could (might be a couple of stragglers in the lines, but it compiles and exhibits the problem I'm experiencing)
Code:
.NOLIST
.INCLUDE "m32U4def.inc"
.LIST
;Declare our definitions
;r0 is usually used in conjunction with the Z register.
;.def ??? = r0
.def ri = r1 ;Working register for our Interrupt-Service-Routine
;Registers used during an interrupt must be reserved
;for that purpose or they have to reset their initial
;value at the end of the service or unpredictable results
;will occur.
.def sb = r2 ;Switch buffer from our switches
.def sb0 = r3 ;Switch buffer from our switches
.def sb1 = r4 ;Switch buffer for our switches
.def z2 = r5 ;our HIGH2 value counter
.def z1 = r6 ;our HIGH1 value counter.
;.def ??? = r7
.def ct0 = r8 ;General Counter / General Purpose Register
.def ct1 = r9 ;General Counter / General Purpose Register
;.def ??? = r11
;.def ??? = r12
.def gp01 = r13 ;General purpose register: Initialized to 0x01
.def gp00 = r14 ;General purpose register: Initialized to 0x00
.def gpff = r15 ;General purpose register: Initialized to 0xFF
;--------------
.def gp = r16 ;General purpose register
.def gpa = r17 ;General purpose register
.def gpb = r18 ;General purpose register
.def gpc = R19 ;General purpose register
.def cta = R20 ;General Counter / General Purpose Register
.def tesa = R21 ;Used for TESTING
.def tesb = R22
;.def ??? = R23
;The next two registers are usually grouped together for 16-bit calculations.
.def WL = r24 ;Use this to track our position in the current array. LO byte
.def WH = r25 ;Use this to track our position in the current array. HI bite
;Do not define the following, they are already defined in m32U4def.inc
;.def XL = R26
;.def XH = R27
;.def YL = R28
;.def YH = R29
;.def ZL = R30
;.def ZH = R31
.CSEG
rjmp main
main:
;START INITIALIZE BLOCK
ldi gp, (1<<JTD)
out MCUCR, gp ;Disable the maddening JTAG interface....
out MCUCR, gp
;START INITIALIZE REGISTER BLOCK
;commonly used values.
clr gp00 ;clear
mov gpff, gp00 ;with commonly used fixed values.
dec gpff ;get 0xff
mov gp01, gp00
inc gp01 ;get 0x01
;counters
mov z1, gp00 ;Initalize our "HI" byte counter.
mov z2, gp00 ;Initalize our "HI2" byte counter.
;END INITIALIZE REGISTER BLOCK
;START INITIALIZE COUNTERS
ldi gp, (1<<WGM01) ;turn on our WGM1 register
out TCCR0A, gp ;Set our CTC mode for counter 0.
;END INITIALIZE COUNTERS
;START INITIALIZE TIMERS
;Prescaler of the counter/timer = 256, that is 4 MHz/256 = 15625 Hz = $3D09
;turn on our prescaler and clock
ldi gp, 0x01 ;Initialize the timer/counter 0 prescaler
out TCCR0B, gp ;set our timer/counter register
out OCR0A, gpff ;Says I need to set this but doesn't seem to do much.
;Check into OCR0A register later.
;END INITIALIZE TIMERS
LoopMain:
;Start wait
in gp, TIFR0 ;Read in our interrupt flags
andi gp, 1<<OCF0A ;Mask off our flags
cpi gp, 1<<OCF0A ;see if our flag has "triggered"
brne LoopMain; ;if not, then we wait.
out TIFR0, gp ;Reset our flag, cause we're done with it.
inc z1 ;increment our HI counter
ldi gp, 0xFF ;load our wait value
cp z1, gp ;compare our values.
brne LoopMain; ;go back around if we still haven't reached our goal.
mov z1, gp00 ;reset our z1 counter
inc z2
rcall Func_Matrix_4x4
com sb0
out portb, sb0
rjmp LoopMain ;Go back to the beginning.
Func_Matrix_4x4: ;TEST: Mangles gp, sb0, sb1! Returns sb0
; values > 8-4-2-1-0
; 7-B-D-E-F < inverted values
ldi tesa, 0xF0 ;Create our MASK
;COL 0
ldi cta,0xFE ;For PORTD
ldi gp, 0x01 ;For DDRD
out PORTD, cta
out DDRD, gp ;Set one of our bits to OUTPUT
nop
in sb0, PIND ;Grab our values
;COL 1
ldi cta,0xFD
ldi gp, 0x02
out PORTD, cta
out DDRD, gp ;Set one of our bits to OUTPUT
nop
in sb1, PIND ;Grab our values
and sb0, tesa
and sb1, tesa
swap sb1
or sb0, sb1
;Returns R3, all other register values are discarded
ret
;END Func_Matrix_4x4
I removed as much of the irrelevant code while still being able to replicate the problem.
While nearly every sample I checked into on the internet regarding matrixes used PORT B, I cannot as I need the SPI interface (which is removed from the circuit). The LED's in the circuit on PORTB are there just for testing.
I believe I disabled the JTAG interface via software correctly.
I show 1/2 of a 4x4 matrix. I actually ran out of breadboard room. But I figure any problems that can't be solved with a 4x2 matrix isn't going to be solved with a full 4x4 matrix. Correct me if I'm wrong though.
AVR Studio V4.18 Build 684
Atmega32U4 on a MattairTech MT-DB-U4
The following window are the fusebits. Yes, like a moron, I inadvertently set the JTAGEN bit. Yes, like a moron, I misplaced the programmer. At least I finally figured out how to disable the JTAG ports via software.
I am a little puzzled by the notation that the external clock is being used and at 8Mhz. The MT-DB-U4 states the external crystal is 16Mhz and that I should be running the internal 8Mhz clock with the code I have. About the only thing I can think of is that the external clock is interfering with my D0. Not sure how that can be fixed however.
I'm out of ideas. Any tips or something else I should look at? |
|
|
| |
|
|
|
|
|
Posted: May 22, 2012 - 07:45 AM |
|

Joined: Jul 07, 2007
Posts: 55
|
|
| I just realized the title kind of sucks. Describes the problem incorrectly. Sorry about that. |
|
|
| |
|
|
|
|
|
Posted: May 22, 2012 - 08:32 AM |
|


Joined: Mar 28, 2001
Posts: 20386
Location: Sydney, Australia (Gum trees, Koalas and Kangaroos, No Edelweiss)
|
|
|
Quote:
I am a little puzzled by the notation that the external clock is being used and at 8Mhz.
That's the setting for 8MHz and UP, however it should use full swing mode for 8MHz and UP. Remember that you have the CKDIV8 fuse on so it it 8 times slower.
Quote:
The MT-DB-U4 states the external crystal is 16Mhz
Not if VCC is 3.3V which maybe the case as you are using 2.6V BOD. |
_________________ John Samperi
Ampertronics Pty. Ltd.
www.ampertronics.com.au
* Electronic Design * Custom Products * Contract Assembly
|
| |
|
|
|
|
|
Posted: May 22, 2012 - 11:38 AM |
|

Joined: Nov 02, 2009
Posts: 3239
Location: Zelenograd, Russia
|
|
Proper timing is yor main problem.
First, I'd highly suggest inverting your strobe diodes and using low strobes instead of high - this allows the AVR internal pullup resistors to be used without adding external ones.
Then, look what are you doing - you're applying a positive strobe, then after just a single NOP you're reading the pins (which BTW do not have any pulldown resistors, so the turn-off time is veeeeery long). Even at 1 MHz this is a too short time for pin voltage levels to stabilize, not even mentioning the pushbutton contact bouncing which is of 5..10 milliseconds order.
A common good practice to get rid of that "ghosting" and other side effects like bouncing is using a 10..20 mS timer interrupt to read the pins twice and detect the state changes by EOR'ing those two read results (current and previous). When a stable state is confirmed (two sequential reads are the same) you just write a new stable state to a dedicated variable and swap the strobes, otherwise do nothing.
And, as usually, there are myriads of AVR keyboard routines floating around in the Web, not excluding the Atmel appnotes which the Guru Freaks are periodically pointing to. |
|
|
| |
|
|
|
|
|
Posted: Jun 01, 2012 - 05:53 AM |
|

Joined: Jul 07, 2007
Posts: 55
|
|
Sorry I didn't get back to this sooner. I got wrapped up reading a couple of interesting discussions that I just haven't gotten back to this.
js wrote:
Quote:
I am a little puzzled by the notation that the external clock is being used and at 8Mhz.
That's the setting for 8MHz and UP, however it should use full swing mode for 8MHz and UP. Remember that you have the CKDIV8 fuse on so it it 8 times slower.
So if I have a 16 Mhz crystal and with the CKSEL bits apparently (if my bit math is correct) set to use the external crystal, then I'm running the AVR at 2Mhz?
js wrote:
Quote:
The MT-DB-U4 states the external crystal is 16Mhz
Not if VCC is 3.3V which maybe the case as you are using 2.6V BOD.
I did not notice that. Well... I'm powering the circuit (majority of the time) via the USB port. So according to the charts, the BODLEVEL should be set to 4.3 then? The final circuit is intended to powered via wall current so I don't see a whole lot of point in enabling the BODLEVEL at all. Unless I'm missing something fundamental about it? Let me read that chapter a bit.
---
MBedder wrote:
Proper timing is yor main problem.
First, I'd highly suggest inverting your strobe diodes and using low strobes instead of high - this allows the AVR internal pullup resistors to be used without adding external ones.
I'm not sure where you get the idea I'm strobing on high, instead of low. The chart is simply inverted from the true logical values I'm using. I just spent all that time carefully typing and aligning all of that out before I realized I erred with the logic output. The code sample should use the correct logic. If I'm not correct about that please tell me where the error is, it means I've been looking at the tables in the docs wrong.
MBedder wrote:
Then, look what are you doing - you're applying a positive strobe, then after just a single NOP you're reading the pins (which BTW do not have any pulldown resistors, so the turn-off time is veeeeery long). Even at 1 MHz this is a too short time for pin voltage levels to stabilize, not even mentioning the pushbutton contact bouncing which is of 5..10 milliseconds order.
Is a pull down even necessary if I'm strobing on low? Isn't setting a port to LOW and OUTPUT is a direct path to ground?
MBedder wrote:
A common good practice to get rid of that "ghosting" and other side effects like bouncing is using a 10..20 mS timer interrupt to read the pins twice and detect the state changes by EOR'ing those two read results (current and previous). When a stable state is confirmed (two sequential reads are the same) you just write a new stable state to a dedicated variable and swap the strobes, otherwise do nothing.
That's an interesting idea of using EOR. I've never really thought of using it like that. Makes it easy. I've been using a block of memory in a Round Robin fashion and ORing the results of 7 or 8 samplings. The most annoying aspect is keeping track of which state the button is in previously and tracking the new state. For example, knowing a button was held down then released and executing a different bit a code than if the same button was open then pushed down. Which meant creating masks and doing all sorts of acrobats with three different values. I'll have to experiment a bit with EOR to see what I can get out of it.
MBedder wrote:
And, as usually, there are myriads of AVR keyboard routines floating around in the Web, not excluding the Atmel appnotes which the Guru Freaks are periodically pointing to.
That's an extremely useful link and I learned a little bit more. I don't really think of it as a keyboard interface. A keyboard would be an adequate substitute though, but I digress. For future reference:
Timing was the problem. I derived the matrix code from a straight forward parallel button input circuit where each switch was tied to a dedicated IO port. Set to Input, Pull High, watch for LOW signal as switch shorts to ground, standard fare. The single NOP worked so well that I never once considered it to be the source of the problem when I decided to expand the inputs by using a Matrix (I originally used Parallel to serial IC's and decided I needed the SPI for something else and the problem didn't crop up there either). I did a quickie test by adding a LPM with each NOP giving me 4 cycles each time. All of the "ghost" behavior went away.
I did some experiments and two cycles is the bare minimum. I rewrote and integrated the new code giving me three cycles at each NOP and the switching works perfectly.
Now the logic side of it is a whole other story. :\
Thanks for the assistance. |
|
|
| |
|
|
|
|
|
Posted: Jun 01, 2012 - 06:17 AM |
|


Joined: Mar 28, 2001
Posts: 20386
Location: Sydney, Australia (Gum trees, Koalas and Kangaroos, No Edelweiss)
|
|
|
Quote:
then I'm running the AVR at 2Mhz?
If all is correct and the CKDIV8 fuse is still on.
Quote:
the BODLEVEL should be set to 4.3 then?
Always set it at the highest setting available depending on VCC.
Quote:
I don't see a whole lot of point in enabling the BODLEVEL at all.
Even MORE reason to enable BOD if running from mains. Remember BOD stands for Brown Out Detections as in mains supply. |
_________________ John Samperi
Ampertronics Pty. Ltd.
www.ampertronics.com.au
* Electronic Design * Custom Products * Contract Assembly
|
| |
|
|
|
|
|
Posted: Jun 03, 2012 - 02:11 AM |
|

Joined: Jul 07, 2007
Posts: 55
|
|
Oh duh, what was I thinking? (Probably wasn't ) I was thinking BOD was intended to be a low battery type of thing. OK, I think I understand how it works. |
|
|
| |
|
|
|
|
|
|
|
|