Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Arduino MEGA 2560 #724

Closed
kgolding opened this issue Nov 15, 2019 · 36 comments
Closed

Support Arduino MEGA 2560 #724

kgolding opened this issue Nov 15, 2019 · 36 comments
Labels
enhancement New feature or request

Comments

@kgolding
Copy link

Please add support for the Ardunio MEGA 2560 boards, as this board provides a larger number of GPIO's than most of the other boards (54 off).

https://www.arduino.cc/en/Main/arduinoBoard_Mega2560?setlang=en

@deadprogram deadprogram added the enhancement New feature or request label Nov 15, 2019
@ofauchon
Copy link
Contributor

Hi.

I found ATMega2560's declaration in tinygo sources :

tinygo/src/device/avr/atmega2560.s
tinygo/src/device/avr/atmega2560.ld
tinygo/src/device/avr/atmega2560.go

So I tried to create a new local json target "arduino-mega2560.json"

{
	"inherits": ["avr"],
	"llvm-target": "avr-atmel-none",
	"cpu": "atmega2560",
	"build-tags": ["arduino", "atmega2560", "atmega", "avr5"],
	"cflags": [
		"-mmcu=atmega2560"
	],
	"ldflags": [
		"-Wl,--defsym=_bootloader_size=512",
		"-Wl,--defsym=_stack_size=512"
	],
	"linkerscript": "src/device/avr/atmega2560.ld",
	"extra-files": [
		"targets/avr.S",
		"src/device/avr/atmega2560.s"
	],
	"flash-command": "avrdude -c arduino -p atmega256 -P {port} -U flash:w:{hex}"

... and run a simple build:

$ tinygo build -target ../arduino-mega2560.json  -o main.bin main.go
/usr/bin/avr-ld: avr:6 architecture of input file `/tmp/tinygo576914937/main.o' is incompatible with avr output
/usr/bin/avr-ld: avr:6 architecture of input file `/tmp/tinygo576914937/extra-0-avr.S.o' is incompatible with avr output
/usr/bin/avr-ld: avr:6 architecture of input file `/tmp/tinygo576914937/extra-1-atmega2560.s.o' is incompatible with avr output

Some more debugs :

$ /usr/bin/avr-ld -plugin /usr/lib/gcc/avr/9.2.0/liblto_plugin.so -plugin-
opt=/usr/lib/gcc/avr/9.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/ccQJV7Rh.res 
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc 
-o /tmp/tinygo576914937/main -L/usr/lib/tinygo -L/usr/lib/gcc/avr/9.2.0 
-L/usr/lib/gcc/avr/9.2.0/../../../../avr/lib --gc-sections --defsym=_bootloader_size=512 
--defsym=_stack_size=512 /tmp/tinygo576914937/main.o /tmp/tinygo576914937/extra-0-avr.S.o
 /tmp/tinygo576914937/extra-1-atmega2560.s.o --start-group -lgcc -lm -lc --end-group -T
targets/avr.ld -T src/device/avr/atmega2560.ld 
/usr/bin/avr-ld: avr:6 architecture of input file `/tmp/tinygo576914937/main.o' is incompatible with avr output
/usr/bin/avr-ld: avr:6 architecture of input file `/tmp/tinygo576914937/extra-0-avr.S.o' is incompatible with avr output
/usr/bin/avr-ld: avr:6 architecture of input file `/tmp/tinygo576914937/extra-1-atmega2560.s.o' is incompatible with avr output

Could you please tell us more about the error ?

Thanks

Olivier Fauchon

@aykevl
Copy link
Member

aykevl commented Jan 15, 2020

I debugged it a bit and found that you also need to pass -mmcu=atmega2560 to the linker:

    "ldflags": [
        "-Wl,--defsym=_bootloader_size=512",
        "-Wl,--defsym=_stack_size=512",
        "-mmcu=atmega2560"
    ],

It got me a bit further (I think):

/usr/lib/gcc/avr/4.9.2/../../../avr/bin/ld: address 0x800205 of /tmp/tinygo242709638/main section `.data' is not within region `RAM'
/usr/lib/gcc/avr/4.9.2/../../../avr/bin/ld: address 0x800205 of /tmp/tinygo242709638/main section `.data' is not within region `RAM'
collect2: error: ld returned 1 exit status
error: failed to link /tmp/tinygo242709638/main: exit status 1

@rbretecher
Copy link

rbretecher commented Jan 28, 2020

Hello,

I've been following this thread for a while and as I actually have the Mega 2560 board, I wanted to try to make tinygo work on it :)

After few tries, I actually managed to compile and flash my Mega 2560 with the examples/serial example 🎉
Thanks to the Arduino serial monitoring tool I was able to see the "hello world!" message.

serial

I'm fairly new to the Arduino world so it's more likely that what I did is not correct and I'm sure it can be improved a lot !. Also, I did not change much things and I have to say that you guys did all the work, here is what's different :

  • I read that mega2560 architecture is avr6 so I replaced avr5 in build-tags with avr6. Also updated cflags/ldflags with -mmcu=avr6.
  • Updated flash command with -c wiring -b 115200 and also used the -D option as I was getting this error without it -> 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: stk500v2_command(): command failed

My arduino-mega2560.json target look like this :

{ 
   "inherits":[ 
      "avr"
   ],
   "llvm-target":"avr-atmel-none",
   "cpu":"atmega2560",
   "build-tags":[ 
      "arduino",
      "atmega2560",
      "atmega",
      "avr6"
   ],
   "cflags":[ 
      "-mmcu=avr6"
   ],
   "ldflags":[ 
      "-Wl,--defsym=_bootloader_size=512",
      "-Wl,--defsym=_stack_size=512",
      "-mmcu=avr6"
   ],
   "linkerscript":"src/device/avr/atmega2560.ld",
   "extra-files":[ 
      "targets/avr.S",
      "src/device/avr/atmega2560.s"
   ],
   "flash-command":"avrdude -c wiring -b 115200 -p atmega2560 -P {port} -U flash:w:{hex} -v -D"
}

Build works great :

$ tinygo build -target targets/arduino-mega2560.json -o serial.bin examples/serial

And flash as well :

$ tinygo flash -target targets/arduino-mega2560.json examples/serial

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e9801 (probably m2560)
avrdude: reading input file "/var/folders/pk/z89yjc3d7_z3fkzpzf4_2mcc0000gn/T/tinygo569696274/main.hex"
avrdude: input file /var/folders/pk/z89yjc3d7_z3fkzpzf4_2mcc0000gn/T/tinygo569696274/main.hex auto detected as Intel Hex
avrdude: writing flash (646 bytes):

Writing | ################################################## | 100% 0.12s

avrdude: 646 bytes of flash written
avrdude: verifying flash memory against /var/folders/pk/z89yjc3d7_z3fkzpzf4_2mcc0000gn/T/tinygo569696274/main.hex:
avrdude: load data flash data from input file /var/folders/pk/z89yjc3d7_z3fkzpzf4_2mcc0000gn/T/tinygo569696274/main.hex:
avrdude: input file /var/folders/pk/z89yjc3d7_z3fkzpzf4_2mcc0000gn/T/tinygo569696274/main.hex auto detected as Intel Hex
avrdude: input file /var/folders/pk/z89yjc3d7_z3fkzpzf4_2mcc0000gn/T/tinygo569696274/main.hex contains 646 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.10s

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

avrdude done.  Thank you.

I will make some tests to see how we can do more with this. Next step is to be able to control a LED 😃

@aykevl
Copy link
Member

aykevl commented Jan 30, 2020

That is some amazing progress! Looks like you're getting very close.

To define your own pin mapping, you can change the arduino build tag to arduino_mega2560 and create a new src/machine/board_arduino_mega2560.go with a pin mapping for this specific board.

@tmclane
Copy link
Contributor

tmclane commented Feb 4, 2020

This interests me as well. Any more progress? Is your work up in a branch somewhere?

@rbretecher
Copy link

rbretecher commented Feb 5, 2020

I made more tests but except the serial communication, I could not find any way to do anything else.

I did what @aykevl advised me and created a dedicated board file like this one (also updated build tags so that it uses it instead of the arduino one) :

// +build atmega2560

package machine

// Return the current CPU frequency in hertz.
func CPUFrequency() uint32 {
	return 16000000
}

// LED on the Arduino
const LED Pin = 13

The examples/blinky1 compiles well but then nothing is happening when I'm flashing the board whereas the built-in LED should blink.

Am I supposed to do anything else for pin mapping ?

@tmclane
Copy link
Contributor

tmclane commented Feb 5, 2020

I can't get serial communication to work either. I did create a pin mapping file the same as you and was attempting to populate it. I do run into an issue with an invalid reference:

$ ./build/tinygo flash -target targets/arduino-mega2560.json examples/serial
# machine
src/machine/machine_atmega.go:237:20: IRQ_USART_RX not declared by package avr

I was fixing that manually by changing it to IRQ_USART0_RX which lets it compile.
The file ./lib/avr/packs/atmega/ATmega2560.atdf generates USARTs 0, 1, 2 and so there is no bare IRQ_USART_RX defined.

I am positive this is the wrong fix for this issue. I believe we may need to create a src/machine/board_arduino_mega2560.go as well which defines the actual pinout.

@tmclane
Copy link
Contributor

tmclane commented Feb 6, 2020

FYI: I pushed my work up to my fork here: https://github.com/tmclane/tinygo/tree/feature/mega2560

I ended up creating alternate files and putting build flags in them such that the compile error doesn't happen any more with IRQ_USART0_RX being not defined.

Programs will now compile and flash but don't seem to execute on my particular mega2560. Perhaps it is a clone issue?

@aykevl
Copy link
Member

aykevl commented Feb 8, 2020

I ended up creating alternate files and putting build flags in them such that the compile error doesn't happen any more with IRQ_USART0_RX being not defined.

The best way to fix that right now is to make two new files machine_atmega328p.go and machine_atmega2560.go that define the interrupt number as a constant:

// +build atmega328p
package machine
const uartInterrupt = avr.IRQ_USART_RX
// +build atmega2560
package machine
const uartInterrupt = avr.IRQ_USART0_RX

and then you can use that constant in machine_atmega.go.

@tmclane
Copy link
Contributor

tmclane commented Feb 8, 2020

Thanks.. I have updated my branch with your suggestion.

I've never done any low-level hardware coding like this. How do I figure out what is missing / wrong?

What's the best way to get started?

@aykevl
Copy link
Member

aykevl commented Feb 8, 2020

I've never done any low-level hardware coding like this. How do I figure out what is missing / wrong?

I can't give a single answer to that. But my usual approach to this is to reduce as far as possible, and start with a known-good state (just like any other bug, really).
My usual approach to bring up hardware is to write to an LED directly. So you could insert some code in src/runtime/runtime_avr.go in the main function, that's the first Go code that's executed (before anything has been initialized, like globals). You can try enabling a pin directly that way:

// untested
avr.DDRB.Set(1 << uint8(5))
avr.PORTB.Set(1 << uint8(5))

In this case, it looks like @rbretecher actually got serial working, so if you can get that to work as well you could start from there.

@tmclane
Copy link
Contributor

tmclane commented Feb 9, 2020

I don't get anything from the serial example although it does return values for my arduino nano 328p.
I pushed my code hoping others would do so as well.

How do you figure the offsets for those DDRB and PORTB?
Is that from the atdf file? That's where I am confused as to how to compute the proper values.

@tmclane
Copy link
Contributor

tmclane commented Feb 9, 2020

Ah looks like the default Baud rate is actually 57600 instead of the expected 9600.

@aykevl
Copy link
Member

aykevl commented Feb 9, 2020

How do you figure the offsets for those DDRB and PORTB?
Is that from the atdf file? That's where I am confused as to how to compute the proper values.

You mean 1 << uint8(5)? That is to set the fifth bit (starting from 0) of the register. In this case, I checked the existing TinyGo source (in machine_avr.go and machine_atmega.go) that configures and sets a pin - assuming the pin is on PB5 (as explained here).

But checking the internet (https://www.exp-tech.de/blog/arduino-mega-2560-pinbelegung) it looks like my guess was wrong. The built-in LED is actually on PB7, which means the 7th bit on port B:

// untested
avr.DDRB.SetBits(1 << uint8(7))
avr.PORTB.SetBits(1 << uint8(7))

Actually, you probably should turn the output low to turn the LED on:

// untested
avr.DDRB.SetBits(1 << uint8(7))
avr.PORTB.ClearBits(1 << uint8(7))

@tmclane
Copy link
Contributor

tmclane commented Feb 9, 2020

Ah ok so that's how those PB# and other designations work. I was looking everywhere for an actual mapping from 13 -> 26 since that's the actual pin on the chip.

I knew there was something basic I was missing.

@tmclane
Copy link
Contributor

tmclane commented Feb 9, 2020

Thank you!

@tmclane
Copy link
Contributor

tmclane commented Feb 9, 2020

Looks like the runtime_avr.go initAll() function call is what is stopping serial output. Disabling that enables it to print to serial. I noticed also that the time.Sleep call doesn't actually seem to sleep for the time period specified i.e. I set it to 2 seconds and it still seems to output the hello world! message every second.

//go:export main
func main() {
	preinit()
	//	initAll()
	postinit()
	callMain()
	abort()
}

I see where initAll() is defined in runtime.go and it has a message stating that the compiler will fill this in with calls to the initialization function for each package.
How do I locate the one for the avr platform?

@aykevl
Copy link
Member

aykevl commented Feb 9, 2020

How do I locate the one for the avr platform?

It's the combination of all the init functions. There is no exact way to know it but grepping may help and disabling (or renaming) them one by one until it starts working - then you know which init function is to blame.

@tmclane
Copy link
Contributor

tmclane commented Feb 10, 2020

Ok. Actually I think my board might be restarting after it does a println actually.

I put two in a row in the serial example and it never prints the second one.
That would be why the time.Sleep call doesn't work either I expect.

@tmclane
Copy link
Contributor

tmclane commented Feb 12, 2020

@rbretecher Does your board print more than one println message in a sketch? Or is it printing the first one and then restarting giving the appearance of it sleeping for one second and repeating?

@rbretecher
Copy link

Sorry for the late reply.

Indeed, I have the same problem, only the first println instruction works. Is there any way to debug this and see what could be the issue ?

@aykevl
Copy link
Member

aykevl commented Feb 29, 2020

It's hard to say what is going on without using a debugger. And besides that good debuggers are hard to get for AVR, the LLVM AVR backend doesn't support it either due to a bug (fixed in master and part of LLVM 11 to be released somewhere around October). Once this fix lands, it may be possible to debug it in simavr.

My usual strategy is trying to reduce it to the bare minimum. -opt=1 might be useful, if it works at all. And then fiddling around with it until it hopefully starts working.
I wouldn't be surprised if this is a backend bug: I don't think devices with such large address spaces have been tested much in LLVM.

@aykevl
Copy link
Member

aykevl commented Mar 10, 2020

I have a local branch that adds support for the ATmega2560 chip, based on #946. You can in fact look at that branch, the changes for the ATmega2560 are pretty trivial (replace the MCU with atmega2560 and avr51 to avr6).

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

I'll give it a look and a try.

The link to your branch appears to just link to the same PR :)

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

I think I understand the changes you mean. I'm going to try to do them myself and see if I am correct when you update your link.

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

Lo and behold it works and doesn't reboot now after doing a Serial.println.

Hallelujah! Thanks for the help! Maybe I can figure out how to make it blink now.

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

My branch: https://github.com/tmclane/tinygo/tree/feature/mega2560 was updated.

Flashing it with command:

./build/tinygo flash -target targets/atmega2560.json examples/serial

Made this change to examples/serial:

diff --git a/src/examples/serial/serial.go b/src/examples/serial/serial.go
index b0dd403..c96c869 100644
--- a/src/examples/serial/serial.go
+++ b/src/examples/serial/serial.go
@@ -3,8 +3,11 @@ package main
 import "time"
 
 func main() {
+       i := 0
        for {
-               println("hello world!")
+               println(i)
                time.Sleep(time.Second)
+               println("hello world! after sleep ")
+               i += 1
        }
 }

Results in:

0
hello world! after sleep 
1
hello world! after sleep 
2
hello world! after sleep 
3

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

And blinking works if I use the PORTB directly as suggested.

package main

import (
	"device/avr"
	"time"
)

func main() {
	avr.DDRB.SetBits(1 << uint8(7))

	i := 0
	for {
		time.Sleep(time.Second)
		i += 1
		if i%2 == 0 {
			avr.PORTB.ClearBits(1 << uint8(7))
		} else {
			avr.PORTB.SetBits(1 << uint8(7))
		}
	}
}

@aykevl
Copy link
Member

aykevl commented Mar 10, 2020

Cool!
You can try creating a new src/machine/board_arduino-mega2560.go with a pin mapping similar to src/machine/board_arduino.go.

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

The offset defined in the machine_atmega.go is wrong:

// Configure sets the pin to input or output.
func (p Pin) Configure(config PinConfig) {
	if config.Mode == PinOutput { // set output bit
		if p < 8 {
			avr.DDRD.SetBits(1 << uint8(p))
		} else if p < 14 {
			avr.DDRB.SetBits(1 << uint8(p-8))
		} else {
			avr.DDRC.SetBits(1 << uint8(p-14))
		}
	} else { // configure input: clear output bit
		if p < 8 {
			avr.DDRD.ClearBits(1 << uint8(p))
		} else if p < 14 {
			avr.DDRB.ClearBits(1 << uint8(p-8))
		} else {
			avr.DDRC.ClearBits(1 << uint8(p-14))
		}
	}
}

Should this be placed into another file? Or should I do something similar to the irq_USART0_RX const definition?

Oops I left in my offset definition I used to test it with the blinky1 example.

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

How would you recommend this be approached?

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

Do you just adjust the PIN constants such that they subtract off to match the shift amount? (seems like a bad way to do it)

@tmclane
Copy link
Contributor

tmclane commented Mar 10, 2020

I believe I need to make a mega2560 specific file to define the Pin functions:

  • Configure
  • Get
  • GetPortMask
    etc

This enables me to get the blinky1 program to run without modifications.

I'll open a PR with what I have and you guys can help direct my efforts there.

@tmclane
Copy link
Contributor

tmclane commented Mar 11, 2020

Alright. I finally understand how all the parts work.
I definitely need some of the commits related to the memory offsets on your atmega1284 branch in order to make it work. I rebased to origin/dev last night and took me a bit to realize why it wasn't running anymore!

@aykevl
Copy link
Member

aykevl commented Mar 11, 2020

I believe I need to make a mega2560 specific file to define the Pin functions:

* Configure

* Get

* GetPortMask
  etc

Yes this seems like the most sensible approach to me.

@deadprogram deadprogram added the next-release Will be part of next release label Mar 26, 2020
@deadprogram
Copy link
Member

This has now been released, so now closing. Thanks!

@deadprogram deadprogram removed the next-release Will be part of next release label Apr 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants