christop wrote:
If my math is right, the CPU clock speed would have to be exactly 3.5MHz to achieve that bit rate:

F_CPU = bitrate * 16 * (UBRR + 1)
3500000 = 31250 * 16 * (6 + 1)

If the clock rate is what it was previously (3.6864Mhz), the actual bit rate is slightly higher:

bitrate = F_CPU / (UBRR + 1) / 16
32914.3 = 3686400 / (6 + 1) / 16

Any error in the rate above a few percent will give you some garbage data as you saw. (32914.3/31250 = 1.053 = 5.3% too fast)


As an aside, the clock rate 3.6864MHz is used as it is an integer multiple of common (standard) serial bit rates:

115200 = 3686400 / (1 + 1) / 16
57600 = 3686400 / (3 + 1) / 16
38400 = 3686400 / (5 + 1) / 16
9600 = 3686400 / (23 + 1) / 16

(And obviously many more common bit rates are possible)

UBRR would be set to 1, 3, 5, or 23 for those bit rates.

Edit: if the bit rate has to be exactly 31250, I would use a CPU clock source that is an integer multiple of 500000 (31250*16), such as 4MHz (with a UBRR value of 7).


Yeah, I kinda realized that this morning, so i have an ATtiny85 generating a 4Mhz clock source, which should be able to do that baud rate with 0% (more or less) error. Fingers crossed!
christop wrote:
There are sites that can calculate that stuff too, which is good for people who aren't stuck on a Wintel machine (I'm on an Android phone at the moment, my main desktop runs Fedora, and my other desktop computers have non-Intel processors (68k, PowerPC, ...)).

http://www.wormfood.net/avrbaudcalc.php
http://app.josephn.net/avr_ubrr/
Scroll down a bit; I rewrote it in Javascript a while ago, and that version is embedded in the page. Smile

The existent AVR ones are of course more immediately useful.
Tari wrote:
Scroll down a bit; I rewrote it in Javascript a while ago, and that version is embedded in the page. Smile

Indeed, I didn't scroll down far enough to see the Javascript version. Remember, I'm on an Android phone with a screen smaller than the palm of my hand. Razz
Hey guys! Progress!
Using the 4 MHz oscillator output of the ATtiny85 was too variant to do anything with, so I scrapped that and went with 115200 baud. Sadly, this means that UARTtunes cannot accept a standard MIDI device. This fact tempts me to use the MOD protocol, though I think I will stick with MIDI for now.


Code:
Hello World!


My next step will be to combine the Oscillator with the serial. Wish me luck!
pcb_master: I'm now curious to know how you tested your original "Hello world" program running at 31250bps. Did you connect your board to a serial port on your computer, or did you use a USB-to-serial converter, or something else entirely? (Or do you have some kind of MIDI interface device on your computer?) It's possible that whatever you used could not run at 31250bps and probably fell back to one of the closest "standard" rates (either 28800bps or 38400bps). I happen to have a USB-to-serial cable that I bought recently and it supports only the standard rates.

So even if you can get exactly, or within 1% of, 31250bps, you won't be able to communicate with most other serial devices (the exception being MIDI devices, of course).
I'm using an FTDI USB to Serial converter that I know can archive that baud rate (it's the same chip on the Arduino).
pcb_master wrote:
I'm using an FTDI USB to Serial converter that I know can archive that baud rate (it's the same chip on the Arduino).

Ah, yes, you're right. The FTDI chip apparently supports nonstandard/custom rates, while my cheap PL2303-based cable does not. I guess that partly explains the big difference in price between the two converters.
I can't figure out why this doesn't work. sei(); doesn't seem to enable the interupts as it should. What is up with it?

Code:

#define F_CPU 3686400

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

/* Prototypes */
void InitUART( unsigned char baudrate );
unsigned char ReceiveByte( void );
void TransmitByte( unsigned char data );

