Standalone attiny13a morse code generator - weird behavior

I am having some problems with a standalone attiny13a for generating morse code messages.
Sometimes the code fails in mysterious ways and other times it works.
Unfortunately it is uncommented.
here is the code:

#define F_CPU 128000
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <avr/io.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define FREQ(x) (1000000/((x)*2))
#define SHT 20
#define LNG (SHT*3)
#define chargap() _delay_ms(LNG)
#define space() _delay_ms(LNG*2)
unsigned char letters[] PROGMEM = {
0x5, 0x18, 0x1A, 0xC, 0x2, 0x12, 0xE,
0x10, 0x4, 0x17, 0xD, 0x14, 0x7, 0x6,
0xF, 0x16, 0x1D, 0xA, 0x8, 0x3, 0x9,  
0x11, 0xB, 0x19, 0x1B, 0x1C};
unsigned char numbers[] PROGMEM = {0x3F, 0x2F, 0x27, 0x23, 0x21, 0x20, 0x30, 0x38, 0x3C, 0x3E};  
void dash(){
	int c;
	for(c = 0;c < LNG;c++){
	PORTB ^= 0x18;
	_delay_ms(1);
	}
	_delay_ms(SHT);
	PORTB = 0;
}
void dot(){
	int c;
	for(c = 0;c < SHT;c++){
	PORTB ^= 0x18;
	_delay_ms(1);
	}
	_delay_ms(SHT);
	PORTB = 0;
}
void decodemorse(unsigned char byte){
	unsigned char output = 0;
	char bit;
	for(bit = 7;bit>-1;bit--){
		if(byte & (1 << bit)){
			if(output) {dash();}	
			if(!output){output = 1;}
		}
		else if(output){dot();}
	}
}
void txstring(char *s){
	int c;
	unsigned char code,ch;
	for(c = 0;c < strlen(s);c++){
		ch = toupper(s[c]);
		if(isspace(ch)){space(); continue;}
		if(isalpha(ch)){code = pgm_read_byte(&letters[ch-'A']);}
		if(isdigit(ch)){code = pgm_read_byte(&numbers[ch-'0']);} 
		decodemorse(code);
		chargap();
	}
}
int main(){
	DDRB = 0x18;
	char ca[15] = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
	//int t;
	while(1){
	txstring(ca);
	_delay_ms(1000);
	}
}

If I use

char s[7]={'T', 'E', 'S', 'T', 0};
txstring(s);

it works but

char s[]="TEST";
txstring(s);

or

txstring("TEST");

does not work.
I cannot figure it out.
I may come and post more info when its not 10min till bed time :wink:

does not work

?

When you specify a string such as "TEST" it might be being put into a different dataspace and require special access (like PROGMEM on the Arduino chips) than if it is initialized character by character. Just a wild guess - I've never used one.

Pete

LOL I just read my own post. I must have been very tired when I wrote it.
I am using winavr with GCC version 4.3.3.
When it does not work, it has no output or sends a string of "T T T T T" in morse.
I am programming the attiny with an Arduino with a modified arduinoISP sketch.
I cannot understand this odd behavior. Possibly running out of memory? (the at13 only has 64 bytes) But why would it work when assigned byte by byte? also

char s[5]={'T', 'E', 'S', 'T', 0};

does not work

char s[6]={'T', 'E', 'S', 'T', 0};

does.
Almost like there is a spurious character added.
Is this a bug in avr-gcc?
BTW PROGMEM is avr-libc specific and not Arduino specific.

Possibly running out of memory?

That's my suspicion.

But why would it work when assigned byte by byte?

That's the part that doesn't fit.

Do you want the application to work or do you want to know why it does not work?

modified arduinoISP sketch

Modified in what way?

Me too.

But why would it work when assigned byte by byte?

That's the part that doesn't fit.

I thought the same thing.

Do you want the application to work or do you want to know why it does not work?

Well...both.
I want to know why it does not work since it is a strange bug and knowing why it doesn't work often leads to a solution.

modified arduinoISP sketch

Modified in what way?

Modified to use slow software spi
source: http://pastebin.com/EaBHienB
The batch file to build and upload it:

avr-gcc -mmcu=attiny13a morse1.c -Os -lm
avr-objcopy -j .text -O ihex a.out morse1.hex
avrdude -P com4 -pt13 -b 19200  -c avrisp -v -U flash:w:morse1.hex

and the compile output:

C:\WinAVR-20100110>avr-gcc -mmcu=attiny13a morse1.c -Os -lm

C:\WinAVR-20100110>avr-objcopy -j .text -O ihex a.out morse1.hex

