First AVR serial comms project - Need a hand

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys

This is my first project where I use the serial port on the AVR.
It seems straightforward, but still, I've run into a strange problem and I can't seem to figure out where I went wrong.

My project so far is simple:
I use an ATMega48 with only a 5V supply circuit, SPI and a the small IR-transmit circuit you can see attached to the post.
The processor is driven by a 7.3728 MHz external crystal.

The serial setup is supposed to be
115.2k
8 data bits 1 stop bit
No parity
Asynchronous
Simple, poll-based transmission, using code from the data sheet.

I use an old Palm Pilot with some custom software to check the sent IR-signals, but regardless of what I tell my program to transmit, I only receive byte values like 0xFF, 0xFB, 0xFE, 0x7F and similar. The pattern I receive in suggests that what I receive isn't external IR-noise, since the bytes are received in nice batches of ten completely uniform signals.

EDIT: Fuses set are SPIEN, BODLEVEL 4V3 and SUT_CKSEL is set to external full swing 258CK_14CK_4MS1.

I tried to simulate the code with AtmelStudio 6 which is my programming environment, but I find the simulator hard to use. Either it won't refresh for me to see the content of UDR0, or that's where my problem is. Under all circumstances, I'm pretty sure the program does actually send, it just sends the wrong bytes.

Here's my code:

/*
 * ttrandomizer.c
 *
 * Created: 17-10-2012 09:52:20
 *  Author: mmk
 */ 

#define F_CPU 7372800UL  // 7,3728 MHz
   //#define F_CPU 14.7456E6
   

//Includes
#include 
#include 
#include 
#include 

//Defines
#define ON 1
#define OFF 0

//Function prototyping
void init(void);
void selspeed(void);
void seltime(void);
void selaction(void);
void sendtelegram(int);

//Variable declaration area
int static volatile randspeed=20;
int static volatile randaction=3;


//Program code start
int main(void)
{
	
	init();
	
    while(1)
    {
	 selspeed(); //Select speed.

	 selaction(); //Select and execute action pattern.
    }
}

void init(void){
	//MCU Control Register
	MCUCR=0x10; //Global pull-up disable.
	
	//Port init
	DDRB=0x00; //Entire port set as input.
	PORTB=0x00; //All pins on Port B driven low (Hi-Z).
	
	DDRC=0x01; //Entire port set as input, except pin 0, which drives the ON/active-LED.
	PORTC=0x01; //LED on. 
	
	DDRD=0x02; //Port D is set as an input, except PD.1 which is set as output (overruled later in serial init).
	PORTD=0x00; //Port D is driven HI-Z/low.
	
	
	//USART init
	UCSR0B = (1 << TXEN0);
	UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); //8-bit data, 1 stop bit, async
	UBRR0H=0x00;
	UBRR0L=0x03; //Speed set to 115.2k.
	
}

void selspeed(void){	
	/*
	Possible speeds
	1.3 m/s = 0x04
	to 
	3.5 m/s = 0x1A
	13 possibilities
	*/
	
	int speedloop; //Local variable to govern how many copies of the telegram are sent out.

	randspeed=rand()%27+4; //Select a "random" value between 4 and 27.
	for (speedloop=0;speedloop<10;speedloop++) {
		sendtelegram(randspeed);	
		_delay_ms(5);
		}		

	}

void seltime(void){
	//Not yet implemented
}

void selaction(void){	//Select "randomly" actions between 1 and 10, then carry the action out.
randaction=rand()%10+1; //Possible actions. Bank left (1-4) or right (5-8), bank left, then right (9) or reverse (10) (feint move).

switch (randaction){
	case 1 :
	break;
	
	case 2 :
	break;
	
	case 3 :
	break;
	
	case 4 :
	break;
	
	case 5 :
	break;
	
	case 6 :
	break;
	
	case 7 :
	break;
	
	case 8 :
	break;
	
	case 9 :
	break;
	
	case 10 :
	break;
	
	default : 
	break;
	
	}	
}

void sendtelegram(int tele){		//As provided by the nice guys at Atmel.
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1<<UDRE0)) );
/* Put data into buffer, sends the data */
UDR0 = tele;
}

I'm sure I did something quite silly, but I've stared at this for a while now and need fresh eyes on it. Can you help? 
:)


Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How certain are you that the CPu is running at 7.3728MHz? As my FAQ#3 says almost every no working UART we see here is because the AVR is not running at the speed the programmer thinks it is.

In case it helps here are some debug routines I sometimes use:

void UART_init(void) {
	UCSRB = (1 << TXEN);
	UBRRL = 5; // 38400 @ 3.6864MHz
}

void UART_put(uint8_t c) {
	while (!(UCSRA & (1 << UDRE)));
	UDR = c;
}

void UART_puts(const char * str) {
	while (*str) {
		UART_put(*str++);
	}
}

void UART_newline(void){
	UART_put('\r');
	UART_put('\n');
}

void UART_putnibble(uint8_t c) {
	if (c < 10) {
		UART_put('0' + c);
	}
	else {
		UART_put('A' + c - 10);
	}
}

