summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Hodder <dave.hodder@focusrite.com>2017-11-20 13:54:16 +0000
committerDave Hodder <dave.hodder@focusrite.com>2017-11-20 13:54:16 +0000
commitd910a607b308c5ed02e476a5051fac184f61c86f (patch)
treeda05174d3c8c6846359e4d16f0cee06bb4703aba
parentaf0c758cceb310b69c9192d7cdc8133ba63a3f98 (diff)
parent7ff741e4206b75e941aa982360402bc8f8e58198 (diff)
downloadlaunchpad-polymaker-d910a607b308c5ed02e476a5051fac184f61c86f.tar.gz
launchpad-polymaker-d910a607b308c5ed02e476a5051fac184f61c86f.zip
Merge branch 'raw-adc' (bringing in ADC and flash access)
-rw-r--r--README.md17
-rw-r--r--include/app.h43
-rw-r--r--include/app_defs.h28
-rw-r--r--lib/launchpad_pro.abin16869730 -> 16874030 bytes
-rw-r--r--src/app.c153
-rw-r--r--tools/simulator.c14
6 files changed, 179 insertions, 76 deletions
diff --git a/README.md b/README.md
index cb9d039..b1c6b60 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,6 @@ Open source firmware for the Novation Launchpad Pro grid controller! By customi
9 9
10You'll definitely need *some* C programming experience, but we've deliberately kept much of the firmwarey nastiness tucked away, to make the process a little friendlier. 10You'll definitely need *some* C programming experience, but we've deliberately kept much of the firmwarey nastiness tucked away, to make the process a little friendlier.
11 11
12This project is still at an early stage, and no "interesting" example apps have yet been developed. You might want to hang on until there's something more detailed before you get stuck in. Or not!
13
14# Philosophy 12# Philosophy
15We 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 Pro, which comprises a build environment, application entry points / API, and a library of low level source code. Our reasoning is as follows: 13We 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 Pro, which comprises a build environment, application entry points / API, and a library of low level source code. Our reasoning is as follows:
16 14
@@ -71,9 +69,9 @@ Once your new "box" is up and running, you can build the app in one of two ways.
71 69
72Either of the above methods will generate the firmware image, `launchpad_pro.syx`, in the project `build` directory. You can then upload this to your Launchpad Pro from the host! 70Either of the above methods will generate the firmware image, `launchpad_pro.syx`, in the project `build` directory. You can then upload this to your Launchpad Pro from the host!
73 71
74## Using OS X 72## Using macOS
75 73
76On OS X you can easily install the GCC ARM toolchain using the [homebrew package manager](http://brew.sh). The EABI tools are maintained in an external repository which you need to put on tap first. You can then run ```make``` to directly compile the code: 74On macOS you can easily install the GCC ARM toolchain using the [homebrew package manager](http://brew.sh). The EABI tools are maintained in an external repository which you need to put on tap first. You can then run ```make``` to directly compile the code:
77 75
78``` 76```
79brew tap PX4/homebrew-px4 77brew tap PX4/homebrew-px4
@@ -82,7 +80,7 @@ make
82``` 80```
83 81
84# Uploading to a Launchpad Pro 82# Uploading to a Launchpad Pro
85Now 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. 83Now 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 macOS, and [MIDI OX](http://www.midiox.com/) on Windows. On Linux, I'll bet you already have a tool in mind.
86 84
87I 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: 85I 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:
88 86
@@ -109,17 +107,16 @@ By calling into the HAL, your app can:
109 107
110- Write colours to the LEDs 108- Write colours to the LEDs
111- Send messages to the MIDI/USB ports 109- Send messages to the MIDI/USB ports
110- Store and recall a little bit of data on the Launchpad Pro's flash memory
112 111
113The best way to learn about these is to read the documentation in `app.h`, and to study the (very basic) example code! 112The best way to learn about these is to read the documentation in `app.h`, and to study the (very basic) example code!
114 113
115Currently the HAL/app interface does not support reading or writing the flash memory.
116
117# Debugging 114# Debugging
118We 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. 115We 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.
119 116
120Instead, you're going to have to do things the old fashioned way - by blinking LEDs or sending MIDI messages (though hopefully no need for a 'scope!). For what it's worth, that's the way I've developed this version of the firmware - dogfooding all the way ;) 117Instead, you're going to have to do things the old fashioned way - by blinking LEDs or sending MIDI messages (though hopefully no need for a 'scope!). For what it's worth, that's the way I've developed this version of the firmware - dogfooding all the way ;)
121 118
122If do you want to debug interactively (and of course you do), you can use the interactive desktop simulator on OS X. 119If do you want to debug interactively (and of course you do), you can use the interactive desktop simulator on macOS:
123 120
1241. Build the Xcode project located in `/tools/osx` 1211. Build the Xcode project located in `/tools/osx`
1252. Connect your Launchpad Pro 1222. Connect your Launchpad Pro
@@ -127,7 +124,7 @@ If do you want to debug interactively (and of course you do), you can use the in
1274. Put the Launchpad Pro into "Programmer" mode using the Setup button (you'll get odd behaviour otherwise) 1244. Put the Launchpad Pro into "Programmer" mode using the Setup button (you'll get odd behaviour otherwise)
1285. Start debugging in Xcode! 1255. Start debugging in Xcode!
129 126
130Currently it only supports button presses and LED messages - there's no virtual MIDI IO, setup button or aftertouch (yet). It has a really awful busywaiting timer for the 1kHz tick. However, it does allow you to debug your application logic using Xcode! 127Currently it only supports button presses and LED messages - there's no setup button, flash storage or aftertouch (yet). It has a really awful busywaiting timer for the 1kHz tick. However, it does allow you to debug your application logic using Xcode!
131 128
132You can also use the simple command-line simulator located in the `/tools` directory. It is compiled and ran as part of the build process, so it serves as a very basic test of your app before it is baked into a sysex dump - more of a test harness. 129You can also use the simple command-line simulator located in the `/tools` directory. It is compiled and ran as part of the build process, so it serves as a very basic test of your app before it is baked into a sysex dump - more of a test harness.
133 130
@@ -142,7 +139,7 @@ To debug the simulator interactively in Eclipse:
142# The Hardware 139# The Hardware
143The Launchpad Pro is based around an ARM Cortex M3 from STMicroelectronics. Specifically, an [STM32F103RBT6](http://www.st.com/web/catalog/mmc/FM141/SC1169/SS1031/LN1565/PF164487). It's clocked at 72MHz, and has 20k RAM (I'm not sure how much of this we're using in the open build yet - should be a fair amount left but I haven't measured it). The low level LED multiplexing and pad/switch scanning consume a fair bit of CPU time in interrupt mode, but have changed a little in the open firmware library (so again, I don't have measurements for how many cycles they're using). 140The Launchpad Pro is based around an ARM Cortex M3 from STMicroelectronics. Specifically, an [STM32F103RBT6](http://www.st.com/web/catalog/mmc/FM141/SC1169/SS1031/LN1565/PF164487). It's clocked at 72MHz, and has 20k RAM (I'm not sure how much of this we're using in the open build yet - should be a fair amount left but I haven't measured it). The low level LED multiplexing and pad/switch scanning consume a fair bit of CPU time in interrupt mode, but have changed a little in the open firmware library (so again, I don't have measurements for how many cycles they're using).
144 141
145It has 128k of flash memory, but I'm pretty sure we won't be exposing all of it as part of this API (dangerously easy to corrupt things!). Current thinking is to expose a few pages for use by apps, though this would still introduce risks (accidentally wearing out sectors, for example). To be continued... 142It has 128k of flash memory, but we won't be exposing all of it as part of this API (dangerously easy to corrupt things!).
146 143
147# Vagrant tips 144# Vagrant tips
148When you're done developing, simply type `vagrant suspend` to halt your VM without destroying it - this will make `vagrant up` a lot quicker next time. If you're really finished, `vagrant destroy` will completely remove the VM from your system (but not any of your code). 145When you're done developing, simply type `vagrant suspend` to halt your VM without destroying it - this will make `vagrant up` a lot quicker next time. If you're really finished, `vagrant destroy` will completely remove the VM from your system (but not any of your code).
diff --git a/include/app.h b/include/app.h
index ed946d5..96801eb 100644
--- a/include/app.h
+++ b/include/app.h
@@ -43,7 +43,7 @@
43 43
44/****************************************************************************** 44/******************************************************************************
45 Button indexing is as follows - numbers in brackets do not correspond to real 45 Button indexing is as follows - numbers in brackets do not correspond to real
46buttons, but can be harmessly sent in hal_set_led. 46 buttons, but can be harmessly sent in hal_set_led.
47 47
48 (90)91 92 93 94 95 96 97 98 (99) 48 (90)91 92 93 94 95 96 97 98 (99)
49 ....... 49 .......
@@ -92,6 +92,40 @@ void hal_send_midi(u8 port, u8 status, u8 data1, u8 data2);
92 */ 92 */
93void hal_send_sysex(u8 port, const u8* data, u16 length); 93void hal_send_sysex(u8 port, const u8* data, u16 length);
94 94
95/**
96 * Read some data from flash.
97 *
98 * Flash storage is in a single block, currently corresponding to one page.
99 * The block is always USER_AREA_SIZE bytes long. You can read/write any bytes
100 * within it - you do not need to worry about paging, that's handled by the HAL.
101 *
102 * The block size may increase to span multiple pages in future :)
103 *
104 * @param offset - how far into the USER_AREA_SIZE byte block to start reading
105 * @param data - buffer to receive data (must be at least length bytes long)
106 * @param length - bytes to read
107 *
108 * Attempts to read beyond the end of the block will fail silently.
109 *
110 * Note that your first ever read from a new device will contain 0xFF's until
111 * you overwrite them.
112 */
113void hal_read_flash(u32 offset, u8 *data, u32 length);
114
115/**
116 * Write data to flash
117 *
118 * Do take care to avoid thrashing it, as you can wear it out with excessive
119 * writes. The HAL does not currently do anything clever to mitigate this.
120 *
121 * @param offset - how far into the USER_AREA_SIZE byte block to start writing
122 * @param data - buffer to write (must be at least length bytes long)
123 * @param length - bytes to write
124 *
125 * Attempts to write beyond the end of the block will fail silently
126 */
127void hal_write_flash(u32 offset,const u8 *data, u32 length);
128
95// ____________________________________________________________________________ 129// ____________________________________________________________________________
96// 130//
97// Callbacks from the hardware (implemented in your app.c) 131// Callbacks from the hardware (implemented in your app.c)
@@ -99,8 +133,13 @@ void hal_send_sysex(u8 port, const u8* data, u16 length);
99 133
100/** 134/**
101 * Called on startup, this is a good place to do any initialisation. 135 * Called on startup, this is a good place to do any initialisation.
136 *
137 * @param adc_buffer - this is a pointer to the raw ADC frame buffer. The
138 * data is 12 bit unsigned. Note the indexing is strange -
139 * translate ADC indices to LED/button indices using the
140 * ADC_MAP table declared in app_defs.h.
102 */ 141 */
103void app_init(); 142void app_init(const u16 *adc_buffer);
104 143
105/** 144/**
106 * 1kHz (1ms) timer. You can set LEDs and send MIDI out from this function, 145 * 1kHz (1ms) timer. You can set LEDs and send MIDI out from this function,
diff --git a/include/app_defs.h b/include/app_defs.h
index 829f8a6..d9b4311 100644
--- a/include/app_defs.h
+++ b/include/app_defs.h
@@ -100,5 +100,33 @@ typedef unsigned char u8;
100#define MIDI_OUT_CABLE 1 100#define MIDI_OUT_CABLE 1
101 101
102// ____________________________________________________________________________ 102// ____________________________________________________________________________
103//
104// Flash storage
105// ____________________________________________________________________________
106
107#define USER_AREA_SIZE 1024
108
109// ____________________________________________________________________________
110//
111// Raw ADC reads. For technical reasons, the ADC layout is not the same as the
112// LED layout. Be sure to translate ADC values using the table below as you
113// need them!
114// ____________________________________________________________________________
115
116#define PAD_COUNT 64
117
118static const u8 ADC_MAP[PAD_COUNT] =
119{
120 11, 51, 12, 52, 13, 53, 14, 54,
121 15, 55, 16, 56, 17 ,57, 18, 58,
122 21, 61, 22, 62, 23, 63, 24, 64,
123 25, 65, 26, 66, 27, 67, 28, 68,
124 31, 71, 32, 72, 33, 73, 34, 74,
125 35, 75, 36, 76, 37, 77, 38, 78,
126 41, 81, 42, 82, 43, 83, 44, 84,
127 45, 85, 46, 86, 47, 87, 48, 88,
128};
129
130// ____________________________________________________________________________
103 131
104#endif 132#endif
diff --git a/lib/launchpad_pro.a b/lib/launchpad_pro.a
index 712626d..c2c9af2 100644
--- a/lib/launchpad_pro.a
+++ b/lib/launchpad_pro.a
Binary files differ
diff --git a/src/app.c b/src/app.c
index f0edec8..5b83182 100644
--- a/src/app.c
+++ b/src/app.c
@@ -41,51 +41,57 @@
41// 41//
42// This is where the fun is! Add your code to the callbacks below to define how 42// This is where the fun is! Add your code to the callbacks below to define how
43// your app behaves. 43// your app behaves.
44//
45// In this example, we render the raw ADC data as LED rainbows.
46//______________________________________________________________________________
47
48static const u16 *g_ADC = 0;
49
44//______________________________________________________________________________ 50//______________________________________________________________________________
45 51
46void app_surface_event(u8 type, u8 index, u8 value) 52void app_surface_event(u8 type, u8 index, u8 value)
47{ 53{
48 switch (type) 54 switch (type)
49 { 55 {
50 case TYPEPAD: 56 case TYPEPAD:
51 { 57 {
52 // example - light / extinguish pad LEDs, send MIDI 58 // example - light / extinguish pad LEDs, send MIDI
53 hal_plot_led(TYPEPAD, index, value, value, value); 59 hal_plot_led(TYPEPAD, index, value, value, value);
54 hal_send_midi(DINMIDI, NOTEON | 0, index, value); 60 hal_send_midi(DINMIDI, NOTEON | 0, index, value);
55 } 61 }
56 break; 62 break;
57 63
58 case TYPESETUP: 64 case TYPESETUP:
59 { 65 {
60 // example - light the setup LED 66 // example - light the setup LED
61 hal_plot_led(TYPESETUP, 0, value, value, value); 67 hal_plot_led(TYPESETUP, 0, value, value, value);
62 } 68 }
63 break; 69 break;
64 } 70 }
65} 71}
66 72
67//______________________________________________________________________________ 73//______________________________________________________________________________
68 74
69void app_midi_event(u8 port, u8 status, u8 d1, u8 d2) 75void app_midi_event(u8 port, u8 status, u8 d1, u8 d2)
70{ 76{
71 // example - MIDI interface functionality for USB "MIDI" port -> DIN port 77 // example - MIDI interface functionality for USB "MIDI" port -> DIN port
72 if (port == USBMIDI) 78 if (port == USBMIDI)
73 { 79 {
74 hal_send_midi(DINMIDI, status, d1, d2); 80 hal_send_midi(DINMIDI, status, d1, d2);
75 } 81 }
76 82
77 // // example -MIDI interface functionality for DIN -> USB "MIDI" port port 83 // // example -MIDI interface functionality for DIN -> USB "MIDI" port port
78 if (port == DINMIDI) 84 if (port == DINMIDI)
79 { 85 {
80 hal_send_midi(USBMIDI, status, d1, d2); 86 hal_send_midi(USBMIDI, status, d1, d2);
81 } 87 }
82} 88}
83 89
84//______________________________________________________________________________ 90//______________________________________________________________________________
85 91
86void app_sysex_event(u8 port, u8 * data, u16 count) 92void app_sysex_event(u8 port, u8 * data, u16 count)
87{ 93{
88 // example - respond to UDI messages? 94 // example - respond to UDI messages?
89} 95}
90 96
91//______________________________________________________________________________ 97//______________________________________________________________________________
@@ -93,60 +99,81 @@ void app_sysex_event(u8 port, u8 * data, u16 count)
93void app_aftertouch_event(u8 index, u8 value) 99void app_aftertouch_event(u8 index, u8 value)
94{ 100{
95 // example - send poly aftertouch to MIDI ports 101 // example - send poly aftertouch to MIDI ports
96 hal_send_midi(USBMIDI, POLYAFTERTOUCH | 0, index, value); 102 hal_send_midi(USBMIDI, POLYAFTERTOUCH | 0, index, value);
97 103
98 // example - set LED to white, brightness in proportion to pressure 104 // example - set LED to white, brightness in proportion to pressure
99 hal_plot_led(TYPEPAD, index, value/2, value/2, value/2); 105 hal_plot_led(TYPEPAD, index, value/2, value/2, value/2);
100} 106}
101 107
102//______________________________________________________________________________ 108//______________________________________________________________________________
103 109
104void app_cable_event(u8 type, u8 value) 110void app_cable_event(u8 type, u8 value)
105{ 111{
106 // example - light the Setup LED to indicate cable connections 112 // example - light the Setup LED to indicate cable connections
107 if (type == MIDI_IN_CABLE) 113 if (type == MIDI_IN_CABLE)
108 { 114 {
109 hal_plot_led(TYPESETUP, 0, 0, value, 0); // green 115 hal_plot_led(TYPESETUP, 0, 0, value, 0); // green
110 } 116 }
111 else if (type == MIDI_OUT_CABLE) 117 else if (type == MIDI_OUT_CABLE)
112 { 118 {
113 hal_plot_led(TYPESETUP, 0, value, 0, 0); // red 119 hal_plot_led(TYPESETUP, 0, value, 0, 0); // red
114 } 120 }
115} 121}
116 122
117//______________________________________________________________________________ 123//______________________________________________________________________________
118 124
119
120void app_timer_event() 125void app_timer_event()
121{ 126{
122 // example - send MIDI clock at 125bpm 127 // example - send MIDI clock at 125bpm
123#define TICK_MS 20 128#define TICK_MS 20
124 129
125 static u8 ms = TICK_MS; 130 static u8 ms = TICK_MS;
126 131
127 if (++ms >= TICK_MS) 132 if (++ms >= TICK_MS)
133 {
134 ms = 0;
135
136 // send a clock pulse up the USB
137 hal_send_midi(USBSTANDALONE, MIDITIMINGCLOCK, 0, 0);
138 }
139
140/*
141 // alternative example - show raw ADC data as LEDs
142 for (int i=0; i < PAD_COUNT; ++i)
128 { 143 {
129 ms = 0; 144 // raw adc values are 12 bit, but LEDs are 6 bit.
145 // Let's saturate into r;g;b for a rainbow effect to show pressure
146 u16 r = 0;
147 u16 g = 0;
148 u16 b = 0;
149
150 u16 x = (3 * MAXLED * g_ADC[i]) >> 12;
151
152 if (x < MAXLED)
153 {
154 r = x;
155 }
156 else if (x >= MAXLED && x < (2*MAXLED))
157 {
158 r = MAXLED - x;
159 g = x - MAXLED;
160 }
161 else
162 {
163 g = MAXLED - x;
164 b = x - MAXLED;
165 }
130 166
131 // send a clock pulse up the USB 167 hal_plot_led(TYPEPAD, ADC_MAP[i], r, g, b);
132 hal_send_midi(USBSTANDALONE, MIDITIMINGCLOCK, 0, 0);
133 } 168 }
169 */
134} 170}
135 171
136//______________________________________________________________________________ 172//______________________________________________________________________________
137 173
138void app_init() 174void app_init(const u16 *adc_raw)
139{ 175{
140 // example - light the LEDs to say hello! 176
141 for (int i=0; i < 10; ++i) 177 // store off the raw ADC frame pointer for later use
142 { 178 g_ADC = adc_raw;
143 for (int j=0; j < 10; ++j)
144 {
145 u8 r = i < 5 ? (MAXLED * (5-i))/5 : 0;
146 u8 g = i < 5 ? (MAXLED * i)/5 : (MAXLED * (10-i))/5;
147 u8 b = i < 5 ? 0 : (MAXLED * (i-5))/5;
148
149 hal_plot_led(TYPEPAD, j*10 + i, r, b, g);
150 }
151 }
152} 179}
diff --git a/tools/simulator.c b/tools/simulator.c
index 56298d7..138277e 100644
--- a/tools/simulator.c
+++ b/tools/simulator.c
@@ -57,16 +57,28 @@ void hal_send_sysex(u8 port, const u8* data, u16 length)
57 printf("...hal_send_midi(%d, (data), %d);\n", port, length); 57 printf("...hal_send_midi(%d, (data), %d);\n", port, length);
58} 58}
59 59
60void hal_read_flash(u32 offset, u8 *data, u32 length)
61{
62 printf("...hal_read_flash(%d, (data), %d);\n", offset, length);
63}
64
65void hal_write_flash(u32 offset,const u8 *data, u32 length)
66{
67 printf("...hal_write_flash(%d, (data), %d);\n", offset, length);
68}
69
60// ____________________________________________________________________________ 70// ____________________________________________________________________________
61// 71//
62// App event wrappers - these just log to the console. Would be nice to wire 72// App event wrappers - these just log to the console. Would be nice to wire
63// these up to a MIDI input from the real Launchpad Pro! 73// these up to a MIDI input from the real Launchpad Pro!
64// ____________________________________________________________________________ 74// ____________________________________________________________________________
65 75
76static u16 raw_ADC[64];
77
66static void sim_app_init() 78static void sim_app_init()
67{ 79{
68 printf("calling app_init()...\n"); 80 printf("calling app_init()...\n");
69 app_init(); 81 app_init(raw_ADC);
70} 82}
71 83
72static void sim_app_surface_event(u8 type, u8 index, u8 value) 84static void sim_app_surface_event(u8 type, u8 index, u8 value)