C:\WinAVR-20100110>avrdude -P com4 -pt13 -b 19200  -c avrisp -v -U flash:w:morse1.hex

avrdude: Version 5.10, compiled on Jan 19 2010 at 10:45:23
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "C:\WinAVR-20100110\bin\avrdude.conf"


         Using Port                    : com4
         Using Programmer              : avrisp
         Overriding Baud Rate          : 19200
         AVR Part                      : ATtiny13
         Chip Erase delay              : 4000 us
         PAGEL                         : P00
         BS2                           : P00
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page
      Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  Max
W   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ---
-- ---------
           eeprom        65     5     4    0 no         64    4      0  4000  40
00 0xff 0xff
           flash         65     6    32    0 yes      1024   32     32  4500  45
00 0xff 0xff
           signature      0     0     0    0 no          3    0      0     0
 0 0x00 0x00
           lock           0     0     0    0 no          1    0      0  4500  45
00 0x00 0x00
           calibration    0     0     0    0 no          2    0      0     0
 0 0x00 0x00
           lfuse          0     0     0    0 no          1    0      0  4500  45
00 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  4500  45
00 0x00 0x00

         Programmer Type : STK500
         Description     : Atmel AVR ISP
         Hardware Version: 2
         Firmware Version: 1.18
         Topcard         : Unknown
         Vtarget         : 0.0 V
         Varef           : 0.0 V
         Oscillator      : Off
         SCK period      : 0.1 us

avrdude: please define PAGEL and BS2 signals in the configuration file for part
ATtiny13
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.14s

avrdude: Device signature = 0x1e9007
avrdude: safemode: lfuse reads as 7B
avrdude: safemode: hfuse reads as FF
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed

         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: please define PAGEL and BS2 signals in the configuration file for part
ATtiny13
avrdude: reading input file "morse1.hex"
avrdude: input file morse1.hex auto detected as Intel Hex
avrdude: writing flash (552 bytes):

Writing | ################################################## | 100% 6.73s

avrdude: 552 bytes of flash written
avrdude: verifying flash memory against morse1.hex:
avrdude: load data flash data from input file morse1.hex:
avrdude: input file morse1.hex auto detected as Intel Hex
avrdude: input file morse1.hex contains 552 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 5.43s

avrdude: verifying ...
avrdude: 552 bytes of flash verified

avrdude: safemode: lfuse reads as 7B
avrdude: safemode: hfuse reads as FF
avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Modified in what way? Modified to use slow software spi. #define F_CPU 128000

Ah. Got it.

http://pastebin.com/EaBHienB

No copyright for your contribution. Does that mean you release your contribution under the same license as Randall Bohn (New BSD License)?

Do you want the application to work or do you want to know why it does not work? Well...both.

We better get started...

I built your program using the Arduino IDE and then ran the ELF file through avr-objdump. Getting into main burns 2 bytes of SRAM (return address). main pushes 6 registers onto the stack. main calls txstring which pushes 12 registers onto the stack (2 bytes for the call). Calling toupper takes 2 more bytes. Total stack depth at that point is 24 bytes.

isspace are isalpha are the same depth (24 bytes) as toupper.

decodemorse adds 5 more registers to the stack bringing the depth at that pont to 29 bytes (22+2+5). Calling dash or dot adds 2 more bytes. Maximum depth so far is 31 bytes.

chargap is inlined (no additional depth).

main adds 15 bytes to the stack for ca for a maximum depth of 46 (31+15).

So, if the project is built with the Arduino IDE and I analyzed the assembly correctly, there is probably enough memory for the application.

On a side-note, it looks like modifying the stack pointer is not properly protected. I think the instructions at 1ac and 1ae should be swapped. But that won't effect your application.

 1a0:	cd b7       	in	r28, 0x3d	; 61
 1a2:	de b7       	in	r29, 0x3e	; 62
 1a4:	2f 97       	sbiw	r28, 0x0f	; 15
 1a6:	0f b6       	in	r0, 0x3f	; 63
 1a8:	f8 94       	cli
 1aa:	de bf       	out	0x3e, r29	; 62
 1ac:	0f be       	out	0x3f, r0	; 63
 1ae:	cd bf       	out	0x3d, r28	; 61

Not too worried about it.

Do you want the application to work or do you want to know why it does not work? Well...both.

We better get started...

I built your program using the Arduino IDE and then ran the ELF file through avr-objdump. Getting into main burns 2 bytes of SRAM (return address). main pushes 6 registers onto the stack. main calls txstring which pushes 12 registers onto the stack (2 bytes for the call). Calling toupper takes 2 more bytes. Total stack depth at that point is 24 bytes.