void UART_puthex(uint8_t c) {
	UART_putnibble(c >> 4);
	UART_putnibble(c & 0x0F);
}

void UART_putsP(const char * str, uint16_t n) {
	while (pgm_read_byte(str) != 0) {
		UART_put(pgm_read_byte(str++));
	}
	UART_puthex(n >> 8);
	UART_puthex(n & 0xFF);
	UART_newline();
}

void UART_dumpsector(uint8_t * Buff) {
	for (uint16_t i=0; i<512; i++) {
		if ((i % 16) == 0) {
			UART_put(' ');
			for(uint16_t j=(i -16); j<=i; j++) {
				UART_put(((Buff[j]>=(uint8_t)32) && (Buff[j]<(uint8_t)127)) ? Buff[j] : '.');
			}
			UART_newline();
		}
		UART_puthex(Buff[i]);
		UART_put(' ');
	}
	UART_newline();
}

If one used:

int main(void) {
  UART_init();
  UART_put('A');
}

A letter 'A' should be output. That's as simple as:

	UCSRB = (1 << TXEN);
	UBRRL = 5; // 38400 @ 3.6864MHz
	while (!(UCSRA & (1 << UDRE)));
	UDR = 'A';

The point being that as long as the clock is right it should never really take more than setting the TXEN bit, the UBRR register and then putting a character into UDR. (even the while() loop is superfluous for the case of sending a single character).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just set the CKOUT fuse and measured the frequency with an Agilent DSO-X 3014X 100MHz scope.
It measures the frequency to 7.37 MHz, so I guess the CPU is being clocked as expected.
Next thing to check, I guess would be the value of UBBR, which should be 3, right?
If so, your example should send out an "A" without a hitch, as I understand.
I'll give it a go and report back in a moment..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Next thing to check, I guess would be the value of UBBR, which should be 3, right?

I already checked that for you - it is.

What happens with TXD once it's outside the AVR - where is it going to? If a PC is it going through a MAX232 which switches 0..5V into +12V..-12V (not only level conversion but inversion too).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I changed my program code to this:

//Program code start
int main(void)
{
	
	//init();


    while(1)
    {
		UCSR0B = (1 << TXEN0);
	   UBRR0L = 3; // 38400 @ 3.6864MHz
		while (!(UCSR0A & (1 << UDRE0)));
		UDR0 = 'A';
		/*
	 selspeed(); //Select speed.

	 selaction(); //Select and execute action pattern.
	 */
    }
}

Which means just the four lines of code from your example (I changed UBBR0L to 3 as per the data sheet, and I didn't get any "A"s, just the same FF, FD, 7F and so forth..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

What happens with TXD once it's outside the AVR - where is it going to? If a PC is it going through a MAX232 which switches 0..5V into +12V..-12V (not only level conversion but inversion too).

No level changing, the pin is routed via the PCB to the circuit I attached in the first post, which means that it's routed through a 1k resistor to the basis of my BC547. The 547 works as a low side switch to the IR-LED.

I'll just check if the spectrum of my LED is the same as for the Palm Pilot I use to check it with.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't used serial comm. over IR, but it looks a little inverted to me. The serial signal is idle high, so you IR diode is on when idle. When the start bit is sent, the line goes low and your IR diodes is turned off. Sounds a little backward to me.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm, I can see that the LD271 (which is the one I use) shines at around 950nm while the TFDU 4100 and HSDL-4420 on the receivers are rated at 880nm.
I didn't notice until now, so I guess there's no further reason to look at the code until I find the proper LED.

@snigelen, as the circuit is now, a low on PD.1 means no light in the LED, while a high on PD.1 opens the BC547, causing a current to flow and the LED to light up.
So, Lo = LED off and Hi = LED on.
Is this not right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Malmkvist wrote:
as the circuit is now, a low on PD.1 means no light in the LED, while a high on PD.1 opens the BC547, causing a current to flow and the LED to light up.
So, Lo = LED off and Hi = LED on.
Yes.
Quote:
Is this not right?
I don't know. It just sounds strange to me that the LED is on when nothing is sent, it's a waste of power.

I don't even know if there exist a standard serial IR protocol where you can just hook up a transistor and an IR diode to an USART.

Don't you need something like IrDA? For example XMEGA A have this

XMEGA A Manual wrote:
22. IRCOM - IR Communication Module
22.1 Features
• Pulse modulation/demodulation for infrared communication
• IrDA 1.4 Compatible for baud rates up to 115.2 kbps
• Selectable pulse modulation scheme
– 3/16 of baud rate period
– Fixed pulse period, 8-bit programmable
– Pulse modulation disabled
• Built in filtering
• Can be connected to and used by any USART

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I wouldn't be trying to develop everything at once. Start with UART routed to a more "normal" destination (through a MAX232 and on to a PC). Get that working. Now your UAR routines are proven and you no longer need to worry about them. THEN attach the IR LED and start working on that part of the design. If you try to do everything at once with too many unknowns you'll never find the source of any problems you may have.