[Solved] Using PlatformIO for compilation

Its P#_# with a capital P and # is numbers. Port first, then pin

2 Likes

I am also working on this at the moment. Its not so difficult, but I need to get the hang of the platformio.ini system

I believe we will be building with no problems on platformio within a few days

Only thing I do not yet know how to do is the lpcrc and how to implement it. But since all NXP LPC11U targets need it by default, I think it should be somehow possible

1 Like

It should be possible to do something like (might not work, I don’t know Python):

Import("env")

# Not sure what the arguments are
# One of them might be 'hello'
def after_build(source, target, env):
  env.Execute("arm-none-eabi-objcopy.exe -I ihex .\build\hello.hex -O binary .\build\hello.bin")
  env.Execute(".\build\lpcrc .\build\hello.bin")

env.AddPostAction("buildprog", after_build)

And then have the .ini include:

[env:pre_and_post_hooks]
extra_script = extra_script.py

I’m basing that on info from here:

and here:

http://docs.platformio.org/en/latest/projectconf/advanced_scripting.html#before-pre-and-after-post-actions

Looks like it’s also possible to specify a custom uploader, so we could have scripts for automatic copying onto CRP_DISABLD.

not sure how atom/vscode works in terms of plugins but i found the mac electron uploader again and heres the relivent code to make a uploader work

it needed shelljs in electron but it might be different in atom/vscode

var exec = require('shelljs.exec')
var shell = require('shelljs')

ipcMain.on("uploadToPokitto", (event, path) => {
	if(process.platform == "darwin"){
		shell.cp("-f",path,"/Volumes/CRP\\ DISABLD/firmware.bin")
		exec("diskutil unmount /Volumes/CRP\\ DISABLD")
		event.returnValue = "upload finished"
	}
	if(process.platform == "linux"){
	}
	if(process.platform == "win32"){
	}
})

ipcMain.on("isPokittoconnected", (event, path) => {
	if(process.platform == "darwin"){
		var cmd = exec("find /Volumes/CRP\\ DISABLD",{silent: true})
		event.returnValue = cmd.code
	}
	if(process.platform == "linux"){
	}
	if(process.platform == "win32"){
	}
})

VSCode and Atom both use Node.js to some degree (see here and here), so this might work.
VSCode uses Yeoman which apparently uses shelljs so it’s possible that shelljs is already available for VSCode extensions.

ok then it might work, the only trouble is how to create the button in the editor (that guide only shows a comandline thing)

Seems most extensions just add stuff to the ‘Command Palette’ on VSCode, i.e. the UI doesn’t get edited.

There are extensions for providing buttons, for example:
https://marketplace.visualstudio.com/items?itemName=seunlanlege.action-buttons

But to be honest I think we should just provide the functionality and let the end user decide how they want their IDE to look.
That or at least provide both a lightweight command-only option in addition to a button option.

The point of editors like Atom and VSCode is to be lightweight and modular, so it makes sense to let people choose how to customise things.

The more lightweight we make our extensions/plugins, the easier it will be to support multiple editors and the less effort we have to spend keeping them up to date.

well this is inline to the platfromIO wich also gives you a bunch of buttons like build and upload, so having a upload to pokitto button seems to make sence, but i think those buttons are extensions to terminal commands

In that you want to be looking at PlatformIO functionality rather than the editors (i.e. forget Atom and VSCode and focus on PlatformIO).

As I showed earlier PlatformIO uses Python for scripting and any build events and upload events are handled by PlatformIO’s Python API.

1 Like

oh ok so we should override there upload with ours? if thats even posible
python isnt something i know well enugh so and this is already prety confusing, i geuss you can alter the shellscripts into a python file or something

I don’t know Python either (but I could learn if need be, it’s not the hardest of languages).
We do have other people who do know Python (e.g. @Hanski).

As I said earlier:

There’s an example that shows how to specify your own upload command here. It’s for AVR but it would be easy to replace.

We’ve already got a few ‘upload’ scripts knocking around the forums, we just have to gather them up, make sure they work and ask their authors for permission to include them.

There might also be a way of replacing those scripts with Python code. I don’t know where CRP_DISABLD turns up on all the different systems so I don’t know what kind of APIs we’d need. I know for Windows at least there would have to be an API for scanning drive names to locate the correct drive.

to detect CRP_DISABLD we just need root system calls
mac its always under /Volumes
idk if vscode runs on the pi but the thats always /media/pi
and i think on most linux distros its under /dev/disk/ (need someone to check this)
only windows with random drive labels is a problem wich needs some sort of scan function

i see python wants subprocess.call() to do systemcalls so i guess that then
not sure how to specify the os though

