4586Views15Replies

Author Options:

Programming AVR in C (Help me!) Answered

Hi! I've been trying to figure this out, and I can't seem to find the answer. I've seen it done in Assembler, so it is possible. I'm using ATiny2313's, if it makes a difference. I want to know how I can output a negative voltage through a pin. I'm trying to do some multiplexing. Thanks. I just started with AVR microcontrollers.

15 Replies

user
westfw (author)2007-06-14
In general, to have a pin output 5+, you put a "1" in the appropriate io port bit, and to output GND, you put a "0" in the appropriate bit. Exactly how you do this will depend some on the compiler and your environment. For example, Arduino wraps a function digitalWrite around it:
 digitalWrite(ledPin, HIGH);   // sets the LED on  delay(1000);                  // waits for a second  digitalWrite(ledPin, LOW);    // sets the LED off  delay(1000);                  // waits for a second
Internally, though, these resolve to sbi() and cbi() pseudo-functions that mimic the assembly language instructions of the same names. The other common method I've seen (in a PIC compiler; I don't know about AVRs) is to map the IO port into memory space, so you would do something like:
          porta.bit1 = 1;          portb.bit2 = 2;
Usually the arguments you use are going to be pre-defined in a .h file specific to the processor you are using.

Perhaps you are confused by comments like those in the arduino code I quoted, where it says that it's turning the LED off when it sets the bit to zero. That's only true because the other side of the LED is connected to GND as well, and having both sides at GND means off. To ACTUALLY turn off a pin so that it is outputting neither +5 or GND, you could set it to be an input.

Select as Best AnswerUndo Best Answer

user
westfw (author)westfw2007-06-14

(note that gmoon's advice is good even though it doesn't look like mine. He's setting the whole port register at once with an appropriate read/modify/write instruction and computer value, while the arduino sbi/cbi code is using the single bit set/clear instructions of the avr.)

Select as Best AnswerUndo Best Answer

user
gmoon (author)westfw2007-06-14

The sbi() and cbi() macros are still present in avrgcc (or avr-libc, actually), but they are depreciated. I'm not sure why, but probably has something to do with macro expansion (and problems that result.) They now recommend the more explicit logic. But you can still use the older macros, if you like. Just include "compat/deprected.h"

Select as Best AnswerUndo Best Answer

user
westfw (author)gmoon2007-06-15

In somewhat recent gcc, someone rewrote the pre-processor in a way that changed quite a lot about how macro expansion works (especially in conjunction with string catenation and other "fringe" macro featues.) This caused us all sorts of problems at work; I believe we patched in some of the old behavior. It's sorta amazing how much of a popularly used language can be left "undefined"...

Select as Best AnswerUndo Best Answer

user
gmoon (author)westfw2007-06-16

I suspect C macros are something the designers wish would be eliminated altogether (but they are useful), and existing ones replaced with functions.

RE: undefined--yeah, I was always taught that variables are in an 'undefined' state unless initialized. But avrgcc variables are always initially 0. Probably just a case of the initial RAM state always being zero. So right off, that's not ANSI (and not portable.)

But in a wider sense, that's different in every target, so it's a way of keeping C thinner (no automatic init code included.)

Select as Best AnswerUndo Best Answer

user
gmoon (author)2007-06-14

OK, I see you just want to set the pin state. So for example, PB0 (port B, bit 0), starting with setting the pin for output:

You first need to set the DDR (data direction register) to output:

DDRB |= _BV(DDB0); // set DDR bit 0, output

Now change the state
PORTB |= _BV(PB0); // set bit (hi)

(cannot show the clear bit instruction, instructables refuses to show an apersand...and I'm too lazy to look up the html value.)

PORTB (ampersand)= ~_BV(PB0); // clear bit (lo)

These examples use the avrgcc defines for PORTB, PB0, etc., and the Bit Value macro, _BV(). Also, read the data sheet section on "I/O Ports", it has a table with DDR and port combinations.

This is output only, you'll need to change the DDR for input, and read PINB (PORTB is output for portb, PINB is input...) I cannot guarantee this works, as it's from memory--but the data sheet has all the info you need...

And obviously, your Makefile needs to load the correct defines for the chip you are using, or nothing will work right....

Select as Best AnswerUndo Best Answer

user
zachninme (author)gmoon2007-06-15

I know. I understand all that stuff. The problem I was having was I didn't think low would work as a GND on an LED. I guess it does, thanks.

Select as Best AnswerUndo Best Answer

user
gmoon (author)gmoon2007-06-14

I should mention this drives the pin, i.e., provides current when HI. If you want an open-collector type output, the simplest, quickest way is not to enable pullups, but switch between input tri-state and output LO with the DDR. Using internal pullups requires an additional step/code.

Select as Best AnswerUndo Best Answer

user
gmoon (author)2007-06-13

Maybe share in more detail what you're trying to achieve. I've used ATtiny2313 ICs (not sure about 2321), among others. Just about anything that can be done using assembly lang is also possible w/ C (exceptions might involve speed/efficiency/size, though avrgcc produces decently optimized code.)

Select as Best AnswerUndo Best Answer

user
zachninme (author)gmoon2007-06-14
user
westfw (author)2007-06-13

To do multiplexing, you usually set one output pin to +V (on the anode side) and one output pin to GND (on the cathode side.) As others have said, you usually can't get "negative" voltages from a pin, but I don't think that's what you want anyway; you just want GND, which is "negative" COMPARED to +V.

Select as Best AnswerUndo Best Answer

user
zachninme (author)westfw2007-06-14

Sorry, a GND. (Although I do have use for -V, but I dont need that now) I still don't know how to do that in C, though :P

Select as Best AnswerUndo Best Answer

user
NachoMahma (author)2007-06-13

. I'm with trialex; I've never seen "negative" outputs on a microcontroller. Even the industrial controllers I've worked with needed a special (ie, expensive) "card" for negative output.

Select as Best AnswerUndo Best Answer

user
NachoMahma (author)NachoMahma2007-06-13

. Oops. Make that "positive and negative output" at the end.

Select as Best AnswerUndo Best Answer

user
trialex (author)2007-06-13

I can't say I've seen it done before. Are you sure it was done on the bare pin just using assembler? Was it possible that it used some small amount of logic-level harware outside the microcontroller itself? You might be able to use a level-shifting chip, like the MAX232 to get negative voltages, although I'm guessing there would be a purpose-built chip like a simple inverter. If it is possible to do, make sure you post your results so we can all learn, I'd certainly be interested.

Select as Best AnswerUndo Best Answer