using 4 dio pins as a 4 bit dac

Dear Forum, 

to drive electro magnetic actuators with some velocity sensitivity i am thinking about

-instead of using 4 expensive Aout modules with the need of bipolar power:

  • mapping midi note on and velocity to 3 or 4 dio pins with a resistor ladder to get 8 or 16 voltage levels. I would tune the resistors to get a custom curve because solenoids need a minimum impulse to get moving (initial lowest level around 50%, then linear to 100% from there).

Is this a reasonable idea?

What Midibox App is a good starting point? Can this be done with MidiboxNG? From reading through the Manual i assume yes, but is it reasonable and fast enough for 24 3 or 4 bit outs?

(Midifying a 2 octave Glockenspiel)

Or should i start from scratch with a new mios32 app? I know a little C and i have worked through most of the tutorials… but not much more. The documentation is superb, but still i feel a little lost…

My solution is sure to have a lot of if statements, propably hard coding every velocity of every note value by hand…

 

Or is it necessary to program a driver for a new “Midibox Aout_4Bit” module? (<- would be the thing i feel least confident about, no idea where to start…)

 

As an alternative i tried using the pwm signal from dimmed LEDs in MidiboxNG, but that did not work very well bacause the pwm frequency is rather low (around 4 pulses in 10ms), to slow for very short trigger impulses.

I am really excited to getting started with 32 bit Midibox - my last build was the MB Sid, quite some years ago :slight_smile:

Thanks for any comments! 

 

Maybe check out http://www.ucapps.de/mbhp_aout_lc.html

I’m not sure what app would suit you best though. NG is probably the most flexible, depending on what you want to do.

Thanks! Since i need two octaves the resistor ladder cv-lc will not get me far. Using three Midibox_CV_NG would do the trick, but i consider this really overkill since i am pretty sure that i will not get more than 8, maybe 16 different amplitude levels with the solenoids and Glockenspiel. So 3 or 4 bit resolution should be ideal.

I have since been looking once more into the dout example in the mios32 programming section.

The code to adress 4 dout pins for every note to get 16 voltage levels should be very straight forward.

I am sure to get something running over the weekend. I will see if there are any general design problems with this idea.

that was easier to code than i thought. I really enjoyed learning about this. 

I dont have my scope, my douts or any leds here, so i added some code to print the output pattern to the lcd.

I noticed that if i put the clear_lcd command outside any of the function blocks, i get an error during compilation. No sure how this should be done. I found a workaround though…

include <stdbool.h>

is needed at the top of main.c for the display of the boolean values on the lcd

 

u8 start=0; // to clear the lcd…

int velocity_4bit;

/////////////////////////////////////////////////////////////////////////////

// This hook is called when a MIDI package has been received

/////////////////////////////////////////////////////////////////////////////

void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package)

{

// toggle Status LED on each incoming MIDI package

MIOS32_BOARD_LED_Set(0x0001, ~MIOS32_BOARD_LED_Get());
 
// no idea how and where else i can clear the lcd

if( start == 0 ) {

MIOS32_LCD_Clear();

start=1;

}
 
if( midi_package.type == NoteOn && midi_package.velocity > 0 ) {
 
u8 dout_pin = (midi_package.note % 12)*4;

velocity_4bit = midi_package.velocity >> 3;

 

MIOS32_BOARD_J5_PinSet(dout_pin, !((velocity_4bit >> 3)) & 1);

MIOS32_BOARD_J5_PinSet(dout_pin+1, !((velocity_4bit >> 2)) & 1);

MIOS32_BOARD_J5_PinSet(dout_pin+2, !((velocity_4bit >> 1)) & 1);

MIOS32_BOARD_J5_PinSet(dout_pin+3, !((velocity_4bit >> 0)) & 1);
 
// Just for testing: Print to lcd:

MIOS32_LCD_CursorSet(dout_pin, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 3) & 1));

MIOS32_LCD_CursorSet(dout_pin+1, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 2) & 1));

MIOS32_LCD_CursorSet(dout_pin+2, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 1) & 1));

MIOS32_LCD_CursorSet(dout_pin+3, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 0) & 1));
 
MIOS32_LCD_CursorSet(0, 1);

MIOS32_LCD_PrintFormattedString(“%02d:%02d”, midi_package.velocity,velocity_4bit);
 
}

 

