summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaveHodder67 <dave.hodder@focusrite.com>2015-07-01 16:00:05 +0100
committerDaveHodder67 <dave.hodder@focusrite.com>2015-07-01 16:00:05 +0100
commitb9bac4f039aaeb8fee4b2f49bef148818408ef74 (patch)
treee4c2e51b254ce66d0356ff63f6b557e71ebb424e
parentd30110407eb706b9ac84d7c5465b95560834d947 (diff)
downloadlaunchpad-polymaker-b9bac4f039aaeb8fee4b2f49bef148818408ef74.tar.gz
launchpad-polymaker-b9bac4f039aaeb8fee4b2f49bef148818408ef74.zip
importing the real code (!)
-rw-r--r--Makefile58
-rw-r--r--README.md66
-rw-r--r--Vagrantfile24
-rw-r--r--bootstrap.sh58
-rw-r--r--include/app.h84
-rw-r--r--include/app_defs.h65
-rwxr-xr-xresources/Launchpad Pro-1.0.154.syxbin0 -> 114219 bytes
-rw-r--r--src/app.c92
-rw-r--r--stm32_flash.ld143
-rw-r--r--tools/hextosyx.cpp152
10 files changed, 690 insertions, 52 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8c91f24
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,58 @@
1BUILDDIR = build
2
3TOOLS = tools
4
5SOURCES += src/app.c
6
7OBJECTS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(SOURCES))))
8
9INCLUDES += -Iinclude \
10-I\
11
12SYX = $(BUILDDIR)/launchpad_pro.syx
13LIB = lib/launchpad_pro.a
14ELF = $(BUILDDIR)/launchpad_pro.elf
15HEX = $(BUILDDIR)/launchpad_pro.hex
16HEXTOSYX = $(BUILDDIR)/hextosyx
17
18HOST_CC = g++
19CC = arm-none-eabi-gcc
20LD = arm-none-eabi-gcc
21OBJCOPY = arm-none-eabi-objcopy
22
23CFLAGS = -Os -g -Wall -I.\
24-D_STM32F103RBT6_ -D_STM3x_ -D_STM32x_ -mthumb -mcpu=cortex-m3 \
25-fsigned-char -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER -DHSE_VALUE=6000000UL \
26-DCMSIS -DUSE_GLOBAL_CONFIG -g3 -ffunction-sections -std=c99 -mlittle-endian \
27$(INCLUDES) -o
28
29LDSCRIPT = stm32_flash.ld
30
31LDFLAGS += -T$(LDSCRIPT) -u _start -u _Minimum_Stack_Size -mcpu=cortex-m3 -mthumb -specs=nano.specs -specs=nosys.specs -nostdlib -Wl,-static -g3 -N -nostartfiles -Wl,--gc-sections
32
33all: $(SYX)
34
35# build the final sysex file from the ELF
36$(SYX): $(HEX) $(HEXTOSYX)
37 ./$(HEXTOSYX) $(HEX) $(SYX)
38
39# build the tool for conversion of ELF files to sysex ready for upload to the unit
40$(HEXTOSYX):
41 $(HOST_CC) -Ofast -std=c++0x -I./$(TOOLS)/libintelhex/include ./$(TOOLS)/libintelhex/src/intelhex.cc $(TOOLS)/hextosyx.cpp -o $(HEXTOSYX)
42
43$(HEX): $(ELF)
44 $(OBJCOPY) -O ihex $< $@
45
46$(ELF): $(OBJECTS)
47 $(LD) $(LDFLAGS) -o $@ $(OBJECTS) $(LIB)
48
49$(BUILDDIR)/%.o: %.c
50 mkdir -p $(dir $@)
51 $(CC) -c $(CFLAGS) $< -o $@
52
53$(BUILDDIR)/%.o: %.s
54 mkdir -p $(dir $@)
55 $(CC) -c $(CFLAGS) $< -o $@
56
57clean:
58 rm -rf $(BUILDDIR)
diff --git a/README.md b/README.md
index 5424389..f5f0968 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,19 @@
1# Launchpad Pro 1# Launchpad Pro
2Open source firmware for the Novation Launchpad Pro grid controller! By customising this code, you can: 2Open source firmware for the Novation Launchpad Pro grid controller! By customising this code, you can:
3 3
4- Implement your own unique standalone modes 4- Implement your own unique standalone apps
5- Create chorders, sequencers, light shows, games and more 5- Create chorders, sequencers, light shows, games and more
6- Learn a little about firmware development! 6- Learn a little about software development!
7 7
8You definitley need *some* C programming experience, but we've deliberately kept much of the firmwarey nastiness tucked away, to make the process a little friendlier. 8You'll definiteley need *some* C programming experience, but we've deliberately kept much of the firmwarey nastiness tucked away, to make the process a little friendlier.
9 9
10# Philosophy 10# Philosophy
11We could have released the full source for the factory shipping firmware, but we've decided not to for a variety of reasons. Instead, we created a simplified framework for developing "apps" on Launchpad, which comprises a build environment, application entry points / API, and a library of low level source code. Our reasoning is as follows: 11We could have released the full source for the factory shipping firmware, but we decided not to for a variety of reasons. Instead, we created a simplified framework for developing "apps" on Launchpad, which comprises a build environment, application entry points / API, and a library of low level source code. Our reasoning is as follows:
12 12
13- There is no value in customising low level routines such as LED multiplexing or ADC scanning - this code has been carefully tweaked over many months to deliver the best results, and is not something you'd want to tweak. I'm prepared to be convinced otherwise! 13- There is no value in customising low level routines such as LED multiplexing or ADC scanning - this code has been carefully tweaked over many months to deliver the best results, and is not something you'd want to mess with.
14- There is very little value in customising main() or other low level features, and again these things are hard to do well. 14- There is very little value in customising main() or other low level features, and again these things are hard to do well. Interrupt priorities? No.
15- If we shipped the application firmware as-is, we'd have a support nightmare on our hands (imagine the support calls - my "Launchpad Pro is behaving strangely..."). Instead, we wanted to create a clear boundary between "normal" usage with Ableton, and custom firmware. As such, Ableton integration has been removed from this firmware, as has the setup / navigation functionality. 15- If we shipped the application firmware as-is, we'd have a support nightmare on our hands (imagine the phonecalls - my "Launchpad Pro is behaving strangely..."). Instead, we wanted to create a clear boundary between "normal" usage with Ableton, and custom firmware. As such, Ableton integration has been removed from this firmware, as has the setup / navigation functionality.
16- If we left the Ableton integration and menu structure in place, open firmware developers would have to work around it. They would also potentially consume precious RAM/CPU resources. I've a feeling this isn't what you'd want, but again, we're interested to hear your feedback. 16- If we left the Ableton integration and menu structure in place, open firmware developers would have to work around it. They would also potentially consume precious RAM/CPU resources. I've a feeling this isn't what you'd want, but we're interested to hear your feedback.
17- Licensing requirements for the CMSIS library version we use are ambiguous. Yes, we could port to the public version, but why bother, given the above reasoning - I'd prefer to spend my time on good documentation and examples. As such, all the CMSIS code is compiled into launchpad_pro.a, and we do not need to distribute the headers. 17- Licensing requirements for the CMSIS library version we use are ambiguous. Yes, we could port to the public version, but why bother, given the above reasoning - I'd prefer to spend my time on good documentation and examples. As such, all the CMSIS code is compiled into launchpad_pro.a, and we do not need to distribute the headers.
18 18
19I'm sure you'll have feedback for us, so please do get in touch! I'm [blogging the process too](http://dvhdr.tumblr.com/) if you'd like to read my musings. 19I'm sure you'll have feedback for us, so please do get in touch! I'm [blogging the process too](http://dvhdr.tumblr.com/) if you'd like to read my musings.
@@ -21,29 +21,45 @@ I'm sure you'll have feedback for us, so please do get in touch! I'm [blogging
21# Installation 21# Installation
22This project uses [Vagrant](https://www.vagrantup.com/) to manage the build environment. As such, you need to: 22This project uses [Vagrant](https://www.vagrantup.com/) to manage the build environment. As such, you need to:
23 23
241. Install [Vagrant](https://www.vagrantup.com/) 241. Clone this repository
252. Install [VirtualBox](https://www.virtualbox.org/wiki/Downloads)(or another virtualisation platform of your choice) 252. Install [Vagrant](https://www.vagrantup.com/)
263. `vagrant up` 263. Install [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
274. `vagrant up` to create a brand new development virtual machine, all configured and ready to build the firmware!
27 28
28# Building 29# Building
29SSH into the Vagrant "box" by doing `vagrant up` 30You can build the app in one of two ways. In the spirit of experimenation, I've created a full Eclipse development environment for you to use. However, you might prefer to do things on the command line. (if you do, you might also want to run your Vagrant box headless, which you can do by hacking the Vagrantfile).
30At the command prompt, simply type `make`
31 31
32This will generate the firmware launchpad_pro.syx file in the build directory. You can then upload this to your Launchpad Pro from the host! 32# To use the command line interface:
331. SSH into the Vagrant "box" by doing `vagrant ssh`
342. At the command prompt, simply type `make`
35
36# To build using the Eclipse GUI
37
381. Log in to the Ubuntu GUI (the password is, as is the convention, "vagrant").
392. Launch Eclipse from the doodah on the top left (it's a bit like Spotlight)
403. This is the annoying bit. I can't figure out how to store the project in source control, so you need to import it.
414. In Eclipse, choose "File->Import..."
425. Under "C/C++", choose "Makefile project with existing code"
436. Give the project any name you like (launchpad?)
447. Under "Existing code location" choose /vagrant
458. Hit Finish. You should now see your project on the left!
469. Click the build icon at the top, and wait while the project builds.
47
48Either of the above methods will generate the firmware launchpad_pro.syx file in the /build directory. You can then upload this to your Launchpad Pro from the host!
33 49
34# Uploading to a Launchpad Pro 50# Uploading to a Launchpad Pro
35Now you've got some nice new code to run. To upload it to your Launchpad Pro, you'll need a sysex tool for your host platform (I'd love to get it working from the virtual machine, but that's for later). I recommend [Sysex Librarian](http://www.snoize.com/SysExLibrarian/) on OS X, and [MIDI OX](http://www.midiox.com/) on Windows. On Linux, I'll bet you know better than I do! 51Now you've got some nice new code to run! To upload it to your Launchpad Pro, you'll need a sysex tool for your host platform (I'd love to get it working from the virtual machine, but that's for later). I recommend [Sysex Librarian](http://www.snoize.com/SysExLibrarian/) on OS X, and [MIDI OX](http://www.midiox.com/) on Windows. On Linux, I'll bet you already have a tool in mind.
36 52
37I won't describe how to use these tools, I'm sure you already know - and if you don't, their documentation is better than mine! Here's what you need to do: 53I won't describe how to use these tools, I'm sure you already know - and if you don't, their documentation is superior to mine! Here's what you need to do:
38 54
391. Unplug your Launchpad Pro 551. Unplug your Launchpad Pro
402. Hold the "setup" button down while connecting it to your host via USB (ensure it's connected to the host, and not to a virtual machine!) 562. Hold the "Setup" button down while connecting it to your host via USB (ensure it's connected to the host, and not to a virtual machine!)
413. The unit will start up in "bootloader" mode 573. The unit will start up in "bootloader" mode
424. "play" your launchpad_pro.syx file to the device - it should breifly scroll "updateing..." across the grid 584. Send your launchpad_pro.syx file to the device MIDI port - it should briefly scroll "updating..." across the grid.
435. Wait for the update to complete, and for the device to reboot! 595. Wait for the update to complete, and for the device to reboot!
44 60
45# Bricked it! 61# Bricked it!
46Don't worry - even if you upload toxic nonsense to the device, you cannot brick it - the bootloader is stored in a protected area of flash. If your new firmware doesn't boot, you'll get stuck at step (3) above. Simply repeat the process with the shipping firmware image (Launchpad Pro-1.0.154.syx) to restore your unit to the factory defaults. 62Don't worry - even if you upload toxic nonsense to the device, you cannot brick it - the bootloader is stored in a protected area of flash. If your new firmware doesn't boot, you'll get stuck at step (3) above, or with a crashed unit. Simply repeat the above process with the shipping firmware image (resources/Launchpad Pro-1.0.154.syx) to restore your unit to the factory defaults. Better yet, fix the bugs :)
47 63
48# API 64# API
49The most crucial parts of the API are: 65The most crucial parts of the API are:
@@ -51,18 +67,14 @@ The most crucial parts of the API are:
51- Recieving messages from the pads and buttons 67- Recieving messages from the pads and buttons
52- Writing colours to the LEDs 68- Writing colours to the LEDs
53- Sending and receiving messages from the MIDI ports 69- Sending and receiving messages from the MIDI ports
54- Receiving a tick message to drive timing based code 70- Receiving a tick message to drive timer based code
55
56Up for debate:
57 71
58- USB MIDI support (why not just implement on the host in programmer mode using the factory firmware?) 72The best way to learn about these is to read the documentation in app.h, and to study the example code!
59- Flash memory read / write access
60- Setup button behaviour
61 73
62# Debugging 74# Debugging
63We decided not to support or encourage using a JLink or similar for debugging, as opening a Launchpad Pro to fit a debugging header can easily damage the FSR (force sensitive resistor) sheet. 75We decided not to support or encourage using a hardware debugger, as opening a Launchpad Pro to fit a debugging header can easily damage the FSR (force sensitive resistor) sheet.
64 76
65Instead, you're going to have to do things the old fashioned way - by blinking LEDs or sending MIDI messages. FWIW, that's the way I've developed this version of the firmware - dogfooding all the way ;) 77Instead, you're going to have to do things the old fashioned way - by blinking LEDs or sending MIDI messages. FWIW, that's the way I've developed this version of the firmware - dogfooding all the way ;)
66 78
67If you want to test code in detail, we suggest developing it on a host (we'd love to release our simulator environment at a later stage, but there are currently no plans to do so). 79If you want to test code in detail, we suggest developing it on a host. I want to develop a host app which presents the same API to app.c, but which forwards all messages to and from the hardware Launchpad Pro via MIDI - that way you'll be able to debug most code on the host very nicely.
68 80
diff --git a/Vagrantfile b/Vagrantfile
index 19a4ea9..81f83fd 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -21,11 +21,6 @@ Vagrant.configure(2) do |config|
21 21
22 config.vm.provision :shell, path: "bootstrap.sh" 22 config.vm.provision :shell, path: "bootstrap.sh"
23 23
24 # use the desktop!
25 #config.vm.provider :virtualbox do |vb|
26 # vb.gui = true
27 #end
28
29 # Create a forwarded port mapping which allows access to a specific port 24 # Create a forwarded port mapping which allows access to a specific port
30 # within the machine from a port on the host machine. In the example below, 25 # within the machine from a port on the host machine. In the example below,
31 # accessing "localhost:8080" will access port 80 on the guest machine. 26 # accessing "localhost:8080" will access port 80 on the guest machine.
@@ -50,14 +45,17 @@ Vagrant.configure(2) do |config|
50 # backing providers for Vagrant. These expose provider-specific options. 45 # backing providers for Vagrant. These expose provider-specific options.
51 # Example for VirtualBox: 46 # Example for VirtualBox:
52 # 47 #
53 # config.vm.provider "virtualbox" do |vb| 48 config.vm.provider "virtualbox" do |vb|
54 # # Display the VirtualBox GUI when booting the machine 49 # Display the VirtualBox GUI when booting the machine
55 # vb.gui = true 50 vb.gui = true
56 # 51
57 # # Customize the amount of memory on the VM: 52 # Customize the amount of memory on the VM:
58 # vb.memory = "1024" 53 vb.memory = "2048"
59 # end 54
60 # 55 # let’s have something usable for proper development
56 vb.cpus = 2
57 end
58
61 # View the documentation for the provider you are using for more 59 # View the documentation for the provider you are using for more
62 # information on available options. 60 # information on available options.
63 61
diff --git a/bootstrap.sh b/bootstrap.sh
index 111b248..63991d5 100644
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -1,25 +1,59 @@
1#!/usr/bin/env bash 1#!/usr/bin/env bash
2 2
3# setup as described here: http://gnuarmeclipse.livius.net/blog/toolchain-install/#GNULinux 3# setup as described here: http://gnuarmeclipse.livius.net/blog/toolchain-install/#GNULinux
4# We're using Luna - need to update plugin install locations below if switching to Kepler etc.
5ECLIPSE_URL=http://mirror.netcologne.de/eclipse//technology/epp/downloads/release/luna/SR2/eclipse-cpp-luna-SR2-linux-gtk-x86_64.tar.gz
4 6
5# tools for running 32-bit binaries 7# latest GCC-ARM version
8GCC_ARM_URL=https://launchpad.net/gcc-arm-embedded/4.8/4.8-2014-q3-update/+download/gcc-arm-none-eabi-4_8-2014q3-20140805-linux.tar.bz2
9
10# install tools for running 32-bit binaries
6apt-get update 11apt-get update
7sudo apt-get install -y lib32z1 lib32ncurses5 lib32bz2-1.0 12sudo apt-get install -y lib32z1 lib32ncurses5 lib32bz2-1.0
8 13
9# eclipse IDE 14# install Java
10# sudo apt-get install -y eclipse 15sudo apt-get install -y openjdk-7-jdk
11 16
12# download the compiler 17# download Eclipse
13wget -O /tmp/gcc-arm.tar.bz2 https://launchpad.net/gcc-arm-embedded/4.9/4.9-2015-q1-update/+download/gcc-arm-none-eabi-4_9-2015q1-20150306-linux.tar.bz2 18wget -O /tmp/eclipse.tar.gz $ECLIPSE_URL
19
20# Extract files
21sudo tar zxvf /tmp/eclipse.tar.gz -C /opt/
22# Change ownership to root
23sudo chown -R root:root /opt/eclipse/
24
25# Create launch script in /usr/bin
26echo '#!/bin/sh' | sudo tee /usr/bin/eclipse
27echo 'export ECLIPSE_HOME="/opt/eclipse"' | sudo tee -a /usr/bin/eclipse
28echo '$ECLIPSE_HOME/eclipse $*' | sudo tee -a /usr/bin/eclipse
29sudo chmod 755 /usr/bin/eclipse
30
31# Create menu entry
32echo "[Desktop Entry]" | sudo tee /usr/share/applications/eclipse.desktop
33echo "Name=Eclipse IDE" | sudo tee -a /usr/share/applications/eclipse.desktop
34echo "Comment=Integrated Development Environment" | sudo tee -a /usr/share/applications/eclipse.desktop
35echo "TryExec=/usr/bin/eclipse" | sudo tee -a /usr/share/applications/eclipse.desktop
36echo "Exec=/usr/bin/eclipse" | sudo tee -a /usr/share/applications/eclipse.desktop
37echo "Icon=/opt/eclipse/icon.xpm" | sudo tee -a /usr/share/applications/eclipse.desktop
38echo "Categories=Development;IDE;Java;" | sudo tee -a /usr/share/applications/eclipse.desktop
39echo "Terminal=false" | sudo tee -a /usr/share/applications/eclipse.desktop
40echo "Type=Application" | sudo tee -a /usr/share/applications/eclipse.desktop
41echo "StartupNotify=true" | sudo tee -a /usr/share/applications/eclipse.desktop
42
43# Delete the eclipse tarball
44rm /tmp/eclipse.tar.gz
45
46# download the gcc-arm tools
47wget -O /tmp/gcc-arm.tar.bz2 $GCC_ARM_URL
14 48
15# expand and add it to the path (yes, I know it says don't ever do this - but this is a Vagrant box ;) 49# expand and add it to the path (yes, I know it says don't ever do this - but this is a Vagrant box ;)
16cd /usr/local 50sudo tar xjf /tmp/gcc-arm.tar.bz2 -C /usr/local
17sudo tar xjf /tmp/gcc-arm.tar.bz2
18
19# cd to the /vagrant dir on login
20echo "cd /vagrant" >> /home/vagrant/.bashrc
21 51
22# set up path to arm gcc on login 52# set up path to arm gcc on login
23echo "export PATH=/usr/local/gcc-arm-none-eabi-4_9-2015q1/bin:$PATH" >> /home/vagrant/.bashrc 53sudo echo "export PATH=/usr/local/gcc-arm-none-eabi-4_8-2014q3/bin:$PATH" >> /etc/profile.d/env.sh
24 54
55# Delete the gcc-arm tarball
56rm /tmp/gcc-arm.tar.bz2
25 57
58# helpfully cd to the /vagrant dir on login
59echo "cd /vagrant" >> /home/vagrant/.bashrc
diff --git a/include/app.h b/include/app.h
new file mode 100644
index 0000000..51eef58
--- /dev/null
+++ b/include/app.h
@@ -0,0 +1,84 @@
1//
2// app.h
3//
4
5#ifndef LAUNCHPAD_APP_H
6#define LAUNCHPAD_APP_H
7
8// ____________________________________________________________________________
9//
10#include "app_defs.h"
11
12// ____________________________________________________________________________
13//
14// Interface to the hardware (implemented in launchpad_pro.a library)
15// ____________________________________________________________________________
16
17/**
18 * Set an LED's RGB value. This function is safe to call from any
19 * of the app functions below, at any time.
20 */
21void hal_plot_led(u8 type, u8 index, u8 red, u8 green, u8 blue);
22
23/**
24 * Send a MIDI message to either USB port or to the DIN output.
25 */
26void hal_send_midi(u8 port, u8 status, u8 d1, u8 d2);
27
28/**
29 * Send system exclusive to USB or DIN. Messages must be correctly formatted
30 * (F0 ... F7) and must not exceed 320 bytes.
31 */
32void hal_send_sysex(u8 port, const u8* data, u16 length);
33
34// ____________________________________________________________________________
35//
36// Interface from the hardware (implemented in app.c)
37// ____________________________________________________________________________
38
39/**
40 * Called on startup, this is a good place to do any initialisation.
41 */
42void app_init();
43
44/**
45 * 1kHz (1ms) timer. You can set LEDs and send MIDI out from this function,
46 * but you will get LED tearing as there is (currently) no double buffering.
47 *
48 * You will get jitter.
49 */
50void app_timer_event();
51
52/**
53 * Called when a MIDI message is received from USB or DIN.
54 */
55void app_midi_event(u8 port, u8 status, u8 d1, u8 d2);
56
57/**
58 * As above, but for system exclusive messages. Low level hardware buffering sets
59 * a maximum message size of 320 bytes, messages larger than this will not work.
60 */
61void app_sysex_event(u8 port, u8 * data, u16 count);
62
63/**
64 * Called when a MIDI DIN breakout cable is connected or disconnected. Note that
65 * you can still write MIDI events to the DIN ports even if no cable is connected.
66 */
67void app_cable_event(u8 type, u8 value);
68
69/**
70 * Called when the user presses or releases any button or pad on the control surface.
71 */
72void app_surface_event(u8 type, u8 index, u8 value);
73
74/**
75 * Called when the low level pad scanning reports an aftertouch (pad pressure) event.
76 * A pad press event will always come first. Note that the factory firmware sets a
77 * threshold to prevent excessive aftertouch after the initial hit, at the expense of
78 * dynamic range. This firmware does not - the full range of the pad is transmitted,
79 * with the side effect that aftertouch onset is more rapid.
80 */
81void app_aftertouch_event(u8 index, u8 value);
82
83
84#endif
diff --git a/include/app_defs.h b/include/app_defs.h
new file mode 100644
index 0000000..b339ed1
--- /dev/null
+++ b/include/app_defs.h
@@ -0,0 +1,65 @@
1
2#ifndef APP_TYPES_H
3#define APP_TYPES_H
4
5// ____________________________________________________________________________
6//
7// Types
8// ____________________________________________________________________________
9
10typedef signed long s32;
11typedef signed short s16;
12typedef signed char s8;
13
14typedef unsigned long u32;
15typedef unsigned short u16;
16typedef unsigned char u8;
17
18// ____________________________________________________________________________
19//
20// App structure
21// ____________________________________________________________________________
22
23#define TYPEPAD 0
24#define TYPESETUP 1
25
26#define MAXLED 63
27
28// ____________________________________________________________________________
29//
30// Useful MIDI constants
31// ____________________________________________________________________________
32
33#define NOTEON 0x90
34#define NOTEOFF 0x80
35#define POLYAFTERTOUCH 0xA0
36#define CC 0xB0
37#define CHANNELAFTERTOUCH 0xD0
38#define SONGPOSITIONPOINTER 0xF2
39#define MIDITIMINGCLOCK 0xF8
40#define MIDISTART 0xFA
41#define MIDICONTINUE 0xFB
42#define MIDISTOP 0xFC
43
44// ____________________________________________________________________________
45//
46// MIDI ports
47// ____________________________________________________________________________
48
49// USB MIDI: "Standalone" port
50#define USBSTANDALONE 0
51
52// USB MIDI: "MIDI" port
53#define USBMIDI 1
54
55// MIDI DIN port
56#define DINMIDI 2
57
58
59// MIDI Jack Socket Switch IDs
60
61#define MIDI_IN_CABLE 0
62#define MIDI_OUT_CABLE 1
63
64// ____________________________________________________________________________
65#endif
diff --git a/resources/Launchpad Pro-1.0.154.syx b/resources/Launchpad Pro-1.0.154.syx
new file mode 100755
index 0000000..b6bf226
--- /dev/null
+++ b/resources/Launchpad Pro-1.0.154.syx
Binary files differ
diff --git a/src/app.c b/src/app.c
new file mode 100644
index 0000000..662007e
--- /dev/null
+++ b/src/app.c
@@ -0,0 +1,92 @@
1//______________________________________________________________________________
2//
3// Headers
4//______________________________________________________________________________
5
6#include "app.h"
7
8//______________________________________________________________________________
9
10void app_surface_event(u8 type, u8 index, u8 value)
11{
12 switch (type)
13 {
14 case TYPEPAD:
15 {
16 // example - light / extinguish pad LEDs, send MIDI
17 hal_plot_led(TYPEPAD, index, value, value, value);
18 hal_send_midi(USBSTANDALONE, NOTEON | 0, index, value);
19 }
20 break;
21
22 case TYPESETUP:
23 {
24 // example - light the setup LED
25 hal_plot_led(TYPESETUP, 0, value, value, value);
26 }
27 break;
28 }
29}
30
31void app_midi_event(u8 port, u8 status, u8 d1, u8 d2)
32{
33 // example - MIDI interface functionality for USB "MIDI" port -> DIN port
34 if (port == USBMIDI)
35 {
36 hal_send_midi(DINMIDI, status, d1, d2);
37 }
38
39 // // example -MIDI interface functionality for DIN -> USB "MIDI" port port
40 if (port == DINMIDI)
41 {
42 hal_send_midi(USBMIDI, status, d1, d2);
43 }
44}
45
46void app_sysex_event(u8 port, u8 * data, u16 count)
47{
48}
49
50void app_aftertouch_event(u8 index, u8 value)
51{
52 // example - send poly aftertouch to MIDI ports
53 hal_send_midi(USBMIDI, POLYAFTERTOUCH | 0, index, value);
54
55 // example - set LED to white, brightness in proportion to pressure
56 hal_plot_led(TYPEPAD, index, value/2, value/2, value/2);
57}
58
59void app_cable_event(u8 type, u8 value)
60{
61 // example - light the Setup LED to indicate cable connection
62 if (type == MIDI_IN_CABLE)
63 {
64 hal_plot_led(TYPESETUP, 0, 0, value, 0);
65 }
66 else if (type == MIDI_OUT_CABLE)
67 {
68 hal_plot_led(TYPESETUP, 0, value, 0, 0);
69 }
70}
71
72void app_timer_event()
73{
74}
75
76//______________________________________________________________________________
77
78void app_init()
79{
80 // example - light the LEDs to say hello!
81 for (int i=0; i < 10; ++i)
82 {
83 for (int j=0; j < 10; ++j)
84 {
85 u8 r = i < 5 ? (MAXLED * (5-i))/5 : 0;
86 u8 g = i < 5 ? (MAXLED * i)/5 : (MAXLED * (10-i))/5;
87 u8 b = i < 5 ? 0 : (MAXLED * (i-5))/5;
88
89 hal_plot_led(TYPEPAD, j*10 + i, r, b, g);
90 }
91 }
92}
diff --git a/stm32_flash.ld b/stm32_flash.ld
new file mode 100644
index 0000000..c66fd66
--- /dev/null
+++ b/stm32_flash.ld
@@ -0,0 +1,143 @@
1/* Entry Point */
2ENTRY(Reset_Handler)
3
4/* Highest address of the user mode stack */
5_estack = 0x20005000; /* end of 20K RAM */
6
7/* Generate a link error if heap and stack don't fit into RAM */
8_Min_Heap_Size = 0; /* required amount of heap */
9_Min_Stack_Size = 0x200; /* required amount of stack */
10_Reserve_Stack_Value = 4;
11
12/* Specify the memory areas. Set FLASH ORIGIN to 0x08000000 for debugging. */
13MEMORY
14{
15 FLASH (rx) : ORIGIN = 0x08006400, LENGTH = 128K
16 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
17 MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
18}
19
20/* Define output sections */
21SECTIONS
22{
23 /* The startup code goes first into FLASH */
24 .isr_vector :
25 {
26 . = ALIGN(4);
27 KEEP(*(.isr_vector)) /* Startup code */
28 . = ALIGN(4);
29 } >FLASH
30
31 /* The program code and other data goes into FLASH */
32 .text :
33 {
34 . = ALIGN(4);
35 *(.text) /* .text sections (code) */
36 *(.text*) /* .text* sections (code) */
37 *(.rodata) /* .rodata sections (constants, strings, etc.) */
38 *(.rodata*) /* .rodata* sections (constants, strings, etc.) */
39 *(.glue_7) /* glue arm to thumb code */
40 *(.glue_7t) /* glue thumb to arm code */
41
42 KEEP (*(.init))
43 KEEP (*(.fini))
44
45 . = ALIGN(4);
46 _etext = .; /* define a global symbols at end of code */
47 } >FLASH
48
49
50 .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
51 .ARM : {
52 __exidx_start = .;
53 *(.ARM.exidx*)
54 __exidx_end = .;
55 } >FLASH
56
57 .ARM.attributes : { *(.ARM.attributes) } > FLASH
58
59 .preinit_array :
60 {
61 PROVIDE_HIDDEN (__preinit_array_start = .);
62 KEEP (*(.preinit_array*))
63 PROVIDE_HIDDEN (__preinit_array_end = .);
64 } >FLASH
65 .init_array :
66 {
67 PROVIDE_HIDDEN (__init_array_start = .);
68 KEEP (*(SORT(.init_array.*)))
69 KEEP (*(.init_array*))
70 PROVIDE_HIDDEN (__init_array_end = .);
71 } >FLASH
72 .fini_array :
73 {
74 PROVIDE_HIDDEN (__fini_array_start = .);
75 KEEP (*(.fini_array*))
76 KEEP (*(SORT(.fini_array.*)))
77 PROVIDE_HIDDEN (__fini_array_end = .);
78 } >FLASH
79
80 /* used by the startup to initialize data */
81 _sidata = .;
82
83 /* Initialized data sections goes into RAM, load LMA copy after code */
84 .data : AT ( _sidata )
85 {
86 . = ALIGN(4);
87 _sdata = .; /* create a global symbol at data start */
88 *(.data) /* .data sections */
89 *(.data*) /* .data* sections */
90
91 . = ALIGN(4);
92 _edata = .; /* define a global symbol at data end */
93 } >RAM
94
95 /* Uninitialized data section */
96 . = ALIGN(4);
97 .bss :
98 {
99 /* This is used by the startup in order to initialize the .bss secion */
100 _sbss = .; /* define a global symbol at bss start */
101 __bss_start__ = _sbss;
102 *(.bss)
103 *(.bss*)
104 *(COMMON)
105
106 . = ALIGN(4);
107 _ebss = .; /* define a global symbol at bss end */
108 __bss_end__ = _ebss;
109 } >RAM
110
111 PROVIDE ( end = _ebss );
112 PROVIDE ( _end = _ebss );
113
114 /* User_heap_stack section, used to check that there is enough RAM left */
115 /* also reserve space for the stack overun protection value */
116 ._user_heap_stack :
117 {
118 . = ALIGN(4);
119 . = . + _Min_Heap_Size;
120 . = ALIGN(4);
121 . = . + _Reserve_Stack_Value ;
122 . = ALIGN(4);
123 . = . + _Min_Stack_Size;
124 . = ALIGN(4);
125 } >RAM
126
127 /* MEMORY_bank1 section, code must be located here explicitly */
128 .memory_b1_text :
129 {
130 *(.mb1text) /* .mb1text sections (code) */
131 *(.mb1text*) /* .mb1text* sections (code) */
132 *(.mb1rodata) /* read-only data (constants) */
133 *(.mb1rodata*)
134 } >MEMORY_B1
135
136 /* Remove information from the standard libraries */
137 /DISCARD/ :
138 {
139 libc.a ( * )
140 libm.a ( * )
141 libgcc.a ( * )
142 }
143}
diff --git a/tools/hextosyx.cpp b/tools/hextosyx.cpp
new file mode 100644
index 0000000..fd761b2
--- /dev/null
+++ b/tools/hextosyx.cpp
@@ -0,0 +1,152 @@
1#include <iostream>
2#include <fstream>
3
4// Ported from the original Delphi version.
5// CLI parameters for ID, ByteWidth and BaseAddress removed for simplicity
6
7#include "intelhex.h"
8
9static const int ByteWidth = 32;
10static const int ID = 0x0051;
11
12static const unsigned char RESET[] = {0xf0, 0x00, 0x20, 0x29, 0x00, 0x71};
13
14// must match unpacking code in the bootloader, obviously
15static void eight_to_seven(unsigned char * Output, const int OOffset,
16 intelhex::hex_data& Input, const unsigned long IOffset)
17{
18 for (int i = 0; i < 7; ++i)
19 {
20 if (!Input.is_set(IOffset + i))
21 {
22 // pad unset addresses
23 Input.set(IOffset + i, 0xff);
24 }
25 }
26
27 // seven bytes of eight-bit data converted to
28 // eight bytes of seven-bit data
29 // render 8-bit words as 16-bit words
30 Output[OOffset+0] = Input[IOffset+0] >> 1;
31 Output[OOffset+1] = (Input[IOffset+0] << 6) + (Input[IOffset+1] >> 2);
32 Output[OOffset+2] = (Input[IOffset+1] << 5) + (Input[IOffset+2] >> 3);
33 Output[OOffset+3] = (Input[IOffset+2] << 4) + (Input[IOffset+3] >> 4);
34 Output[OOffset+4] = (Input[IOffset+3] << 3) + (Input[IOffset+4] >> 5);
35 Output[OOffset+5] = (Input[IOffset+4] << 2) + (Input[IOffset+5] >> 6);
36 Output[OOffset+6] = (Input[IOffset+5] << 1) + (Input[IOffset+6] >> 7);
37 Output[OOffset+7] = (Input[IOffset+6]);
38
39 for (int i = 0; i < 8; ++i)
40 {
41 Output[OOffset+i] &= 0x7f;
42 }
43}
44
45static void write_block(intelhex::hex_data& data, std::ofstream& ofs, const unsigned long addr, const unsigned char type)
46{
47 // packet header
48 ofs.write(reinterpret_cast<const char*>(RESET), 5);
49 ofs.put(type);
50
51 int incount = 0;
52 int outcount = 0;
53 int outn = 1 + (ByteWidth * 8) / 7;
54
55 // payload
56 unsigned char payload[41];
57
58 while (incount < ByteWidth)
59 {
60 eight_to_seven(payload, outcount, data, addr+incount);
61
62 incount += 7;
63 outcount += 8;
64 }
65
66 ofs.write(reinterpret_cast<const char*>(payload), outn);
67 ofs.put(0xf7);
68}
69
70static void write_header(intelhex::hex_data& data, std::ofstream& ofs, size_t BaseAddress)
71{
72 // human-readable version number & header block
73 ofs.write(reinterpret_cast<const char*>(RESET), 6);
74 ofs.put(ID >> 8);
75 ofs.put(ID & 0x7f);
76
77 ofs.put(data[BaseAddress + 0x132] >> 4);
78 ofs.put(data[BaseAddress + 0x132] & 0x0f);
79 ofs.put(data[BaseAddress + 0x131] >> 4);
80 ofs.put(data[BaseAddress + 0x131] & 0x0f);
81 ofs.put(data[BaseAddress + 0x130] >> 4);
82 ofs.put(data[BaseAddress + 0x130] & 0x0f);
83
84 ofs.put(0xf7);
85}
86
87static void write_checksum(intelhex::hex_data& data, std::ofstream& ofs)
88{
89 // device doesn't respect the checksum, but we still need this block!
90 ofs.write(reinterpret_cast<const char*>(RESET), 5);
91 unsigned char payload[19];
92
93 const char *FIRMWARE = "Firmware";
94
95 payload[0] = 0x76;
96 payload[1] = 0x00;
97 for (int i = 0; i < 8; ++i)
98 {
99 payload[i+2] = FIRMWARE[i];
100 }
101 payload[10] = 0x00;
102 payload[11] = 0x00;
103 payload[12] = 0x00;
104 payload[13] = 0x00;
105 payload[14] = 0x00;
106 payload[15] = 0x00;
107 payload[16] = 0x00;
108 payload[17] = 0x00;
109 payload[18] = 0xf7;
110
111 ofs.write(reinterpret_cast<const char*>(payload), 19);
112}
113
114int main(int argc, char *argv[])
115{
116 std::cout << "converting " << argv[1] << " to sysex file: " << argv[2] << std::endl;
117
118 // read the hex file input
119 intelhex::hex_data data;
120 data.load(argv[1]);
121
122 size_t BaseAddress = data.min_address();
123 size_t MaxAddress = data.max_address();
124
125 std::cout << "max addr: " << std::hex << data.max_address() << " min_addr: " << std::hex << data.min_address() << std::endl;
126
127 // create output file
128 std::ofstream ofs(argv[2] , std::ios::out | std::ios::binary);
129 if( !ofs )
130 return -1;
131
132 write_header(data, ofs, BaseAddress);
133
134 // payload blocks...
135 unsigned long i = BaseAddress + ByteWidth;
136
137 while (i < MaxAddress)
138 {
139 write_block(data, ofs, i, 0x72);
140
141 i += ByteWidth;
142 }
143
144 write_block(data, ofs, BaseAddress, 0x73);
145
146 // footer/checksum block
147 write_checksum(data, ofs);
148
149 ofs.close();
150
151 return 0;
152} \ No newline at end of file