char input;
unsigned short phase = 0; // modulus is 65536
unsigned short fscaled = 3520; // scaled frequency
int noteTable[49] =
{
515 ,
554 ,
587 ,
622 ,
659 ,
698 ,
740 ,
784 ,
831 ,
880 ,
932 ,
988 ,
1046 ,
1109 ,
1175 ,
1244 ,
1318 ,
1397 ,
1480 ,
1568 ,
1661 ,
1760 ,
1865 ,
1976 ,
2093 ,
2217 ,
2349 ,
2489 ,
2637 ,
2794 ,
2960 ,
3136 ,
3610 ,
3520 ,
3729 ,
3951 ,
4186 ,
4435 ,
4699 ,
4978 ,
5274 ,
5588 ,
5920 ,
6272 ,
6645 ,
7040 ,
7459 ,
7902 ,
8372 ,
};

/* Main - a simple test program*/
int main( void )
{
   InitUART( 1 );
   DDRB = 255;
   sei();
   while(1)
    {
      phase += fscaled; // accumulate phase; this is the PA
         PORTB = phase / 256; // output a sawtooth wave sample between 0 and 255; this is the PAC
         _delay_us(1000000./8192); // this delay value should be calculated by the compiler at compile time
   }
}

ISR(UART_RX_vect)
{
   int note = ReceiveByte();
   fscaled = noteTable[note - 36];
   int volume = ReceiveByte();
   if(volume == 0)
      fscaled = 0;
}

/* Initialize UART */
void InitUART( unsigned char baudrate )
{
   UBRR = baudrate;                  /* Set the baud rate */
   UCR = ( (1<<RXEN) | (1<<TXEN) );  /* Enable UART receiver and transmitter */
}

/* Read and write functions */
unsigned char ReceiveByte( void )
{
   while ( !(USR & (1<<RXC)) )    /* Wait for incomming data */
      ;                         /* Return the data */
   return UDR;
}

void TransmitByte( unsigned char data )
{
   while ( !(USR & (1<<UDRE)) )
      ;                          /* Wait for empty transmit buffer */
   UDR = data;                  /* Start transmittion */
}
Would need to refer to the datasheet to be sure, but I suspect you're failing to unmask the relevant interrupt at the peripheral. Comments in some of my own old code appear to support that assertion:
Code:
void uart_init() {
    // Set baud rate
    UBRR0H = 0;
    UBRR0L = 129;   // 9600 bps at 20 MHz CPU

    // 8n1 async, enable transmit, no interrupts
    UCSR0B = _BV(TXEN0) | _BV(RXEN0);
    UCSR0C = 3 << 1;

    // printf and friends go over our UART
    fdevopen(uart_put, uart_get);
}

static int uart_put(char c, FILE *f) {
    uart_transmit_char(c);
    return 0;
}

static int uart_get(FILE *f) {
    while (!(UCSR0A & (1 << RXC0)));
    return UDR0;
}
pcb_master wrote:

Code:

ISR(UART_RX_vect)
{
   int note = ReceiveByte();
   fscaled = noteTable[note - 36];
   int volume = ReceiveByte();
   if(volume == 0)
      fscaled = 0;
}

You are blocking during an interrupt, which is a big no-no. After you receive the first byte (note), the receive buffer is empty, and the second ReceiveByte will wait (block) until another byte (volume) is received. I don't know what actually happens when the second byte is received--either the second ReceiveByte succeeds, or the interrupt might even fire again (recursively), eventually leading to a stack overflow if you send enough data to it. In either case your main program cannot run while the interrupt handler is waiting for a second byte.

