EEPROM Panel Interface -- Another AVR-GCC C question - Page 1
Forum section: electronics / beginners

author: MrAureliusR
author_rank: Unknown
timestamp: September 19, 2013, 12:01:10 am
content: So I've been making great progress on my project to build a physical programming interface for this SPI EEPROM chip I have. The whole exercise is essentially pointless as it's much easier to program this EEPROM any other possible way but the idea was I'd learn SPI, I2C/TWI and a bunch more C in the process. On all those counts, HUGE success as the project is coming along very well and I've found this whole thing very, very satisfying.I have a very specific question, and I've been trying to findgoodresources on programming AVR micros in C but there's only bits and pieces floating around on the net, and some of them are absolutely terrible. People who shouldn't be programming micros let alone trying to teach others.Anyway, my question is as follows:My 'front panel' interface has 8 buttons. Four of those represent four binary digits, to get 8-bit resolution. They're labelled as 8, 4, 2, and 1 so I can do the math quickly (this is just the prototype, when I actually build the final version I may change the labels). Each one has an LED above it to indicate whether it's on or off. Pressing one of these buttons toggles the LED, and also XORs that buttons value onto the current variable. The variable being changed can be either DATA or ADDR depending on which mode we are in. This all works fine, I've tested it. However, the problem is: let's say I turn on the module, which puts us in ADDR and READ mode by default. This means pushing the 8/4/2/1 buttons will change the address variable, and the READ mode means pressing the GO button will read that address into buffer. Switching to DATA mode will then display this read data. This will all be on a 7-segment display in the near future.What I'm trying to implement (and it seems to simple but I can't think it out) is lets say I have buttons 8 and 2 pressed, so the addr variable reads 0b00001010. This is clear because the LEDs are on. If I press the ADDR/DATA toggle button however, I need the LEDs to change to reflect whatever value is in the data variable. And of course the opposite needs to happen, when you press the button again the addr variable should then be lit up.The problem is all my variables are in 0b00000000 form, but I'm only working with the 4 least significant digits. If I were to simply put PORTD = addr; or something similar, I have a feeling this won't work. The LEDs are PD0, 1, 2, and 3, so they're also the 4 least significant bits of PORTD. However the other four pins are the SW1, SW2, SW4 and SW8 pins. They of course are set as inputs in DDRD. Will they be affected if I were to simply do PORTD = addr;? (or should it be |= in this case I guess?). I'm trying very hard to keep my code as simple and small as possible.Any recommendations or suggestions? The code is FAR from complete so far, but you can see it in my github here if you'd like:https://github.com/MrAureliusR/AtmelProjects/blob/master/Learning_SPI/Learning_SPI/Learning_SPI.cThe file is still called Learning_SPI because that's originally all this code was, me just learning how to interface the ATmega32A with the 25AA640A EEPROM. As you can see in the code, I will have a separate ATtiny84 to drive the 7-segment display. It will be receiving its commands over I2C, basically just to teach myself how to do it. So far everything has worked flawlessly and I'm proud of myself

author: David_AVD
author_rank: Super Contributor
timestamp: September 19, 2013, 12:44:46 am
content: One solution is to clear the PORT D bits 0..3 , then OR them with a masked copy of the new LED data.Code:PORTD = PORTD & 0b11110000;PORTD = PORTD | (LED_DATA & 0b00001111);**Exact syntax not checked**

author: Psi
author_rank: Super Contributor
timestamp: September 19, 2013, 12:48:05 am
content: Quote from: MrAureliusR on September 19, 2013, 12:01:10 amYes they will be affected if you do PORTD = addr;When you change bits in the PORT register the mcu..- Sets the output high or low if DDR is set as output- Enables/disables the internal input pullup resistors if DDR is set as inputYou need to read the current state of the port and only change the bits you want (1-4). Then write that back to the portPORTD = (PORTD & 0b11110000) | (myvalue & 0b00001111);The "ANDs" used here are whats called a mask. If you AND something with a binary pattern it removes anything in the zero part of the pattern.So, lets assume PORTD = 01010001 (we want to preserve 0101) and myvalue = 11100010 (we want to write 0010)-AND operation-portd 01010001mask 11110000out 01010000 (we want to preserve this data, it's the 4 highest bit positions which are your switches)-AND operation-myval 11100010mask 00001111out 00000010 (This is the new data we want to write, you could remove this step if your myvalue variable only ever contains 0-15)-OR operation-out1 01010000out2 00000010PORTD 01010010 (this is what we need to write back to the port to preserve bits 4567 and write new bits 1234 from our variable)Side note: if you using interrupts and plan to modify PORTD inside the interruptI'm not sure how smart the GCC compiler is here. If you modify PORTD inside an interrupt handler and the interrupt request by fluke just so happened to occur in the middle of this operation then you may lose the change to PORTD .etc. if you changed PORTD after the value had been masked and was in memory but before it had written it back to PORTD. Then when the interrupt finishes it will continue where it left off and write the old value in memory back to PORTD, overwriting your new value.

author: Electro Fan
author_rank: Super Contributor
timestamp: September 19, 2013, 12:54:13 am
content: Quote from: MrAureliusR on September 19, 2013, 12:01:10 amSorry I don't have the answer you are seeking but your project is outstanding! Learning C, SPI, I2C/TWI and putting it all together is excellent! Keep us posted on your progress. When you are done a summarizing youtube video would probably find a small ton of receptive students and enthusiasts ready to learn from your learnings and follow your path on this project. Congrats!

author: MrAureliusR
author_rank: Unknown
timestamp: September 19, 2013, 01:07:37 am
content: @ PsiI think I'm getting what you're saying. However I need to make sure PD4, 5, 6, 7 stay low as they are inputs and I want the pull-ups to stay off. So lets see if I've got it...example:User presses buttons 2 and 4 in ADDR mode. bmask_addr is now 0b00000110.User then presses the ADDR/DATA toggle button. Since bmask_data is still 0b00000000 at this point, would it look likePORTD = (bmask_data & 0b00001111); // because I want the top four to stay zero, and anything that's a zero in bmask_data (which is all of them) will cause the 1's to turn to zeros, thus representing the variable correctly.Vice versa, if the user then presses buttons 1, 2, and 8 in DATA mode, bmask_data is now 0b00001011.So switching back to ADDR mode would look likePORTD = (bmask_addr & 0b00001111); // which will once again accurately represent what was in bmask_addr from the last step, namely 0b00000110...I simplified what you wrote a bit, but that's because I don't need to actually use the value of PORTD in the ANDs, as the value at any given point is already stored in the variable that we are moving away from.I'll test this out and see if it works -- thanks a LOT everyone for your help and encouragement! I knew it would be something simple. I managed to figure out XOR on my own, I feel silly I didn't think of plain old AND. I still haven't gotten to use XNOR or anything crazy like that yet, lol.@Electro Fan Thanks for the encouragement. I will most definitely be posting YouTube videos about this project. My videos usually suck but this one will be more interesting for sure. This project started being developed while I was away, which was good as it forced me to think it all out, and I actually wrote everything down and drew diagrams! And schematics! By hand! What a great way to learn more about the whole process. No tools, just grey matter and time. Then when I got back to the bench I dove straight in, knowing what I wanted to do and how the code should go, so I soldered up the prototype, spent an hour or two doing the code and it actually worked FIRST TIME! Amazing!I also have to thank my new AVR Dragon -- this thing is amazing! I know it's not a true 'full' debugger but man -- the debugging it can do is so damn useful to a student like me! And the thing barely costs more than the AVRISP mkII! I've basically got unlimited software breakpoints, which is awesome, so I can push a button and then watch the processor step through all the lines of code one by one and make sure everything is working the way it should. SUCH a great tool I cannot recommend it enough. Anyone who's into AVRs at a relatively basic to intermediate level should own one. I'd love to get an AVR One! or one of the high end ones but I'm years away from thatso this'll have to do.

author: MrAureliusR
author_rank: Unknown
timestamp: September 19, 2013, 01:15:18 am
content: Hmm... it kind of works but when going back into one of the modes it seems to lose some of the wrong digits. Gotta figure out what it's doing. I forgot that even with the AVR Dragon it's hard to see which register is which and it won't easily show you the value of PORTD. I've just gotta sit here and think this through for a minute... maybe I DO need to include PORTD in the statement?Or should it be PORTD |= (bmask_data & 0b00001111); ...

author: Psi
author_rank: Super Contributor
timestamp: September 19, 2013, 01:16:38 am
content: Both Davids and my code above will preserve bits 4567 on PORTD.They will stay at whatever they are before the command runs, they will not change at any time during the execution of the line/lines of code.If you don't care about storing them and want them to always be zero you can simply the code a little.PORTD = (myvalue & 0b00001111);This will only ever write 0000 to bits 4567it will change bits 1234 to whatever is in bits 1234 of myvalue

author: MrAureliusR
author_rank: Unknown
timestamp: September 19, 2013, 01:18:47 am
content: Quote from: Psi on September 19, 2013, 01:16:38 amThis is what I did in my first response above. However when I ran the code something wonky was happening. I'm trying to figure it out right now. I'm debugging...

author: Psi
author_rank: Super Contributor
timestamp: September 19, 2013, 01:19:25 am
content: Quote from: MrAureliusR on September 19, 2013, 01:18:47 amnope, you had |= rather than =You only need |= when you want to preserve something on the port.A |= B is exactly the same as A = A | BIn this situation you don't need to preserve anything because you want bits 4567 to be zero no matter what they were before.So just a general = assignment is what you want.You want to set PORTD and don't care what state is/wasPORTD=(myvalue & 0b00001111);

author: MrAureliusR
author_rank: Unknown
timestamp: September 19, 2013, 02:26:16 am
content: Quote from: MrAureliusR on September 19, 2013, 01:07:37 amNope I had = to begin with not |= .. I was musing about it afterward but ruled it out for the reason you said. I still have the straight PORTD = (bmask_data & 0b00001111); and it loses the data when you switch twice. Ill commit the changes to my github if you'd like to see my code. I'm sure I'll be able to debug it in a bit I've just gotta figure out what's going on.Typed using Hacker's Keyboard for Android

author: Psi
author_rank: Super Contributor
timestamp: September 19, 2013, 02:29:18 am
content: Quote from: MrAureliusR on September 19, 2013, 02:26:16 amWe need more info on what you mean by this. what data is lost and what is being switched twice.Yeah, the code will help.

author: MrAureliusR
author_rank: Unknown
timestamp: September 19, 2013, 02:33:58 am
content: What I mean is this:The module starts up in READ and ADDR mode. I push two buttons at random (lets say 2 and. Then I push the ADDR/DATA button to toggle into DATA entry mode. I push one random button (lets say 1). Then I push the ADDR/DATA button to toggle AGAIN, and the 2 and 8 light up. If I push it once more to go back to DATA mode the 1 I entered earlier doesn't light up.The code is athttps://github.com/MrAureliusR/AtmelProjects/blob/master/Learning_SPI/Learning_SPI/Learning_SPI.cThe function I'm working on is buttoncheck()And I just fixed the commit, I had the wrong version up, it had random different operators. The problem I described was with the plain = sign, not any other operand.EDIT: It suddenly started working after some strange problems... I made a YouTube video for anyone who's interested, to show what I've done so far and I show the interface working... well, at least partially working. Sorry if I sound ... I dunno. Not stupid, but definitely not my sharpest video.

author: Electro Fan
author_rank: Super Contributor
timestamp: September 19, 2013, 07:53:30 am
content: Quote from: MrAureliusR on September 19, 2013, 02:33:58 amYou are doing great! You are definitely integrating a lot of skills, tools, and system pieces - it was cool to see your enthusiasm for JTAG, and your use of the MiniPro TL866 (A or CS?) is also nice.Can you give us a quick thumbnail on your education background - how much is formal/school training and how much is self-taught, how long you've been working on electronics?

author: Psi
author_rank: Super Contributor
timestamp: September 19, 2013, 09:29:20 am
content: Nice!Yeah, breadboard are notorious for things that intermittently work.

author: MrAureliusR
author_rank: Unknown
timestamp: September 19, 2013, 03:39:11 pm
content: I literally just started school a couple weeks ago. Everything I know about microcontrollers and C is self-taught. I took C++ in high school quite a few years ago, and luckily my teacher was a comments nazi so every single piece of code I've ever written has extensive comments... a good habit to get into I think.I just started college for electrical engineering. So far it's a bit boring but that's because they're basically recapping everything I learned in grade 10 science. The number of people in my class who can't figure out Ohm's law... But I'm getting off topic!After I filmed that video last night I actually did end up doing some more work. For those of you who are interested, feel free to check out my GitHub or YT channel. My YouTubing is fairly awful, I'm not a great host like Dave, but at least I show what I'm getting up to.In a day or so my 4511's will show up so I can implement the 7-segment displays. In the last 'grab-bag' of 300 ICs I ordered off eBay I found this really cool hexadecimal display IC.. it's a DIP package with the die and numerical section covered by glass... very cool looking and it works great! Unfortunately it's only 1 digit so I can't use it for this project, but it's great because it does the BCD decoding for you!If people are interested I'll keep posting my progress in this thread.

author: MrAureliusR
author_rank: Unknown
timestamp: September 22, 2013, 02:15:17 am
content: So my next big step was trying to get the ATtiny84A driving a 7-segment display. The obvious answer appeared to be using either a 4056 or a 4511. So I ordered 3 4511s and 5 4056's from element14. The 4056 has a secondary voltage input (Vee) that at a glance I thought was the voltage it used to output to the 7-segment display (wouldn't THAT have been a great idea! why the heck hasn't that been implemented? I'm sure it has been, somewhere) but of course that's NOT what Vee does. It appears to be something to do with swinging voltages? So you can drive up to a 30V 7-seg display? I don't know, I wasn't reading very closely.I have both common anode and common cathode displays but as these chips actually output voltage to the segments I started with common cathode. I hooked everything up, wrote a test program to the ATtiny84A, held my breath, and hit "program". And then sighed when it inevitably didn't work.However, after quadruple checking everything, I really can't figure out why. These aren't exactly complicated chips, they take a nibble and decode it to a 7-segment number. The problem seems to be that every single 7-segment display I have runs at ~2V. I don't know why chip manufacturers seem to fail to realize this. I have one that runs at 5V but it has the decode built-in. (I would just use that one but it's only a single digit!) What happens when I hook everything up is it just stays on the number 8, as if LT (lamp test) is low. But it's not! Believe me, I even disconnected the ATtiny84A, and tried to get the segments to change manually using breadboard wires!! I tried three or four different chips just to make sure, and I can't figure it out. I was thinking about using a common anode 7-seg because then I just use an LM317 to regulate 5V down to 2V and then just get the chips to put out inverted outputs (which I think one of them can do, I have to re-read the datasheet).Basically I'm wondering if anyone has experience using these chips with 7-segment displays. I have a LOT of 7-segment displays lying around that I've been wanting to use. I really thought these chips would be the easiest thing ever!! I feel like a real moron now after all my success so far. Anyway, perhaps I'll just order one of the serial enabled 4-digit 7-seg displays from SparkFun. You just send it serial data and it decodes it into a 4-digit number. That might be the easiest thing to do in my case, because ultimately I want to have the address and the data beside each other, which will take a minimum of 4 digits.Thoughts?

author: IanB
author_rank: Super Contributor
timestamp: September 22, 2013, 02:37:34 am
content: Quote from: MrAureliusR on September 22, 2013, 02:15:17 amOK, I warn you in advance that I don't know much about this, but I would guess that if those 7-segment displays are made from red LEDs then the ~2 V is the forward voltage drop of the LEDs. You may already know that LEDs are current driven devices, not voltage driven devices. So you need to drive each LED segment with a suitable current of a few mA.On the other hand logic chips run off a standard voltage like 5 V. They will output a high or low signal close to 5 V or 0 V and you have to convert that to the current drive that the LED needs. I would guess the easiest thing is to use an appropriate series resistor to limit the LED current to an appropriate value. To maintain consistent brightness you may need to use one resistor per LED segment so that all segments get about the same current no matter how many segments are lit.Maybe some decoder chips out there have constant current outputs to save on resistors? As I say, I really don't have any deep knowledge of this field.

author: MrAureliusR
author_rank: Unknown
timestamp: September 22, 2013, 02:52:05 am
content: I'm such a dolt sometimes.I'm sure a few resistors is all I need. I always forget voltage 'drop '.

author: mariush
author_rank: Super Contributor
timestamp: September 22, 2013, 02:53:48 am
content: The displays don't run at 2v, that's the forward voltage. Depending on the segment color, the forward voltage may be anything from 2v to about 3.2v and the current they support is usually a maximum of around 20mA per segment... but that would be super bright.. you shouldn't have to use more than 10mA.With 5v input, a 1000 ohm (1kOhm) resistor will give each segment about 3mA which should be bright enough... if you want more you could just use a 470 ohm resistor.. the formula is simple : R = (Vs - Vf ) / IfAs IanB says, you need to limit the current that each segment gets. If the displays are common cathode, that's super easy.. just a resistor between the segment pin and the microcontroller or the bcd decoder.With common anode, that's a bit harder because all seven or eight segments are powered from a single input and you're supposed to sink current to get segments to light up. Basically, I suppose you could use a ULN2003A or ULN2803A between the segments and the microcontroller or the bcd decoder, with the appropriate series resistors to each pin.

author: MrAureliusR
author_rank: Unknown
timestamp: September 22, 2013, 07:30:05 am
content: Once again I learn the hard way: think, THEN solder, not the other way round...