else if( (midi_package.type == NoteOff) ||

(midi_package.type == NoteOn && midi_package.velocity == 0) ) {
 
u8 dout_pin = (midi_package.note % 12)*4;

 

MIOS32_BOARD_J5_PinSet(dout_pin, 0);

MIOS32_BOARD_J5_PinSet(dout_pin+1,0);

MIOS32_BOARD_J5_PinSet(dout_pin+2,0);

MIOS32_BOARD_J5_PinSet(dout_pin+3,0);
 
//MIOS32_LCD_CursorSet(dout_pin, 0); // X, Y

//MIOS32_LCD_PrintString(" ");
 
}

}

 

now i have to look into timers to get some way of resetting my pins after some ms. Solenoids don´t like to be “on” for long, and the glockenspiel needs only a short percussive hit.

/////////////////////////////////////////////////////////////////////////////

// Include files

/////////////////////////////////////////////////////////////////////////////
 
include <mios32.h>

include “app.h”

include <stdbool.h>

 

const char* bool_to_string(bool value) {

return value ? “1” : “0”;

}

// $Id$

/*

* MIOS32 Tutorial #020: LCD Output

* see README.txt for details

*

* ==========================================================================

*

* Copyright (C) 2009 Thorsten Klose (tk@midibox.org)

* Licensed for personal non-commercial use only.

* All other rights reserved.

*

* ==========================================================================

*/
 
/////////////////////////////////////////////////////////////////////////////

// Include files

/////////////////////////////////////////////////////////////////////////////
 
include <mios32.h>

include “app.h”

include <stdbool.h>

 

const char* bool_to_string(bool value) {

return value ? “1” : “0”;
 
 

//u8 velocity_dependent_timings[16]={20,18,17,16,14,13,10,10,10,10,10,10,10,10,10,10};
 
}

u8 note_off_timers[24];

/////////////////////////////////////////////////////////////////////////////

// This hook is called after startup to initialize the application

/////////////////////////////////////////////////////////////////////////////

void APP_Init(void)

{

// initialize all LEDs

MIOS32_BOARD_LED_Init(0xffffffff);

// initialize all pins of J5A, J5B and J5C as outputs in Push-Pull Mode

int pin;

for(pin=0; pin<12; ++pin)

MIOS32_BOARD_J5_PinInit(pin, MIOS32_BOARD_PIN_MODE_OUTPUT_PP);
 
int i;

for(i=0; i<24; ++i) {

note_off_timers[i]=0;

}

 

}

 

 

/////////////////////////////////////////////////////////////////////////////

// This task is running endless in background

/////////////////////////////////////////////////////////////////////////////

void APP_Background(void)

{

// clear LCD

// MIOS32_LCD_Clear();
 
}

 
/////////////////////////////////////////////////////////////////////////////

// This hook is called each mS from the main task which also handles DIN, ENC

// and AIN events. You could add more jobs here, but they shouldn’t consume

// more than 300 uS to ensure the responsiveness of buttons, encoders, pots.

// Alternatively you could create a dedicated task for application specific

// jobs as explained in $MIOS32_PATH/apps/tutorials/006_rtos_tasks

/////////////////////////////////////////////////////////////////////////////

void APP_Tick(void)

{

// PWM modulate the status LED (this is a sign of life)

u32 timestamp = MIOS32_TIMESTAMP_Get();

MIOS32_BOARD_LED_Set(1, (timestamp % 20) <= ((timestamp / 100) % 10));

 

 

int i;

for(i=0; i<24; ++i) {

if( note_off_timers[i]>0) {

–note_off_timers[i];
 
} else {

int j = i*4;

MIOS32_BOARD_J5_PinSet(j, 0);

MIOS32_BOARD_J5_PinSet(j+1,0);

MIOS32_BOARD_J5_PinSet(j+2,0);

MIOS32_BOARD_J5_PinSet(j+3,0);

MIOS32_LCD_CursorSet(j, 0); // X, Y

MIOS32_LCD_PrintString(" ");
 
}

}

}
 
/////////////////////////////////////////////////////////////////////////////

// This hook is called each mS from the MIDI task which checks for incoming

// MIDI events. You could add more MIDI related jobs here, but they shouldn’t

// consume more than 300 uS to ensure the responsiveness of incoming MIDI.

/////////////////////////////////////////////////////////////////////////////
 
void APP_MIDI_Tick(void)