You need to change the interrupt handler to receive only one byte, do something with that byte, and then return. The byte could be either a note byte or a volume byte; you have to figure out which it is (hint: use a flag variable to keep track of which byte you're expecting next).
The problem is that when the code is run (inside the debugger) the ISR routine is never triggering. You are right that the ReceiveByte() is blocking, but the ISR is anyway. I might disable interrupts while inside the ISR to prevent multiple executions. Also the ISR is blocking in the sense that another interrupt will not trigger until the first one is finished. So disabling the interrupts right before the second byte is received should prevent multiple executions.

Edit: It seems that the RXCIE bit of the UCR register needs to be set for the Receive Complete interrupt to be enabled.
pcb_master wrote:
You are right that the ReceiveByte() is blocking, but the ISR is anyway.

The ISR is not blocking in the same sense that ReceiveByte is blocking. The ISR should do one thing and return as quickly as possible. It definitely should not poll, which is exactly what ReceiveByte does. It polls (in a wasteful busy loop) the receive buffer's status until a byte is received. ReceiveByte should be used only when a UART interrupt is not being used, and it definitely should not be used inside an interrupt.
I had some free time, so I worked on a through-hole PCB for UARTunes. It's pretty small at approx. 3.5x1.5 inches.


Also I worked on the code a little more:

Code:

#define F_CPU 3686400

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

/* Prototypes */
void InitUART( unsigned char baudrate );
unsigned char ReceiveByte( void );
void TransmitByte( unsigned char data );

char input[3];
unsigned short phase = 0; // modulus is 65536
unsigned short fscaled = 3520; // scaled frequency
int noteTable[49] =
{
515 ,
554 ,
587 ,
622 ,
659 ,
698 ,
740 ,
784 ,
831 ,
880 ,
932 ,
988 ,
1046 ,
1109 ,
1175 ,
1244 ,
1318 ,
1397 ,
1480 ,
1568 ,
1661 ,
1760 ,
1865 ,
1976 ,
2093 ,
2217 ,
2349 ,
2489 ,
2637 ,
2794 ,
2960 ,
3136 ,
3610 ,
3520 ,
3729 ,
3951 ,
4186 ,
4435 ,
4699 ,
4978 ,
5274 ,
5588 ,
5920 ,
6272 ,
6645 ,
7040 ,
7459 ,
7902 ,
8372 ,
};

/* Main - a simple test program*/
int main( void )
{
   InitUART( 1 );
   DDRB = 255;
   sei();
   while(1)
    {
      phase += fscaled; // accumulate phase; this is the PA
         PORTB = phase / 256; // output a sawtooth wave sample between 0 and 255; this is the PAC
         _delay_us(1000000./8192); // this delay value should be calculated by the compiler at compile time
   }
}

ISR(UART_RX_vect)
{
   for(int i = 0; i<3; i++)
   {
      input[i] = UDR;
   }
   if(input[0] == 0x90)
   {
      fscaled = noteTable[input[1] - 36];
   }
   if(input[0] == 0x80)
   {
      fscaled = 0;
   }
}

/* Initialize UART */
void InitUART( unsigned char baudrate )
{
   UBRR = baudrate;                  /* Set the baud rate */
   UCR = ( (1<<RXEN) | (1<<TXEN) | (1<<RXCIE));  /* Enable UART receiver and transmitter and the recive complete interupt*/
}


The notes do turn off now, but the note is staying the same. Is ther anyone else who has a AT90S2313 (or ATtiny2312) that could try my code and see what's wrong with it?
Can you make it echo whatever it's getting via the
Code:
input[i] = UDR;
line so you can see what's happening there? Circuit question: (1) what's with the massive resistor chains, and (2) why can't you use an ATtiny with fewer pins for this project?
KermMartian wrote:
Can you make it echo whatever it's getting via the
Code:
input[i] = UDR;
line so you can see what's happening there? Circuit question: (1) what's with the massive resistor chains, and (2) why can't you use an ATtiny with fewer pins for this project?


1. I'm not really comfortable using the PWM, I could never figure it out.
2. I could use an ATtiny85 for this, but I figured I would need the extra flash and ram. Perhaps if it is small enough when it is finished, I can port it.
I have no idea anymore. This little code snippet makes the code much too large to fit in the controller.

Code:

for (int i = 0; i <= input - 21; i++)
{
   fscaled = 220*(1.05946*i);
}


Help!
For my own reference: your micro has 2K of program memory.

The problem is probably that the soft float routines that you force it to link are incredibly huge. Your options are either to get a bigger micro or not use floats. With the latter option, you could probably do 16.16 fixed-point and still get perfectly acceptable results out of it with much less support code.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 2 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement