Forum Menu




 


Log in Problems?
New User? Sign Up!
AVR Freaks Forum Index

Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Author Message
SavannahLion
PostPosted: May 22, 2012 - 07:30 AM
Wannabe


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. Sad

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?
 
 View user's profile Send private message  
Reply with quote Back to top
SavannahLion
PostPosted: May 22, 2012 - 07:45 AM
Wannabe


Joined: Jul 07, 2007
Posts: 55


I just realized the title kind of sucks. Describes the problem incorrectly. Sorry about that.
 
 View user's profile Send private message  
Reply with quote Back to top
js
PostPosted: May 22, 2012 - 08:32 AM
10k+ Postman


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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
MBedder
PostPosted: May 22, 2012 - 11:38 AM
Raving lunatic


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.
 
 View user's profile Send private message  
Reply with quote Back to top
SavannahLion
PostPosted: Jun 01, 2012 - 05:53 AM
Wannabe


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.
 
 View user's profile Send private message  
Reply with quote Back to top
js
PostPosted: Jun 01, 2012 - 06:17 AM
10k+ Postman


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
 
 View user's profile Send private message Visit poster's website 
Reply with quote Back to top
SavannahLion
PostPosted: Jun 03, 2012 - 02:11 AM
Wannabe


Joined: Jul 07, 2007
Posts: 55


Oh duh, what was I thinking? (Probably wasn't Smile ) I was thinking BOD was intended to be a low battery type of thing. OK, I think I understand how it works.
 
 View user's profile Send private message  
Reply with quote Back to top
Display posts from previous:     
Jump to:  
All times are GMT + 1 Hour
Post new topic   Reply to topic
View previous topic Printable version Log in to check your private messages View next topic
Powered by PNphpBB2 © 2003-2006 The PNphpBB Group
Credits