Here's a headscratcher: M32 to M324

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

Okay, it's a working and reliable design with a Mega32 driving a serial port to ISO9141 converter, a software second serial port driving a USB FTDI chip, 8MHz external crystal, and a graphical 64*128 LCD. Programming is via the ISP on board, and the JTAG is not available as the port is in other use.

And Farnell's have shoved up the price of the Mega32 to nearly double.

So, the Mega324 is available at two thirds the price. Let's play... it's compatible at the package level, same ports, voltage, speed. It has a second UART, which means I can remove my software UART as conveniently I use the same pins for mine as the second UART.

This may be a salient point: the original software was compiled on a 2004 version of WinAVR; I've stuck with that since to avoid any unexpected compiler changes. However, for this experiment, given the newness of the chip, I changed to 20081205.

As expected, the original code had a few changes; the interrupt routine names, the uart control registers. And it compiled no errors.

So I built a widget with the 324 glued in, programmed no problems.

Working: timer interrupt and soft UART (I left the original soft uart in instead of implementing the hardware one for now), the LCD text and graphics routines, including character and image definitions in progmem, the menu system driven from strings in progmem.

Unknown as yet - whether the hardware around the ISO9141 interface is working correctly though it does appear to be at first sight.

Not working: if the ISO9141 is short circuited in this application, a particular set of error messages should be displayed. I'm seeing the wrong ones... very very confusing. Nor am I seeing the structure of the error screen I expect.

My head hurts!

Neil

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

I pretty much painlessly have jumped around Mega16/Mega32/Mega644/Mega324P/Mega164P/Mega644P in various designs. New spins use the '4 series. Chip shortages early on required rebuilds for a different model.

I suppose as it is a different die/feature size it >>could<< be noise problems that did not affect the '32.

Carefully go over the migration document again to see if an I/O bit was missed.

Recheck the fuse settings as those are quite different. Clock, JTAG, brownout.

I think the watchdog scaling is different as well.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Kind of hard to say... but was your code using any hard-coded addresses for messages, or any other kind of constant data?

I would also first re-compile your project under the new version of WinAVR for the old processor, and make sure everything still works. This way you can separate bugs introduced from the new tool-chain and bugs introduced from the port from one AVR to another.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Oh, yeah, I could think of something. The venerable '32 had a simple I/O map, and many/most registers could be reached with SBI/CBI, or IN/OUT. IN/OUT should be a wash with LDS/STS except for code size and an extra cycle.

But let's say you did an SBI in an ISR on the '32, and now that port is not reachable. The mainline might have "IOREG |= 2;" and that is now not atomic, can be interrupted, and will cause loss of hair. So examine all I/O operations especially those in ISRs.

A very quick scan shows that these peripherals moved from "low" to "high": both USARTs, TWI, timer2, timer1, ADC.

I'll bet a cold one that I got it.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Yep, I'll try it with the old code with the new compiler and see what happens. The old code compiles to a larger executable...

One odd thing, the code interface is a simple send-a-byte, expect-a-byte; I'm sending in sequence 0x0f, 0x10, 0x11; and it looks like I'm getting 0x0a, 0x0d, 0x0f back... I wonder if something's sticking a crlf in there that shouldn't be? I think I need to do some experimentation, I think.

Tomorrow, perhaps.

Neil

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

Did you recompile the code with the right compiler settings (e.g. -mmcu) for the 324 ? ... and also the before mentioned hardcoded ressource accesses me come to mind.

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

Don't know - and can't see the compiler from this work machine - but used the standard options on the AVR studio to produce 324 code. There are no hard coded addresses except mapped to ports, and they're all properly addressed as PORTB or similar.

It's odd that so much works and yet this bit doesn't.

I think I need to do some testing; break into the main loop and replace it with first a return of the character from the soft uart (though I'm confident that's working, else the menu system would break) and then rx from the soft uart, tx to uart0, rx uart0, tx soft uart and see what that does.

Wonder if the FIFO is bigger on the 324? Doesn't look it, but I suppose it could be. And I don't see a separate baud rate control for the input and output side.

Lee, there are only two places where I do IO in interrupts. One is the soft UART, which appears to be working, and one is the UART1 RX routine; read only. I never write to those addresses after initial setup.

Neil

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

Well, here's a funny thing: compile the old code for the current AVR, and it breaks the code in exactly the same way. That's on an M32, so it's not the processor differences; it's something to do with the compiler.

Ho hum...

Right, the original M32 code is compiled with -Os. That blows up when I compile on the 2008 AVR with -Os. With -O0, however, it does work correctly - though noticeably slower and 25% larger; it comes out at 0x793b bytes instead of 0x5fc3 bytes. The slowness is most noticeable in the screen writes - it replaces (-Os)

	PORTA |= _BV(wr);
    15c8:	de 9a       	sbi	0x1b, 6	; 27