Like I say, we can just use existing scrpts first and use env.Execute("shell script path").


Can be done using either import win32api or from ctypes import windll, enumerating the volumes and geting the ‘volume information’.

Enumerating drives is shown here:

Volume information is shown here:

So it should just be a matter of glueing those together.

i tried but i cant get any of this to work im not even sure platformio is running my script

im giving up on this, its just frustrating. i have 0 luck getting the compiler to work, none of the scripting makes sense, the default lpc11u68 library has a bug in it that hasn’t been fixed

Don’t worry. I am very close to getting it working. You need to do tricks with the platform.ini

1 Like

This is the reason why we have the mbed-pokitto fork of the mbed library. I have fixed that error. There are a couple of others too. I have notified NXP but IDK if they are interested in fixing them

1 Like

#Update

PlatformIO compiling and linking now but the binary is not doing what it is supposed to. It runs, but I get a white screen.

I am now hooking up the J-Link hardware debugger to PlatformIO to see where the problem is.

And FYI, if someone thinks “Platformio is easy to get running” I can say it is not. Just getting the linker flags to actually go through to the linker requires a lot of fiddling. Nothing works like in a “classic” IDE. You have to play around with platform.ini to get the thing working.

For reference, here is my current platform.ini (WIP):

; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
; -Os --specs=nano.specs --specs=rdimon.specs -lc -lc -lrdimon -o main

[env:lpc11u68]
platform = nxplpc
board = lpc11u68
framework =
lib_deps =
build_flags = -O3
-fno-exceptions
-fno-rtti
-std=gnu++11 -Wextra -Wall
-fomit-frame-pointer -fdata-sections -ffunction-sections
-g -funsigned-char -Wno-unused-parameter
-Wno-missing-field-initializers
-fmessage-length=0 -fno-builtin
-fno-delete-null-pointer-checks -DMBED_RTOS_SINGLE_THREAD -Wvla
-DTARGET_LPC11U68 -D__MBED__=1 -DDEVICE_I2CSLAVE=1
-DTARGET_LIKE_MBED -DTARGET_NXP -D__MBED_CMSIS_RTOS_CM
-DDEVICE_RTC=1 -DTOOLCHAIN_object
-D__CMSIS_RTOS -DTOOLCHAIN_GCC
-DTARGET_CORTEX_M -DTARGET_M0P
-DTARGET_UVISOR_UNSUPPORTED
-DDEVICE_SERIAL=1 -DDEVICE_INTERRUPTIN=1
-DTARGET_LPCTarget
-DTARGET_CORTEX -DDEVICE_I2C=1 -D__CORTEX_M0PLUS
-DTARGET_FF_ARDUINO -DTARGET_RELEASE
-DMBED_BUILD_TIMESTAMP=1507563655.72
-DARM_MATH_CM0PLUS -DTARGET_LPC11U6X
-DDEVICE_SLEEP=1 -DTOOLCHAIN_GCC_ARM
-DDEVICE_SPI=1 -DDEVICE_ANALOGIN=1 -DDEVICE_PWMOUT=1
-DTARGET_LIKE_CORTEX_M0
-Isrc/
-Ilib/Pokitto/POKITTO_CORE
-Ilib/Pokitto/POKITTO_CORE/FONTS
-Ilib/Pokitto/POKITTO_CORE/PALETTES
-Ilib/Pokitto/POKITTO_HW
-Ilib/Pokitto/POKITTO_LIBS/Synth
-Ilib/Pokitto/libpff
-Ilib/Pokitto/mbed-pokitto
-Ilib/Pokitto/mbed-pokitto/api
-Ilib/Pokitto/mbed-pokitto/common
-Ilib/Pokitto/mbed-pokitto/hal
-Ilib/Pokitto/mbed-pokitto/targets/cmsis/
-Ilib/Pokitto/mbed-pokitto/targets/cmsis/TARGET_NXP/
-Ilib/Pokitto/mbed-pokitto/targets/cmsis/TARGET_NXP/TARGET_LPC11U6X/
-Ilib/Pokitto/mbed-pokitto/targets/cmsis/TARGET_NXP/TARGET_LPC11U6X/TOOLCHAIN_GCC_ARM/
-Ilib/Pokitto/mbed-pokitto/targets/cmsis/TARGET_NXP/TARGET_LPC11U6X/TOOLCHAIN_GCC_ARM/TARGET_LPC11U68
-Ilib/Pokitto/mbed-pokitto/targets/hal/TARGET_NXP/TARGET_LPC11U6X
extra_scripts = update_link_flags.py
lpc_checksum.py
; Change this to your /media/disk on Linux/Mac and drive letter on Windows!
; upload_port = D:
upload_protocol = C:/Program Files (x86)/SEGGER/JLink_V502f/JLink.exe
; Debugging
debug_tool = jlink
debug_server =
C:/Program Files (x86)/SEGGER/JLink_V502f/JLinkGDBServer.exe
-singlerun
-if
SWD
-select
USB
-port
2331
-device
LPC11U68

