tinygo-org / tinygo

Go compiler for small places. Microcontrollers, WebAssembly (WASM/WASI), and command-line tools. Based on LLVM.
https://tinygo.org
Other
15.16k stars 890 forks source link

Some arduino issues #1154

Closed wbourne0 closed 3 years ago

wbourne0 commented 4 years ago
When trying to run gourotines: Code: ```go package main import ( "time" ) var currentTime int func trackTime() { for { currentTime++ time.Sleep(1 * time.Microsecond) } } func main() { go trackTime() for { println(currentTime) } } ``` When trying to flash: ``` tinygo flash -target=arduino-mega2560 -port "/dev/ttyACM0" -ocd-output main.go > main.go:17:2: attempted to start a goroutine without a scheduler ```
When trying to get the current time Now i'd like to point out that I know arduino doesnt have a good time system, but they do have a `millis()` function that returns how long the arduino has been running in milliseconds. import ( "time" ) var currentTime int func main() { for { now := time.Now() println(now.String()) } } ``` When building: ```tinygo flash -target=arduino-mega2560 -port "/dev/ttyACM0" -ocd-output main.go /tmp/tinygo295588631/main.o: In function `LBB10_3': main.go:(.text.(reflect.Value).Complex+0x9a): undefined reference to `__extendsfdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. main.go:(.text.(reflect.Value).Complex+0xba): undefined reference to `__extendsfdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found address size '9', this reader can not handle sizes greater than '8'. /tmp/tinygo295588631/main.o: In function `LBB12_3': main.go:(.text.(reflect.Value).Float+0x3c): undefined reference to `__extendsfdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '12', this reader only handles version 2, 3 and 4 information. /tmp/tinygo295588631/main.o: In function `LBB20_48': main.go:(.text.runtime._panic+0x2c4): undefined reference to `__extendsfdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. /tmp/tinygo295588631/main.o: In function `LBB20_71': main.go:(.text.runtime._panic+0x3fa): undefined reference to `__extendsfdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. /tmp/tinygo295588631/main.o:main.go:(.text.runtime._panic+0x406): more undefined references to `__extendsfdf2' follow /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '4354', this reader only handles version 2, 3 and 4 information. /tmp/tinygo295588631/main.o: In function `runtime.printfloat64': main.go:(.text.runtime.printfloat64+0x46): undefined reference to `__unorddf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '13312', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x64): undefined reference to `__ledf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '256', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x7a): undefined reference to `__adddf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '65535', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0xb0): undefined reference to `__eqdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '17', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0xd6): undefined reference to `__unorddf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '15619', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x104): undefined reference to `__gedf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x126): undefined reference to `__eqdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x14c): undefined reference to `__unorddf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x1d2): undefined reference to `__eqdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '1537', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x1f4): undefined reference to `__unorddf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x282): undefined reference to `__gedf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x2a0): undefined reference to `__subdf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '1792', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x2ce): undefined reference to `__ltdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '0', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x2ea): undefined reference to `__divdf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '18946', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x322): undefined reference to `__muldf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '13312', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x35c): undefined reference to `__gedf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '256', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x384): undefined reference to `__adddf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '65535', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x39e): undefined reference to `__ltdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '74', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x3be): undefined reference to `__divdf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '23042', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x3f4): undefined reference to `__divdf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '13312', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x404): undefined reference to `__gedf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '256', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x458): undefined reference to `__fixdfsi' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '65535', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x470): undefined reference to `__floatsidf' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '90', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x484): undefined reference to `__subdf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '26882', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.printfloat64+0x49e): undefined reference to `__muldf3' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '13312', this reader only handles version 2, 3 and 4 information. /tmp/tinygo295588631/main.o: In function `LBB40_15': main.go:(.text.runtime.reflectValueEqual+0x16c): undefined reference to `__eqdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '256', this reader only handles version 2, 3 and 4 information. main.go:(.text.runtime.reflectValueEqual+0x192): undefined reference to `__eqdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '65535', this reader only handles version 2, 3 and 4 information. /tmp/tinygo295588631/main.o: In function `LBB40_29': main.go:(.text.runtime.reflectValueEqual+0x31e): undefined reference to `__eqdf2' /usr/lib/gcc/avr/5.4.0/../../../avr/bin/ld: Dwarf Error: found dwarf version '105', this reader only handles version 2, 3 and 4 information. /tmp/tinygo295588631/main.o: In function `LBB40_31': main.go:(.text.runtime.reflectValueEqual+0x334): undefined reference to `__unorddf2' collect2: error: ld returned 1 exit status error: failed to link /tmp/tinygo295588631/main: exit status 1 ```

There's also a lot of builtin arduino functions we cant use. Could you make a way for us to include Arduino.h via cgo?

niaow commented 4 years ago

The scheduler is turned off by default on AVR, as there really isn't that much memory available. You can enable the scheduler with -scheduler=tasks.

Also note, that your for loop will repeatedly print the same time, without letting the goroutine run. You need to have something pause in the loop so that other things can be run.

println(now.String())

See https://github.com/tinygo-org/tinygo/issues/953. You can still do time.Sub to get a duration, and then divide by time.Millisecond.

wbourne0 commented 4 years ago

Oh. But no matter what the time (in unix) is always 0. If i print time.Now().Unix() its always giving me 0. Is there a way to directly call millis from arduino.h or is that impossible? Also i didnt notice that in the docs (note that its probably there) maybe you could make it more apparent? Like in the machine docs for it

wbourne0 commented 4 years ago
package main

import (
    "time"
)

func main() {

    for {
        now := time.Now()

        println(now.Unix())
    }
}

Output:

0
0
0
0
0
...

Edit: Not as i said above, I know arduino doesnt have any real time support. It would be nice to have some sort of wrapper for arduino's millis() though.

niaow commented 4 years ago

see https://github.com/tinygo-org/tinygo/issues/952

this is something which we have been wanting to fix (time.Sleep output only counts up while sleeping), but nobody has stepped up to implement that yet

It is not likely that we will ever be compatible with Arduino.h on most platforms, since we use interrupts which arduino also tries to use.

If you would like to work on better timer support, we would certainly appreciate any contribution. It should not be all too difficult, now that we have proper interrupt support for AVR.

wbourne0 commented 4 years ago

I'd be fine with that, if i have the time. Would be nice to have some pulseIn and pulseOut functions aswell, might look at those also.

aykevl commented 4 years ago

One issue with proper time support is that it will interfere with the PWM (all available timers are also used for PWM). I'm not sure what the best solution is but once PWM support is improved, I would argue it may be a better idea to put it in a library.

The way Arduino "fixes" it is by making the PWM harder to control (and thus making it harder to use with servos etc.).

wbourne0 commented 4 years ago

Another issue to add to the list: I cant read input from any pin. Arduino code:

package main

import (
    "machine"
)

func main() {
    in := machine.ADC{Pin: machine.A0}
    in.Configure()
    for {
        println(in.Get())
    }
}

As I just have my 3.5 volt pin connected directly to A0 (Wanted to guarantee results), i always get ~680-682.

With the go code however:

package main

import (
    "machine"
)

func main() {
    in := machine.ADC{Pin: machine.A0}
    in.Configure()
    for {
        println(in.Get())
    }
}

I only get 0s, with the same setup used with the arduino code (just a cable connected 3.5v to A0) Edit: Just to clarify, I get the same result with any pin.

deadprogram commented 4 years ago

I point you to the example here: https://github.com/tinygo-org/tinygo/blob/master/src/examples/adc/adc.go

Notice that your code is missing machine.InitADC().

Hope that helps!

wbourne0 commented 4 years ago

Oops. Should have noticed that. I noticed that gourotines dont run even with -scheduler=tasks. Actually, with -scheduler=tasks, nothing at all runs, main doesnt seem to be called, even with a 1 line print statement. I was able to get it to partially work with courotines, but its still buggy:

package main

import (
    "time"
    "tinygo/machine"
)

func timedOutput(pin machine.Pin) {
    for {

        time.Sleep(5 * time.Millisecond)
        pin.High()
        time.Sleep(5 * time.Millisecond)
        pin.Low()
    }

}

func main() {
    out := machine.D46
    in := machine.D48
    output := machine.PinConfig{Mode: machine.PinOutput}
    input := machine.PinConfig{Mode: machine.PinInput}
    out.Configure(output)
    in.Configure(input)
    go timedOutput(out)
    for {
        if in.Get() {
            println("Recieved signal")
        } else {
            println("Haven't gotten a signal.")
        }
        // This nexer exits. Instead, timedOutput runs forever.
        time.Sleep(1 * time.Second)
    }
}
aykevl commented 4 years ago

There is a reason goroutines are disabled by default on the Uno: they require a lot of RAM. I don't know what the issue is in this case but it's best to avoid them on chips with so little (2kB) RAM.

wbourne0 commented 4 years ago

I forgot to clarify i am using the Arduino mega (256kb ram), so I don't need to worry as much about ram issues.

niaow commented 4 years ago

Arduino mega (256kb ram)

The mega actually has 256kb of flash, not RAM. It has 8kb of RAM.

wbourne0 commented 4 years ago

oops, misread the specs. Sorry. Well i would still like to be able to do this, regardless, even if i wont be able to do much. I have been considering getting a different microchip, would like some good recommendations preferably with wifi support and a decent amount of ram.

wbourne0 commented 4 years ago

Alright, another issue i found... I cant write any pwm values, even with the 6 digital pins its limited to (limited by tinygo, the board itself has 11 or so dedicated pwm pins, none of which are used in tinygo D2-D12) Using a pin thats allowed by the library:

package main

import (
    "machine"
    "math"
    "time"
)

const twoPi = float32(2 * math.Pi)

func timedOutput(pin machine.Pin) {
    for {

        time.Sleep(5 * time.Millisecond)
        pin.High()
        time.Sleep(5 * time.Millisecond)
        pin.Low()
    }
}

const (
    sineB = 4.0 / math.Pi
    sineC = -4.0 / (math.Pi * math.Pi)
    Q     = 0.775
    sineP = 0.225
)

func abs(in float32) float32 {
    if in < 0 {
        return -in
    }
    return in
}

func sine(v float32) float32 {
    x := float32(v)
    y := sineB*x + sineC*x*(abs(x))
    y = sineP*(y*(abs(y))-y) + y
    return float32(y)
}

var currentTime = 0

func timeTracker() {
    for {
        currentTime++
        time.Sleep(1)
    }
}

func test() {
    println("test2")
}

func main() {
    machine.InitPWM()

    pin := machine.PWM{Pin: machine.D2}

    println(int(pin.Pin))

    pin.Configure()

    currentPhaseAngle := float32(0)
    currentSample := float32(0)
    phaseAngleIncr := 200 * twoPi / 44100

    for {

        currentSample++

        if currentPhaseAngle < -math.Pi {
            currentPhaseAngle += twoPi
        } else if currentPhaseAngle > math.Pi {
            currentPhaseAngle -= twoPi
        }

        out := 255/2*sine(currentPhaseAngle) + 255/2
        intOut := uint16(out)

        rem := out - float32(intOut)

        if rem > .5 {
            intOut++
        }

        pin.Set(intOut << 8)

        println("Setting to", intOut)

        currentPhaseAngle += phaseAngleIncr

        time.Sleep(1 / 44100 * time.Second)
    }

}

When using the same pin but with an arduino script this works fine, I would provide the source but I accidentally closed the window and didnt save, and dont want to re-create it.

wbourne0 commented 4 years ago

The arduino mega pwm pins are:   D2 - D13, D44 - D46 (38, 37, 53, 34, 59, 60, 61, 62, 12, 13, 14, 15, 85, 84, 83) https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/

aykevl commented 4 years ago

The short answer is that the ATmega2560 is a relatively recent addition and is not as well supported. The PWM is probably just not implemented.

Unfortunately I don't have an ATmega2560 so can't do it myself (if I had the time).

wbourne0 commented 4 years ago

Oh ok. Also trying to figure out how to use the interrupts, how exactly would i do that w/ avr? I'm trying to make an interrupt for IRQ_TIMER0_OVF (23) which should occur every microsecond and is what arduino uses for its timing. I tried to do a few things, troublshooted from here

Check that you have enabled the interrupt source in the peripheral (step 2).

Not sure how i should do that, doesnt seem to be something that arduino deals with and its not linked to any pin afaik.

Check that you have enabled the interrupt with the intr.Enable() call (step 4).

No enable func w/ avr

Check that you are listening for the correct interrupt and not for an interrupt for a different peripheral, for example (step 1).

Actually found the interrupt code in avr src code before i found it in tinygo, but it matches.

interrupt.New(23, test)
func test(interrupt.Interrupt) {
    println("test")
}
aykevl commented 4 years ago

I'm trying to make an interrupt for IRQ_TIMER0_OVF (23) which should occur every microsecond and is what arduino uses for its timing.

You will need to check the ATmega328p datasheet. The Timer0 peripheral is not configured at startup, you'll need to do that manually (this is what the Arduino environment does for you, but TinyGo doesn't).

wbourne0 commented 4 years ago

I dont have the time to create a pr, but i'll list the configuration needed to get the interrupt to work.

avr.TIMSK0.Set(avr.TIMSK0_TOIE0)  // Enable timer0 overflow interrupt
avr.TCCR0B.Set(avr.TCCR0B_CS00) // Enable the timer without any prescaling
avr.TIFR0.Set(avr.TIFR0_TOV0) // Enable overflow interrupt flag

The info for prescaling is here Then, the overflow is dependant on the cpu's refresh rate.

Should be equal to machine.CPUFrequency() / (256 * 1) ticks per second. (cpu frequency / timer overflow (256) * prescaling)

package main

import (
    "device/avr"
    "machine"
    "runtime/interrupt"
)

var t = int32(0)

func trackTime(interrupt.Interrupt) {
    t++
}

func main() {
    avr.TIMSK0.Set(avr.TIMSK0_TOIE0)
    avr.TCCR0B.Set(avr.TCCR0B_CS00)
    avr.TIFR0.Set(avr.TIFR0_TOV0)

    interrupt.New(avr.IRQ_TIMER0_OVF, trackTime)
    // note that some accuracy could be lost with different cpus as i'm not using floats,
    // for the mega 2560 though its a clean division.
    cps := machine.CPUFrequency() / 256

    for {

        if t >= cps {
            println("1 sec")
            t = 0
        }

    }
}
oberoid commented 4 years ago

Hi, I'm following this conversation as a newcomer in tinygoland. I try to understand this imported function avr.TIMSK0.Set(avr.TIMSK0_TOIE0). Could you please give me a link to the file where this function is defined on github? Grts F.P.

wbourne0 commented 4 years ago

Thats in a generated file. You have to download the source, (recursively) and run make gen-device-avr it should be at src/device/avr/atmega2560.go once you generate it.

oberoid commented 4 years ago

Thanks! I just have my arduino-nano blinking and found the files.

wbourne0 commented 4 years ago

Alright. Note that the code i provided wont work for you, as the arduino nano uses sam iirc.

wbourne0 commented 4 years ago

Looking at the digital inputs it doesnt look like anything is correct...

// getRegisterPortMask returns the register, port, and mask for the pin
func (p Pin) getRegisterPortMask() (*volatile.Register8, *volatile.Register8, uint8) {
    switch {
    case p >= PA0 && p <= PA7:
        return avr.DDRA, avr.PORTA, 1 << uint8(p-portA)
    case p >= PB0 && p <= PB7:
        return avr.DDRB, avr.PORTB, 1 << uint8(p-portB)
    case p >= PC0 && p <= PC7:
        return avr.DDRC, avr.PORTC, 1 << uint8(p-portC)
    case p >= PD0 && p <= PD7:
        return avr.DDRD, avr.PORTD, 1 << uint8(p-portD)
    case p >= PE0 && p <= PE6:
        return avr.DDRE, avr.PORTE, 1 << uint8(p-portE)
    case p >= PF0 && p <= PF7:
        return avr.DDRF, avr.PORTF, 1 << uint8(p-portF)
    case p >= PG0 && p <= PG5:
        return avr.DDRG, avr.PORTG, 1 << uint8(p-portG)
    case p >= PH0 && p <= PH6:
        return avr.DDRH, avr.PORTH, 1 << uint8(p-portH)
    case p >= PJ0 && p <= PJ1:
        return avr.DDRJ, avr.PORTJ, 1 << uint8(p-portJ)
    case p >= PK0 && p <= PK7:
        return avr.DDRK, avr.PORTK, 1 << uint8(p-portK)
    case p <= PL0 && p >= PL7:
        return avr.DDRL, avr.PINL, 1 << uint8(p-portL)
    default:
        return avr.DDRB, avr.PORTA, 255
    }
}

So there's a few things wrong here: The pins that belong to the different ports arent in static ranges. EG: case p <= PL0 && p >= PL7: Well if I'm using digital pin 43 *PL6) well... PL0 == 80 and PL7 == 39 But PL6 == 86, so this wont catch it. Let alone the fact that all of:

[
    "PF0",
    "PF1",
    "PF2",
    "PF3",
    "PF4",
    "PF5",
    "PF6",
    "PF7",
    "PG0",
    "PG1",
    "PG2",
    "PG5",
    "PG6",
    "PH0",
    "PH1",
    "PH3",
    "PH4",
    "PH5",
    "PH6",
    "PJ0",
    "PJ1",
    "PK0",
    "PK1",
    "PK2",
    "PK3",
    "PK4",
    "PK5",
    "PK6",
    "PK7",
    "PL0",
    "PL7"
]

are in that range.

Now I made an exception for pin 86 (D43 and also PL6) yet it still didnt work. However, once I changed avr.PORTL to avr.PINL it worked, looks like PIN(X) is used instead of PORT(X) for getting the input of a pin.

aykevl commented 4 years ago

@AllAwesome497 please try with the dev branch. There were a number of changes related to pins on AVR in the dev branch.

wbourne0 commented 4 years ago

Havent had much time to work on this in a while, I'll see if it works now @aykevl

Edit: Ended up running out of time, will test next weekend if i get the chance.

judokan9 commented 3 years ago

Hello,

i have also some Problems with the MEGA2560 when i try to output float variables.

How to reproduce:

x :=5.5
y := 5.5

println(x + y)

After this, the serial terminal and the program hangs and the arduino is doing nothing anymore.

wbourne0 commented 3 years ago

Ok, the issues with the pins seem to be fixed - now I'm getting everything to run correctly, I believe.

wbourne0 commented 3 years ago

I think everything in the original issue has been resolved, so closing.