isspace are isalpha are the same depth (24 bytes) as toupper.

decodemorse adds 5 more registers to the stack bringing the depth at that pont to 29 bytes (22+2+5). Calling dash or dot adds 2 more bytes. Maximum depth so far is 31 bytes.

chargap is inlined (no additional depth).

main adds 15 bytes to the stack for ca for a maximum depth of 46 (31+15).

So, if the project is built with the Arduino IDE and I analyzed the assembly correctly, there is probably enough memory for the application.

This agrees with my quick 'n dirty estimate of about 50 bytes. Should be enough.
The copy you analyzed does work but when I put it in a fixed string instead of byte - by - byte then it fails.
Here is a diff if the asm output from a working vs non-working version:

242a243,246
> 	.data
> .LC0:
> 	.string	"TEST"
> 	.text
246,250d249
< 	push r14
< 	push r15
< 	push r16
< 	push r17
< 	push r29
252,259c251
< 	in r28,__SP_L__
< 	in r29,__SP_H__
< 	sbiw r28,15
< 	in __tmp_reg__,__SREG__
< 	cli
< 	out __SP_H__,r29
< 	out __SREG__,__tmp_reg__
< 	out __SP_L__,r28
---
> 	push r29
261c253
< /* frame size = 15 */
---
> /* frame size = 0 */
264,280c256,257
< 	movw r30,r28
< 	adiw r30,1
< 	ldi r24,lo8(15)
< 	movw r26,r30
< 	st X+,__zero_reg__
<         dec r24
< 	brne .-6
< 	ldi r25,lo8(84)
< 	std Y+1,r25
< 	ldi r24,lo8(69)
< 	std Y+2,r24
< 	ldi r24,lo8(83)
< 	std Y+3,r24
< 	std Y+4,r25
< 	movw r14,r30
< 	ldi r16,lo8(32000)
< 	ldi r17,hi8(32000)
---
> 	ldi r28,lo8(32000)
> 	ldi r29,hi8(32000)
282c259,260
< 	movw r24,r14
---
> 	ldi r24,lo8(.LC0)
> 	ldi r25,hi8(.LC0)
284c262
< 	movw r24,r16
---
> 	movw r24,r28
337a316
> .global __do_copy_data

Here is the (i think) relevant segment of asm from the non-working one.

.LC0:
	.string	"TEST"
	.text
.global	main
	.type	main, @function
main:
	push r28
	push r29
/* prologue: function */
/* frame size = 0 */
	ldi r24,lo8(24)
	out 55-32,r24
	ldi r28,lo8(32000)
	ldi r29,hi8(32000)
.L27:
	ldi r24,lo8(.LC0)
	ldi r25,hi8(.LC0)
	rcall txstring
	movw r24,r28

I am no master at AVR ASM but it looks like it tries to pass the address from flash directly but please correct me if I am wrong.
If you cannot understand diff output I can post the whole thing.
Thank you very much for the help on this confusing problem.

The copy you analyzed does work but when I put it in a fixed string instead of byte - by - byte then it fails.

The only significant difference between the two is that this...

char s[]="TEST";

...stores the string in SRAM twice (one copy in the data segment, the other copy on the stack) but this...

char ca[5] = {'T', 'e', 's', 't', 0};

...stores the string in SRAM once (one copy on the stack). It's a side-effect of the way the compiler generates the code to initialize the variable.

I am no master at AVR ASM but it looks like it tries to pass the address from flash directly but please correct me if I am wrong.

The structure is accurate (initialize two registers then call). It's impossible to say if it is or is not correct without more context.

But, that does raise the possibility that a difference in compiler switches may prevent me from identifying the problem. I'll be back in a few minutes...

Please try this one.

sketch_dec30a.cpp.hex (1.56 KB)

That one works perfectly. Which code was used?

The only significant difference between the two is that this...

char s[]="TEST";

...stores the string in SRAM twice (one copy in the data segment, the other copy on the stack) but this...

char ca[5] = {'T', 'e', 's', 't', 0};

...stores the string in SRAM once (one copy on the stack). It's a side-effect of the way the compiler generates the code to initialize the variable.

BTW, Neither of those examples work. Only if you increase the 5 to 6 does it work.
This is a very strange bug.

Sorry about that. This one...

int main(){
	DDRB = 0x18;
        char s[]="TEST";
//	char ca[15] = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};
	//int t;
	while(1){
//	txstring(ca);
        txstring(s);
	_delay_ms(1000);
	}
}

I'm using WinAVR-20100110. Which AVR toolset are you using?

I am also using WinAVR-20100110.
When I compile the code it does not work.
Also, your compiled version is 560 bytes in flash but my version is 548 bytes.
I wonder what would account for the discrepancy?
Thanks for the help. This problem really makes no sense to me.

That one works perfectly.

To add to the confusion, I accidentally built it for 1.2 MHz instead of 128 KHz.

I'm using Arduino 1.0 IDE. This is the verbose...

C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=attiny13 -DF_CPU=1200000L -DARDUINO=100 -IC:\Projects\Arduino\Sketch\hardware\tiny2\cores\empty C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp -oC:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.o 
Forum_85026.cpp:1:1: warning: "F_CPU" redefined
<command-line>: warning: this is the location of the previous definition
Forum_85026.cpp:19: warning: only initialized variables can be placed into program memory area
Forum_85026.cpp:24: warning: only initialized variables can be placed into program memory area
Forum_85026.cpp: In function 'void txstring(char*)':
Forum_85026.cpp:57: warning: comparison between signed and unsigned integer expressions
Forum_85026.cpp:56: warning: 'code' may be used uninitialized in this function
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=attiny13 -DF_CPU=1200000L -DARDUINO=100 -IC:\Projects\Arduino\Sketch\hardware\tiny2\cores\empty C:\Projects\Arduino\Sketch\hardware\tiny2\cores\empty\main.cpp -oC:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\main.cpp.o 
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-ar rcs C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\core.a C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\main.cpp.o 
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-gcc -Os -Wl,--gc-sections -mmcu=attiny13 -o C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.elf C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.o C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\core.a -LC:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp -lm 
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.elf C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.eep 
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-objcopy -O ihex -R .eeprom C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.elf C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.hex 
Binary sketch size: 560 bytes (of a 1024 byte maximum)

If you can build an ELF file, attach it to a post and I'll compare.

It seemed to run at the correct frequency.

I'm using Arduino 1.0 IDE. This is the verbose...

C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=attiny13 -DF_CPU=1200000L -DARDUINO=100 -IC:\Projects\Arduino\Sketch\hardware\tiny2\cores\empty C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp -oC:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.o 

Forum_85026.cpp:1:1: warning: "F_CPU" redefined
: warning: this is the location of the previous definition
Forum_85026.cpp:19: warning: only initialized variables can be placed into program memory area
Forum_85026.cpp:24: warning: only initialized variables can be placed into program memory area
Forum_85026.cpp: In function 'void txstring(char*)':
Forum_85026.cpp:57: warning: comparison between signed and unsigned integer expressions
Forum_85026.cpp:56: warning: 'code' may be used uninitialized in this function
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=attiny13 -DF_CPU=1200000L -DARDUINO=100 -IC:\Projects\Arduino\Sketch\hardware\tiny2\cores\empty C:\Projects\Arduino\Sketch\hardware\tiny2\cores\empty\main.cpp -oC:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\main.cpp.o
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-ar rcs C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\core.a C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\main.cpp.o
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-gcc -Os -Wl,--gc-sections -mmcu=attiny13 -o C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.elf C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.o C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\core.a -LC:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp -lm
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.elf C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.eep
C:\Arduino\arduino-1.0\hardware\tools\avr\bin\avr-objcopy -O ihex -R .eeprom C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.elf C:\Users\BRIANC~1.001\AppData\Local\Temp\build9125599963205406663.tmp\Forum_85026.cpp.hex
Binary sketch size: 560 bytes (of a 1024 byte maximum)

Some things I have noticed is:
There is alot more compiler options and compile steps
It was built with the C++ compiler but I am using the C compiler.

If you can build an ELF file, attach it to a post and I'll compare.

Elf attached

morse1.elf (2.45 KB)

It seemed to run at the correct frequency.

Oh man! I should have gotten some sleep before writing that. The #define at the top of the file overrides whatever I have selected. The HEX file I sent was built for 128 KHz.

Attached are the two ELF files converted to assembly (the "cpp" extension just makes it faster for me to load them in Visual Studio). As far as I can tell, the only difference is in txstring. Several times I've looked carefully through the two versions of txstring and I cannot find anything that would explain the problem. At this point I can only suggest either using the Arduino IDE or stealing the significant bits from the command-lines posted earlier.

Forum_85026.cpp (11.3 KB)

morse1.cpp (10.6 KB)

If I compile with:

-fno-exceptions -ffunction-sections -fdata-sections

It works.
At this point, I suspect a avr-gcc bug.
Atleast it works with those compile options.