with (-O0)

	PORTA |= _BV(wr);
    161a:	ab e3       	ldi	r26, 0x3B	; 59
    161c:	b0 e0       	ldi	r27, 0x00	; 0
    161e:	eb e3       	ldi	r30, 0x3B	; 59
    1620:	f0 e0       	ldi	r31, 0x00	; 0
    1622:	80 81       	ld	r24, Z
    1624:	80 64       	ori	r24, 0x40	; 64
    1626:	8c 93       	st	X, r24

and there's quite a lot of those in the main loop.

Haven't tried with the new processor yet.

Neil

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

hmmm, blows up with -Os??? Sure sounds like a shared variable, or "do nothing" delay loop problem.

Make sure all your variables that are shared between ISR's and your main code are declared as volatile. And that you use the delay routines from avr-libc instead of trying to roll your own.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

There's only one do-nothing delay; that's in the lcd screen initialisation and uses delay.h. All other timings - they have to be quite tight to meet comms specs - are interrupt driven from the external crystal.

Still sniffing around...

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

Here's the code for the hardware UART - I'd forgotten to make the read and write pointers volatile, but making them so has not changed the error.

// hardware uart external registers

static volatile uint8_t next_read_1;
static volatile uint8_t next_write_1;	// the pointers to the receive1 buffer

static unsigned char buffer1[BUFFERSIZE];
								// circular buffer for receiver 1

SIGNAL (SIG_UART_RECV)
{
	// the interrupt signal when a byte is received by the uart
	// store the data into the buffer
	buffer1[next_write_1++] = UDR;

	if (next_write_1 == BUFFERSIZE)
		next_write_1 = 0;
}

void ser1_init (int rate)
{
	// wake up the serial port, set the baud rate according to the 
	// specified rate, and enable the data receive complete interrupt
	// rather than specifying a baud rate, rate = the divisor sent to the 
	// UBRRL and UBRRH registers
	
	UBRRH = (unsigned char) (rate>>8);
	UBRRL = (unsigned char) (rate & 0xff);
	
	// make sure we're not in double speed or mpm mode
	
	UCSRA = 0;
	
	// enable tx and rx and rx interrupt
	UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
	
	// set format - eight bit one stop no parity
	UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);
	
	// clear the buffer counters
	next_write_1 = 0;
	next_read_1 = 0;
}

void ser1_out (char ch)
{
	// output the character to the software serial port, waiting until the output

	// wait till buffer is available
	while (!(UCSRA & (1<<UDRE)));
	
	// then send the data
	UDR = ch;
	
}