{

 
}

 
u8 start=0; // to clear the lcd…

int velocity_4bit;
 
/////////////////////////////////////////////////////////////////////////////

// This hook is called when a MIDI package has been received

/////////////////////////////////////////////////////////////////////////////

void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package)

{

// toggle Status LED on each incoming MIDI package

MIOS32_BOARD_LED_Set(0x0001, ~MIOS32_BOARD_LED_Get());
 
// no idea how and where else i can clear the lcd

if( start == 0 ) {

MIOS32_LCD_Clear();

start=1;

}
 
if( midi_package.type == NoteOn && midi_package.velocity > 0 ) {
 
u8 note_number = (midi_package.note % 12);

u8 dout_pin = note_number * 4;

velocity_4bit = midi_package.velocity >> 3;

 

//note_off_timers[note_number] = velocity_dependant_timings[velocity_4bit];

MIOS32_BOARD_J5_PinSet(dout_pin, !((velocity_4bit >> 3)) & 1);

MIOS32_BOARD_J5_PinSet(dout_pin+1, !((velocity_4bit >> 2)) & 1);

MIOS32_BOARD_J5_PinSet(dout_pin+2, !((velocity_4bit >> 1)) & 1);

MIOS32_BOARD_J5_PinSet(dout_pin+3, !((velocity_4bit >> 0)) & 1);

 

// Just for testing: Print to lcd:

MIOS32_LCD_CursorSet(dout_pin, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 3) & 1));

MIOS32_LCD_CursorSet(dout_pin+1, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 2) & 1));

MIOS32_LCD_CursorSet(dout_pin+2, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 1) & 1));

MIOS32_LCD_CursorSet(dout_pin+3, 0); // X, Y

MIOS32_LCD_PrintString(bool_to_string((velocity_4bit >> 0) & 1));

 

note_off_timers[note_number] = 3000/(velocity_4bit*2); // just for testing, this has to be tested and maybe we want longer gate for low velocity

MIOS32_LCD_CursorSet(0, 1);

MIOS32_LCD_PrintFormattedString(“%02d:%02d:%02d”, midi_package.velocity,velocity_4bit,note_off_timers[note_number]);

 
 
}

 

else if( (midi_package.type == NoteOff) ||

(midi_package.type == NoteOn && midi_package.velocity == 0) ) {
 
u8 dout_pin = (midi_package.note % 12)*4;

 

MIOS32_BOARD_J5_PinSet(dout_pin, 0);

MIOS32_BOARD_J5_PinSet(dout_pin+1,0);

MIOS32_BOARD_J5_PinSet(dout_pin+2,0);

MIOS32_BOARD_J5_PinSet(dout_pin+3,0);
 
//MIOS32_LCD_CursorSet(dout_pin, 0); // X, Y

//MIOS32_LCD_PrintString(" ");
 
}

}
 
/////////////////////////////////////////////////////////////////////////////

// This hook is called before the shift register chain is scanned

/////////////////////////////////////////////////////////////////////////////

void APP_SRIO_ServicePrepare(void)

{

}
 
/////////////////////////////////////////////////////////////////////////////

// This hook is called after the shift register chain has been scanned

/////////////////////////////////////////////////////////////////////////////

void APP_SRIO_ServiceFinish(void)

{

}
 
/////////////////////////////////////////////////////////////////////////////

// This hook is called when a button has been toggled

// pin_value is 1 when button released, and 0 when button pressed

/////////////////////////////////////////////////////////////////////////////

void APP_DIN_NotifyToggle(u32 pin, u32 pin_value)

{

}
 
/////////////////////////////////////////////////////////////////////////////

// This hook is called when an encoder has been moved

// incrementer is positive when encoder has been turned clockwise, else

// it is negative

/////////////////////////////////////////////////////////////////////////////

void APP_ENC_NotifyChange(u32 encoder, s32 incrementer)

{

}
 
/////////////////////////////////////////////////////////////////////////////

// This hook is called when a pot has been moved

/////////////////////////////////////////////////////////////////////////////

void APP_AIN_NotifyChange(u32 pin, u32 pin_value)

{

}

If anyone is interested, this sort of works, turning off the pins/updating the lcd after a specified time. It works as in the J5 debounce tutorial 006_rtos_task.

Thanks for all the great tutorials, they really provide a good starting point for people like me!