Here is the extra script needed to pass the linker flags (update_link_flags.py):

# Custom settings, as referred to as “extra_script” in platformio.ini
#
# See http://docs.platformio.org/en/latest/projectconf.html#extra-script

from SCons.Script import DefaultEnvironment

env = DefaultEnvironment()

env.Append(
LINKFLAGS=[
“–specs=nano.specs”,
“–specs=nosys.specs”
]
)

I tried every possible combination of “-Wl,–specs=nano.specs” “–specs=nano.specs” “-specs=nano.specs” but only using an external script actually made it work

Furthermore I was banging my head like crazy to get the LPC checksum Python script to run because

import intelhex

always ended in: ImportError: no module by name intelhex found, even though it was in my lib/site-packages

and it turned out I had to

pip install intelhex

from inside the IDE from the terminal instead!

Here is the LPC checksum calculation code:

#!/usr/bin/env python

import sys
import struct
import argparse
import intelhex

__version__ = "2.0.0"

"""
Calculate checksum image for LPC firmware images and write. Code is a Python
port of the C version written by Roel Verdult named `lpcrc'.

The checksum is the two's-complement of the sum of the first seven 4-byte
blocks. This value is placed in the eight block.
"""

BLOCK_START = 0
BLOCK_COUNT = 7
BLOCK_SIZE = 4
BLOCK_TOTAL = (BLOCK_COUNT * BLOCK_SIZE)


def run():
    """
    Entry point for console script.
    """
    sys.exit(main())


def main():
    """
    Command line wrapper for the checsum() method. Requires the first parameter
    to be the filename. If no filename is given, the syntax will be printed.
    Output is written to stdout and errors to stderr.
    """

    # Parse arguments.
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "filename", type=str, help="input file for checksumming")
    parser.add_argument(
        "-f", "--format", action="store", type=str, default="bin",
        choices=["bin", "hex"], help="input file format (defaults to bin)")
    parser.add_argument(
        "-r", "--readonly", action="store_true",
        help="read only mode (do not write checksum to file)")
    options = parser.parse_args()

    # Calculate checksum.
    try:
        result = checksum(
            options.filename, options.format, options.readonly)
    except Exception as e:
        sys.stdout.write("Error: %s\n" % e)
        return 1

    # Done.
    sys.stdout.write("Succesfully updated checksum to 0x%08x\n" % result)


def checksum(filename, format="bin", read_only=False):
    """
    Calculate the checksum of a given binary image. The checksum is written
    back to the file and is returned. When read_only is set to True, the file
    will not be changed.

    filename  -- firmware file to checksum
    format    -- input file format (bin or hex, default bin)
    read_only -- whether to write checksum back to the file (default False)
    """

    # Open the firmware file.
    handle = intelhex.IntelHex()
    filename = "firmware.bin"
    handle.loadfile(filename, format=format)

    # Read the data blocks used for checksum calculation.
    block = bytearray(map(ord, handle.gets(BLOCK_START, BLOCK_TOTAL)))

    if len(block) != BLOCK_TOTAL:
        raise Exception("Could not read the required number of bytes.")

    # Compute the checksum value.
    result = 0

    for i in range(BLOCK_COUNT):
        value, = struct.unpack_from("I", block, i * BLOCK_SIZE)
        result = (result + value) % 0xFFFFFFFF

    result = ((~result) + 1) & 0xFFFFFFFF

    # Write checksum back to the file.
    if not read_only:
        handle.puts(BLOCK_START + BLOCK_TOTAL, struct.pack("I", result))
        handle.tofile(filename, format=format)

    # Done
    return result

# E.g. `python lpc_checksum.py --format bin firmware.bin`.
if __name__ == "__main__":
    run()
1 Like

Sounds familiar… is that the Pokitto equivalent of a segfault?

Probably an interrupt handler that ends up in a hardfault, or a system initialization stuck, such as waiting for PLL clock to lock

#and:

hardware debugging on PlatformIO is possible only by subscribing to “plus” subscription at 9,90$ per month.

For me this alone kills the platform.

I would rather get everything working on Ecclipse than waste more time on this.

But, since we’ve come such a long way, I will try my best to get this working also.

1 Like