void ser1_print (char * text)
{
int q;

	// print a string of characters to the soft uart
	for (q=0; q
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

it's something to do with the compiler.

Neil,

In that case I hope you don't mind if I move this to the GCC forum before the GCC police arrive?

By the way, do you not have access to a Dragon or JTAGICEmkII. I can't help thinking it'd really help to track this down. If you send me a PM I'll happily loan you one of mine.

Cliff

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

Two things to consider: a lot of bugs have been shaken out with GCC 4.3.x
lately. If you want to continue with that version, use Eric's release candidate
for the next WinAVR version instead of the 200812xx version.

Another option is to go back to GCC 4.2.x for a try. IIRC, that corresponds
to WinAVR version 200712xx. If you don't want to install such an old version,
feel free to email me the complete sourcecode (I promise you to not hand it
to anyone else), and I'll compile it on your behalf with a GCC 4.2.x version.
I'm on Unix where I can afford it to install both compiler versions in
parallel :), just call'em by a different name.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Thanks for the offer, Cliff - I don't have either of those tools but unfortunately the JTAG ports are busy driving the LCD.

Jörg, thanks for the offer. Let me have another think about this and perhaps some further experimentation. I have - I suspect - a working version, and it wouldn't be too much trouble to sort out the slow screen writes with an asm section, possibly inlined. My main urge at the moment is to have something usable so I can still provide widgets, and obviously at the moment I wouldn't extend to the second serial port while there's still an issue.

But I'll bash together a test that just loops in one serial and out the other, and back, and see what happens there. If there's a difference, then I can look at the source code and see what's going on. It does look like the optimiser's eating something...

The only place I can see from my source above is that I have a possibly dangerous non-atomic comparison with a volatile variable which can be set by an interrupt routine:

char ser1_ischar (void)
{
   // returns true if there is a character available from the soft uart
   // false if there is nothing waiting
   // call ser2_in after this to retrieve the data
   
   if (next_read_1 != next_write_1)
      return 1;
   else
      return 0;
} 

which should probably be

char ser1_ischar (void)
{
   cli;
   if (next_read_1 != next_write_1)
   {
      sei;
      return 1;
   }
   else
   {
      sei;
      return 0;
   }
} 

Neil

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

For complete safety, it should be:

char ser1_ischar (void)
{
   uint8_t old_sreg = SREG;
   cli;
   if (next_read_1 != next_write_1)
   {
      SREG = old_sreg;
      return 1;
   }
   else
   {
      SREG = old_sreg;
      return 0;
   }
} 

Or alternatively:

char ser1_ischar (void)
{
   uint8_t old_sreg = SREG;
   register char retvalue;
   cli;
   retvalue = (next_read_1 != next_write_1 ? 1 : 0);
   SREG = old_sreg;
   return retvalue;
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You got me - why is that necessary? Doesn't the ISR preserve the SREG?

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

The ISR does... That function is not he ISR.

What Eric proposes preserves the state if the I bit in SREG... this way the routine can be called regardless of whether the interrupts are enabled or disabled at the time of the call.

The way you had it, interrupts would be enabled whenever the function exits, regardless of what their state was when you called. Which could lead to unforeseen problems if calling it from a larger block of code that is running with the assumption that interrupts are disabled.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Neil You can get older winavr versions here
http://sourceforge.net/project/s...

And the copy i have locally on my PC has the following MD5 SUM
50f8ecbc22b70060174b1e57dc44a6f9 WinAVR-20070122-install.exe

Calculated with this little excellent proggie that integrates in the Win-Explorer
DigestIt 2004 - http://www.colonywest.us/index.p...

edit: oopzz it was 1201 not 0122 that was the real version , sorry

/Bingo

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

Ah, of course, Glitch. The interrupts are enabled all the time. There's an interrupt every 800 clocks or so for the soft UART and internal timers, and one from the hardware UART. So the SEI() is the desired exit from either path on that routine.

My concern was that there *could* be an interrupt between getting next_read_1 and comparing it with next_write_1 - though even that shouldn't have been a show-stopper, since it would detect the difference next time it was called.

Neil

edit: the interrupts are enabled all the time except when there is an interrupt already happening.

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

If interrupts are guaranteed to be enabled whenever the call to that function is made... then your way is fine.

Eric's way just makes it bomb-proof for any future updates, when someone accidentally calls it from within an atomic block of code.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

As your next_read_1 and next_write_1 variables are uint8_t, accessing
them ought to be atomic anyway.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Update from last night - I'm using only the 324 at the moment; I had to post the M32 unit out to a customer and I haven't had time to build another one yet!

Chopped out huge swathes of the original code and left only the ser2_ (software) and ser1_ (hardware) uarts running.

After initialisation of ser2 at 2400 baud and ser1 at 7812 baud, I run a tight loop which checks for input from ser2 and outputs it on ser1, checks for input on ser1 and outputs it on ser2. Ser2 talks to hyperterm, ser1 has a hardware loopback.

If I type, oddly enough I get back what I typed. If I type as fast as the keyboard rollover will accept keys, I occasionally get a framing fault - I assume it to be the input of the soft uart; I don't test for it. What I don't get is the errors I saw with the same routines - but less rapidly due to main event loops, data translation, LCD output and other stuff going on - with the full software. This is the same with both -O0 and -Os so I'm totally confused.

Next plan is to do away with the soft uart - just keeping the timer interrupt which I need for timeouts and suchlike - and duplicate a new ser0_ which will replace it. First attempt last night didn't work, but I suspect I have failed to locate all the '0' and '1' references in the cut'n'pasted code.

Onwards and upwards!

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

Confusion reigns, but I have a workable solution. I have rewritten the code to replace my original soft uart with a driver for the M324's second usart, and retain only enough of the original soft uart to maintain necessary timekeeping ticks.

To summarise:

- the original code with the soft uart works with -O0
- the original code fails with -Os
- a simple loop with the soft uart works with both -O0 and -Os (that's in the soft, write to the hard, physical loop, and back the same way)
- the new code with both hardware uarts works with -O0 and -Os

So, for now, I'm testing the device in the car for some long-term viewing and we'll see what happens.

The only conclusion I can make is that the change in compiler has produced some subtle timing error that's causing the glitch. Removing the soft uart shortens the time the processor spends in interrupt routines and perhaps that's the reason.

Neil

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

Neil,

Well surely you can take a look at the generated Asm in each case to see where the subtle timing differences have occurred and then target some remdial action to repair it? (like adding NOPs or preventing/enforcing inlining or typecasting or whatever's needed)

Cliff

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

In theory, yes. But the compiled code fills the M32, and the fault is not apparent when the program is pared back to the serial routines in question. And I suspect the fault is not necessarily in those routines anyway...

Since there's no need for the soft uart routine on the 324P anyway, there doesn't seem a lot of point chasing it up. If it were still visible on the simplified programme, chasing the fault might be worth doing - but as it is?