Music_transcription.c:
/*
* File: Music_transcription.c
* Author: Mikayla Diesch (mld243), Shubha Sekar (ss2694), Maayan Kline (mok8)
* Adapted from:
* TFT_keypad_BRL4.c
* Author: Bruce Land
* Target PIC: PIC32MX250F128B
*/
// graphics libraries
#include "config.h"
#include "tft_master.h"
#include "tft_gfx.h"
// need for rand function
#include <stdlib.h>
// threading library
#include <<plib.h>
// config.h sets 40 MHz
#define SYS_FREQ 40000000
#include "pt_cornell_1_2_1.h"
#include "Filter_bandpass_14bit.h"
#define RELEASE 0
#define MAYBE_PRESSED 1
#define STILL_PRESSED 2
#define MAYBE_RELEASED 3
#define NOT_TRANSMITTING 0
#define TRANSMITTING 1
#define TERMINATION_STR "end\r\n"
#define NEWLINE_CHARACTER "\r\n"
// PORT B
#define EnablePullDownB(bits) CNPUBCLR=bits; CNPDBSET=bits;
#define DisablePullDownB(bits) CNPDBCLR=bits;
#define EnablePullUpB(bits) CNPDBCLR=bits; CNPUBSET=bits;
#define DisablePullUpB(bits) CNPUBCLR=bits;
//===============================================================
typedef signed int fix16 ;
#define multfix16(a,b) ((fix16)(((( signed long long)(a))*(( signed long long)(b)))>>16)) //multiply two fixed 16:16
#define float2fix16(a) ((fix16)((a)*65536.0)) // 2^16
#define fix2float16(a) ((float)(a)/65536.0)
#define fix2int16(a) ((int)((a)>>16))
#define int2fix16(a) ((fix16)((a)<<16))
#define divfix16(a,b) ((fix16)((((signed long long)(a)<<16)/(b))))
#define sqrtfix16(a) (float2fix16(sqrt(fix2float16(a))))
#define absfix16(a) abs(a)
// lowpass coeff
volatile fix16 alpha = float2fix16(0.005) ; // time constant ~ 200/8000 sec = 0.25 sec
// == bit fixed point 2.14 format ===============================
// resolution 2^-14 = 6.1035e-5
// dynamic range is +1.9999/-2.0
typedef signed short fix14 ;
#define multfix14(a,b) ((fix14)((((long)(a))*((long)(b)))>>14)) //multiply two fixed 2.14
#define float2fix14(a) ((fix14)((a)*16384.0)) // 2^14
#define fix2float14(a) ((float)(a)/16384.0)
#define absfix14(a) abs(a)
// Filter_bank_size from Filter_bank_coeff.h
volatile fix14 history_1[Filter_bank_size], history_2[Filter_bank_size] ; //output history
volatile fix14 history_in1[Filter_bank_size], history_in2[Filter_bank_size];
volatile fix14 filter_out[Filter_bank_size], thresh;
volatile float filter_out_float[Filter_bank_size];
volatile int ISR_time;
volatile int max, init_index, tempo;
volatile fix16 Q, Q3, Q1_5, Q0_75, Q0_25;
static fix16 cycle_period = float2fix16(0.00041545);
static char note[2] = {'N', ' '};
//================================================================
/***************************************************
Code for the ECE 4760 final project of Mikayla Diesch (mld243),
Shubha Sekar (ss2694), Maayan Kline (mok8), for a device to transcribe
live music to a MIDI file. This is done by sampling the input from
the microphone circuit, running it through a filter bank, and
checking which bank has the greatest energy. The filter banks are
tuned to the frequencies corresponding to the pitches within a
given range. This data is then sent over UART using DMA to a
client which cxollects this data and formats it as a MIDI file.
****************************************************/
// string buffer
char buffer[100];
// === thread structures ============================================
// thread control structs
// note that UART input and output are threads
static struct pt pt_timer, pt_filter_bank, pt_key, pt_DMA_output, pt_button, pt_init, pt_input;
volatile int time[1000];
volatile fix16 time_secs[1000];
volatile int notes[1000];
volatile int previous_max, idx, last_idx;
volatile int note_time;
// system 1 second interval tick
int sys_time_seconds;
// === Timer Thread =================================================
// update a 1 second tick counter
static PT_THREAD (protothread_timer(struct pt *pt))
{
PT_BEGIN(pt);
tft_setCursor(0, 0);
tft_setTextColor(ILI9340_WHITE); tft_setTextSize(1);
tft_writeString("Time in seconds since boot\n");
while(1) {
// yield time 1 second
PT_YIELD_TIME_msec(1000) ;
sys_time_seconds++ ;
//draw sys_time
tft_fillRoundRect(0,10, 100, 14, 1, ILI9340_BLACK);// x,y,w,h,radius,color
tft_setCursor(0, 10);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
sprintf(buffer,"%d", sys_time_seconds);
tft_writeString(buffer);
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // timer thread
void displayState(char s[]) {
// draw key number
tft_fillRoundRect(10, 290, 200, 28, 1, ILI9340_BLACK); // x,y,w,h,radius,color
tft_setCursor(10, 290);
tft_setTextColor(ILI9340_YELLOW);
tft_setTextSize(4);
tft_writeString(s);
}
void displayKey(int key) {
// draw key number
tft_fillRoundRect(30, 260, 200, 28, 1, ILI9340_BLACK); // x,y,w,h,radius,color
tft_setCursor(30, 260);
tft_setTextColor(ILI9340_YELLOW);
tft_setTextSize(4);
sprintf(buffer, "%d", key);
tft_writeString(buffer);
}
volatile int UART_STATE = NOT_TRANSMITTING;
// === Filter Banks Thread =================================================
volatile int i, k;
static char cmd[16];
static int value;
static PT_THREAD (protothread_filter_bank(struct pt *pt))
{
PT_BEGIN(pt);
while(1) {
// yield time 1 second
PT_YIELD_TIME_msec(10) ;
tft_fillRoundRect(100,10, 100, 14, 1, ILI9340_BLACK); // x,y,w,h,radius,color
tft_setCursor(100, 10);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
sprintf(buffer,"%d", ReadADC10(0));
tft_writeString(buffer);
if (max == -1) {
note[0] = 'N';
note[1] = ' ';
}
if (max == 0 || max == 12 || max == 24) note[0] = 'C';
else if (max == 1 || max == 13 || max == 25) {
note[0] ='C';
note[1] = '#';
}
else if (max == 2 || max == 14 || max == 26) note[0] = 'D';
else if (max == 3 || max == 15 || max == 27) {
note[0] ='D';
note[1] = '#';
}
else if (max == 4 || max == 16 || max == 28) note[0] = 'E';
else if (max == 5 || max == 17 || max == 29) note[0] = 'F';
else if (max == 6 || max == 18 || max == 30) {
note[0] ='F';
note[1] = '#';
}
else if (max ==7 || max == 19 || max == 31) note[0] = 'G';
else if (max ==8 || max == 20 || max == 32) {
note[0] ='G';
note[1] = '#';
}
else if (max ==9 || max == 21 || max == 33) note[0] = 'A';
else if (max ==10 || max == 22 || max == 34) {
note[0] ='A';
note[1] = '#';
}
else if (max == 11 || max == 23 || max == 35) note[0] = 'B';
// Print the note being played
tft_fillRoundRect(0,100, 100, 14, 1, ILI9340_BLACK); // x,y,w,h,radius,color
tft_setCursor(0, 100);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
sprintf(buffer,"%c %c", note[0], note[1]);
tft_writeString(buffer);
// Print the first note recorded and its duration
tft_fillRoundRect(0,150, 100, 14, 1, ILI9340_BLACK); // x,y,w,h,radius,color
tft_setCursor(0, 150);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2);
sprintf(buffer,"tempo = %d", tempo);
tft_writeString(buffer);
// Print the threshold and the max filter bank value
tft_fillRoundRect(0,200, 100, 50, 1, ILI9340_BLACK); // x,y,w,h,radius,color
tft_setCursor(0, 200);
tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(1);
sprintf(buffer,"note time= %f \n8thresh = %f", fix2float16(time_secs[idx-1]), fix2float16(Q0_25));
tft_writeString(buffer);
// send the data via DMA to serial
if (last_idx != idx && (UART_STATE == TRANSMITTING)) {
sprintf(PT_send_buffer, "%d,%d%s", notes[idx-1], time[idx-1], NEWLINE_CHARACTER);
tft_writeString(PT_send_buffer);
//sprintf(PT_send_buffer, "index = %d\n",idx);
last_idx = idx;
//displayState("transmitting");
// by spawning a print thread
PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output));
}
// NEVER exit while
} // END WHILE(1)
PT_END(pt);
} // filter banks thread
// === Init Thread =================================================
static PT_THREAD (protothread_init(struct pt *pt))
{
PT_BEGIN(pt);
static int j = 0;
static float cmd_num = 0;
//spawn a thread to handle terminal input
// the input thread waits for input
// -- BUT does NOT block other threads
// string is returned in "PT_term_buffer"
PT_SPAWN(pt, &pt_input, PT_GetSerialBuffer(&pt_input) );
// returns when the thread dies
// in this case, when < enter > is pushed
// now parse the string
sscanf(PT_term_buffer, "%s %d", cmd, &value);
while (cmd[j] != '\0') {
cmd_num *= 10;
cmd_num += cmd[j] - '0';
j++;
}
tempo = cmd_num;
Q = float2fix16(60.0/tempo);
Q3 = multfix16(int2fix16(3), Q);
Q1_5 = multfix16(float2fix16(1.5), Q);
Q0_75 = multfix16(float2fix16(0.75), Q);
Q0_25 = multfix16(float2fix16(0.5), Q);
PT_END(pt);
}
// init thread
//== Timer 2 interrupt handler ===================================
// ipl2 means "interrupt priority level 2"
// rate is 8000/sec
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2ISR(void)
{
fix14 output;
static int i, input, j;
thresh = float2fix14(0.004);
// timing
mPORTBSetPinsDigitalOut(BIT_13);
mPORTBSetBits(BIT_13);
// read the ADC
input = (ReadADC10(0)-512)<<4;
// read the first buffer position
//channel0 = ReadADC10(0); // read the result of channel 4 conversion from the idle buffer
for (i=0; i< Filter_bank_size; i++) {
// second order butterworth computed by matlab butter(1,[lower upper])
output = multfix14(b1[i], input-history_in2[i]) + multfix14(history_1[i], -a2[i]) +
multfix14(history_2[i], -a3[i]) ;
//output = input;
history_2[i] = history_1[i];
history_1[i] = output;
history_in2[i] = history_in1[i];
history_in1[i] = input;
// now lowpass the absolute value of the filter output
// new_output = alpha*input + (1-alpha)*last_output
// time constant about 64 samples
filter_out[i] = (filter_out[i] + ((absfix14(output) - filter_out[i])>>6));
//pow[i] = filter_out[i]>>3;
if (filter_out[i] > filter_out[max]) {
max = i;
}
// if no entry in the filter bank is greater than thresh, register silence
if (filter_out[max] < thresh) {
max = -1;
}
}
time_secs[idx] = multfix16(cycle_period, int2fix16(note_time));
// if this note has been playing since the last sample, increment time
if (previous_max == max || time_secs[idx] < Q0_25) {
note_time++;
}
if (time_secs[idx] >= Q3) {
notes[idx] = previous_max;
time[idx] = 1;
note_time = 0;
idx++;
previous_max = max;
}
// else a new note is playing
// store note info
if (previous_max != max && time_secs[idx] > Q0_25) {
notes[idx] = previous_max;
//time[idx] = multfix16(int2fix16(note_time), cycle_period);
//time[idx] = note_time;
if (time_secs[idx] >= Q3) {
time[idx] = 1;
}
if (time_secs[idx] >= Q1_5 && time_secs[idx] < Q3) {
time[idx] = 2;
}
if (time_secs[idx] >= Q0_75 && time_secs[idx] < Q1_5) {
time[idx] = 4;
}
if (time_secs[idx] < Q0_75) {
time[idx] = 8;
}
note_time = 0;
idx++;
previous_max = max;
}
mPORTBClearBits(BIT_13);
// clear the interrupt flag
ISR_time = ReadTimer2();
mT2ClearIntFlag();
}
// end interrupt
//== Push Button FSM Thread ===================================
static PT_THREAD (protothread_button(struct pt *pt))
{
PT_BEGIN(pt);
//displayState("In thread");
static int STATE = RELEASE;
static int PRESSED = 0;
// PortB as inputs
mPORTBSetPinsDigitalIn(BIT_7); //Set port as input
// Enable 10K pulldown resistors on B port pins
EnablePullDownB(BIT_7);
while(1)
{
PRESSED = mPORTBReadBits(BIT_7);
//displayKey(PRESSED);
switch(STATE) {
case RELEASE:
//displayState("RELEASE");
if ( PRESSED ) {
STATE = MAYBE_PRESSED;
}
break;
case MAYBE_PRESSED:
//displayState("MAYBE_PRESSED");
if( !PRESSED )
STATE = RELEASE;
else {
STATE = STILL_PRESSED;
if ( UART_STATE == TRANSMITTING )
{
displayState("NOT_TRASMITTING");
sprintf(PT_send_buffer, TERMINATION_STR);// by spawning a print thread
PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output));
UART_STATE = NOT_TRANSMITTING;
//Disable time interrupt
DisableIntT2;
}
else {
displayState("TRANSMITTING");
UART_STATE = TRANSMITTING;
//Enable time interrupt
EnableIntT2;
}
}
break;
case STILL_PRESSED:
//displayState("STILL_PRESSED");
if ( !PRESSED )
STATE = MAYBE_RELEASED;
break;
case MAYBE_RELEASED:
//displayState("MAYBE_RELEASED");
if ( !PRESSED )
STATE = RELEASE;
else
STATE = STILL_PRESSED;
break;
default:
STATE = RELEASE;
break;
}
// yield time
PT_YIELD_TIME_msec(30);
} // END WHILE(1)
PT_END(pt);
} // Push button FSM thread
// === Main ======================================================
void main(void) {
static char cmd[16];
static int value;
static int j = 0;
static float cmd_num = 0;
static int is_float = 0;
static int mag = -1;
SYSTEMConfigPerformance(PBCLK);
ANSELA = 0; ANSELB = 0; CM1CON = 0; CM2CON = 0;
// the ADC ///////////////////////////////////////
// configure and enable the ADC
CloseADC10(); // ensure the ADC is off before setting the configuration
// define setup parameters for OpenADC10
// Turn module on | ouput in integer | trigger mode auto | enable autosample
// ADC_CLK_AUTO -- Internal counter ends sampling and starts conversion (Auto convert)
// ADC_AUTO_SAMPLING_ON -- Sampling begins immediately after last conversion completes; SAMP bit is automatically set
// ADC_AUTO_SAMPLING_OFF -- Sampling begins with AcquireADC10();
#define PARAM1 ADC_FORMAT_INTG16 | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON//
// define setup parameters for OpenADC10
// ADC ref external | disable offset test | disable scan mode | do 1 sample | use single buf | alternate mode off
#define PARAM2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
// Define setup parameters for OpenADC10
// use peripheral bus clock | set sample time | set ADC clock divider
// ADC_CONV_CLK_Tcy2 means divide CLK_PB by 2 (max speed)
// ADC_SAMPLE_TIME_5 seems to work with a source resistance < 1kohm
// At PB clock 30 MHz, divide by two for ADC_CONV_CLK gives 66 nSec
#define PARAM3 ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_5 | ADC_CONV_CLK_Tcy2 //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy2
// define setup parameters for OpenADC10
// set AN9 and as analog inputs
#define PARAM4 ENABLE_AN9_ANA
// define setup parameters for OpenADC10
// do not assign channels to scan
#define PARAM5 SKIP_SCAN_ALL
// use ground as neg ref for A | use AN9 for input A
// configure to sample AN9 pin26
SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN9 ); // configure to sample AN9
OpenADC10( PARAM1, PARAM2, PARAM3, PARAM4, PARAM5 ); // configure ADC using the parameters defined above
EnableADC10(); // Enable the ADC
// timer interrupt //////////////////////////
// Set up timer2 on, interrupts, internal clock, prescalar 1, toggle rate
// at 40 MHz PB clock
// 5000 is 8 ksamples/sec
OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_1, 16618);
// set up the timer interrupt with a priority of 2
ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2);
mT2ClearIntFlag(); // and clear the interrupt flag
//Disable time interrupt
DisableIntT2;
// === config threads ==========
// turns OFF UART support and debugger pin
PT_setup();
// === setup system wide interrupts ========
INTEnableSystemMultiVectoredInt();
// init the threads
PT_INIT(&pt_timer);
PT_INIT(&pt_filter_bank);
PT_INIT(&pt_button);
PT_INIT(&pt_init);
// init the display
tft_init_hw();
PPSOutput(2, RPA1, SDO1);
tft_begin();
tft_fillScreen(ILI9340_BLACK);
//240x320 vertical display
tft_setRotation(0); // Use tft_setRotation(1) for 320x240
// seed random color
srand(1);
note_time = 0;
last_idx = 0;
idx = 0;
max = -1;
previous_max = -1;
init_index = 0;
thresh = 0;
// round-robin scheduler for threads
while (1){
PT_SCHEDULE(protothread_init(&pt_init));
PT_SCHEDULE(protothread_timer(&pt_timer));
PT_SCHEDULE(protothread_button(&pt_button));
PT_SCHEDULE(protothread_filter_bank(&pt_filter_bank));
}
} // main
// === end ======================================================
config.h:
/*
* File: config.h
* Author: Syed Tahmid Mahbub, Bruce Land
* Modifed by: Mikayla Diesch (mld243), Shubha Sekar (ss2694), Maayan Kline (mok8)
*/
#ifndef CONFIG_H
#define CONFIG_H
#define _SUPPRESS_PLIB_WARNING 1
#include <plib.h>
// serial stuff
#include <stdio.h>
//=============================================================
// 40 MHz
#pragma config FNOSC = FRCPLL, POSCMOD = OFF
#pragma config FPLLIDIV = DIV_2, FPLLMUL = MUL_20 //40 MHz
#pragma config FPBDIV = DIV_1, FPLLODIV = DIV_2 // PB 40 MHz
#pragma config FWDTEN = OFF, FSOSCEN = OFF, JTAGEN = OFF
//==============================================================
// Protothreads configure
// IF use_vref_debug IS defined, pin 25 is Vref output
//#define use_vref_debug
// IF use_uart_serial IS defined, pin 21 and pin 22 are used by the uart
#define use_uart_serial
#define BAUDRATE 9600 // must match PC terminal emulator setting
/////////////////////////////////
// set up clock parameters
// system cpu clock
#define sys_clock 40000000
// sys_clock/FPBDIV
#define pb_clock sys_clock // divide by one in this case
#endif /* CONFIG_H */
Filter_bandpass_14bit.h:
#define Filter_bank_size 37
const int b1[37]={
138,
146,
155,
164,
174,
184,
195,
206,
219,
231,
245,
259,
275,
291,
308,
326,
345,
365,
386,
408,
432,
457,
483,
511,
541,
572,
605,
639,
676,
714,
755,
798,
843,
890,
940,
993,
1048};
const int a2[37]={
-30616,
-30373,
-30103,
-29802,
-29467,
-29093,
-28677,
-28214,
-27698,
-27125,
-26488,
-25781,
-24997,
-24127,
-23164,
-22099,
-20924,
-19629,
-18204,
-16641,
-14929,
-13061,
-11030,
-8829,
-6455,
-3908,
-1193,
1680,
4697,
7830,
11043,
14289,
17503,
20608,
23504,
26078,
28193};
const int a3[37]={
16106,
16090,
16073,
16054,
16035,
16014,
15993,
15970,
15945,
15920,
15893,
15864,
15833,
15801,
15767,
15731,
15693,
15653,
15611,
15566,
15519,
15469,
15416,
15360,
15301,
15239,
15173,
15104,
15031,
14954,
14873,
14787,
14697,
14602,
14502,
14397,
14286};
pt_cornell_1_2_1_custom.h:
/*
* File: pt_cornell_1_2_1.h
* Author: brl4
* Modified by: Mikayla Diesch (mld243), Shubha Sekar (ss2694), Maayan Kline (mok8)
* Created on Sept 22, 2015
*/
/*
* Copyright (c) 2004-2005, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
* Author: Adam Dunkels <adam@sics.se>
*
* $Id: pt.h,v 1.7 2006/10/02 07:52:56 adam Exp $
*/
#include <plib.h>
/**
* \addtogroup pt
* @{
*/
/**
* \file
* Protothreads implementation.
* \author
* Adam Dunkels <adam@sics.se>
*
*/
#ifndef __PT_H__
#define __PT_H__
////////////////////////
//#include "lc.h"
////////////////////////
/**
* \file lc.h
* Local continuations
* \author
* Adam Dunkels <adam@sics.se>
*
*/
#ifdef DOXYGEN
/**
* Initialize a local continuation.
*
* This operation initializes the local continuation, thereby
* unsetting any previously set continuation state.
*
* \hideinitializer
*/
#define LC_INIT(lc)
/**
* Set a local continuation.
*
* The set operation saves the state of the function at the point
* where the operation is executed. As far as the set operation is
* concerned, the state of the function does not include the
* call-stack or local (automatic) variables, but only the program
* counter and such CPU registers that needs to be saved.
*
* \hideinitializer
*/
#define LC_SET(lc)
/**
* Resume a local continuation.
*
* The resume operation resumes a previously set local continuation, thus
* restoring the state in which the function was when the local
* continuation was set. If the local continuation has not been
* previously set, the resume operation does nothing.
*
* \hideinitializer
*/
#define LC_RESUME(lc)
/**
* Mark the end of local continuation usage.
*
* The end operation signifies that local continuations should not be
* used any more in the function. This operation is not needed for
* most implementations of local continuation, but is required by a
* few implementations.
*
* \hideinitializer
*/
#define LC_END(lc)
/**
* \var typedef lc_t;
*
* The local continuation type.
*
* \hideinitializer
*/
#endif /* DOXYGEN */
//#ifndef __LC_H__
//#define __LC_H__
//#ifdef LC_INCLUDE
//#include LC_INCLUDE
//#else
/////////////////////////////
//#include "lc-switch.h"
/////////////////////////////
//#ifndef __LC_SWITCH_H__
//#define __LC_SWITCH_H__
/* WARNING! lc implementation using switch() does not work if an
LC_SET() is done within another switch() statement! */
/** \hideinitializer */
/*
typedef unsigned short lc_t;
#define LC_INIT(s) s = 0;
#define LC_RESUME(s) switch(s) { case 0:
#define LC_SET(s) s = __LINE__; case __LINE__:
#define LC_END(s) }
#endif /* __LC_SWITCH_H__ */
/** @} */
//#endif /* LC_INCLUDE */
//#endif /* __LC_H__ */
/** @} */
/** @} */
/////////////////////////////
//#include "lc-addrlabels.h"
/////////////////////////////
#ifndef __LC_ADDRLABELS_H__
#define __LC_ADDRLABELS_H__
/** \hideinitializer */
typedef void * lc_t;
#define LC_INIT(s) s = NULL
#define LC_RESUME(s) \
do { \
if(s != NULL) { \
goto *s; \
} \
} while(0)
#define LC_CONCAT2(s1, s2) s1##s2
#define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2)
#define LC_SET(s) \
do { \
LC_CONCAT(LC_LABEL, __LINE__): \
(s) = &&LC_CONCAT(LC_LABEL, __LINE__); \
} while(0)
#define LC_END(s)
#endif /* __LC_ADDRLABELS_H__ */
//////////////////////////////////////////
struct pt {
lc_t lc;
int pri;
};
#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED 2
#define PT_ENDED 3
/**
* \name Initialization
* @{
*/
/**
* Initialize a protothread.
*
* Initializes a protothread. Initialization must be done prior to
* starting to execute the protothread.
*
* \param pt A pointer to the protothread control structure.
*
* \sa PT_SPAWN()
*
* \hideinitializer
*/
#define PT_INIT(pt) LC_INIT((pt)->lc)
/** @} */
/**
* \name Declaration and definition
* @{
*/
/**
* Declaration of a protothread.
*
* This macro is used to declare a protothread. All protothreads must
* be declared with this macro.
*
* \param name_args The name and arguments of the C function
* implementing the protothread.
*
* \hideinitializer
*/
#define PT_THREAD(name_args) char name_args
/**
* Declare the start of a protothread inside the C function
* implementing the protothread.
*
* This macro is used to declare the starting point of a
* protothread. It should be placed at the start of the function in
* which the protothread runs. All C statements above the PT_BEGIN()
* invokation will be executed each time the protothread is scheduled.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)
/**
* Declare the end of a protothread.
*
* This macro is used for declaring that a protothread ends. It must
* always be used together with a matching PT_BEGIN() macro.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
PT_INIT(pt); return PT_ENDED; }
/** @} */
/**
* \name Blocked wait
* @{
*/
/**
* Block and wait until condition is true.
*
* This macro blocks the protothread until the specified condition is
* true.
*
* \param pt A pointer to the protothread control structure.
* \param condition The condition.
*
* \hideinitializer
*/
#define PT_WAIT_UNTIL(pt, condition) \
do { \
LC_SET((pt)->lc); \
if(!(condition)) { \
return PT_WAITING; \
} \
} while(0)
/**
* Block and wait while condition is true.
*
* This function blocks and waits while condition is true. See
* PT_WAIT_UNTIL().
*
* \param pt A pointer to the protothread control structure.
* \param cond The condition.
*
* \hideinitializer
*/
#define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond))
/** @} */
/**
* \name Hierarchical protothreads
* @{
*/
/**
* Block and wait until a child protothread completes.
*
* This macro schedules a child protothread. The current protothread
* will block until the child protothread completes.
*
* \note The child protothread must be manually initialized with the
* PT_INIT() function before this function is used.
*
* \param pt A pointer to the protothread control structure.
* \param thread The child protothread with arguments
*
* \sa PT_SPAWN()
*
* \hideinitializer
*/
#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))
/**
* Spawn a child protothread and wait until it exits.
*
* This macro spawns a child protothread and waits until it exits. The
* macro can only be used within a protothread.
*
* \param pt A pointer to the protothread control structure.
* \param child A pointer to the child protothread's control structure.
* \param thread The child protothread with arguments
*
* \hideinitializer
*/
#define PT_SPAWN(pt, child, thread) \
do { \
PT_INIT((child)); \
PT_WAIT_THREAD((pt), (thread)); \
} while(0)
/** @} */
/**
* \name Exiting and restarting
* @{
*/
/**
* Restart the protothread.
*
* This macro will block and cause the running protothread to restart
* its execution at the place of the PT_BEGIN() call.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_RESTART(pt) \
do { \
PT_INIT(pt); \
return PT_WAITING; \
} while(0)
/**
* Exit the protothread.
*
* This macro causes the protothread to exit. If the protothread was
* spawned by another protothread, the parent protothread will become
* unblocked and can continue to run.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_EXIT(pt) \
do { \
PT_INIT(pt); \
return PT_EXITED; \
} while(0)
/** @} */
/**
* \name Calling a protothread
* @{
*/
/**
* Schedule a protothread.
*
* This function shedules a protothread. The return value of the
* function is non-zero if the protothread is running or zero if the
* protothread has exited.
*
* \param f The call to the C function implementing the protothread to
* be scheduled
*
* \hideinitializer
*/
#define PT_SCHEDULE(f) ((f) < PT_EXITED)
//#define PT_SCHEDULE(f) ((f))
/** @} */
/**
* \name Yielding from a protothread
* @{
*/
/**
* Yield from the current protothread.
*
* This function will yield the protothread, thereby allowing other
* processing to take place in the system.
*
* \param pt A pointer to the protothread control structure.
*
* \hideinitializer
*/
#define PT_YIELD(pt) \
do { \
PT_YIELD_FLAG = 0; \
LC_SET((pt)->lc); \
if(PT_YIELD_FLAG == 0) { \
return PT_YIELDED; \
} \
} while(0)
/**
* \brief Yield from the protothread until a condition occurs.
* \param pt A pointer to the protothread control structure.
* \param cond The condition.
*
* This function will yield the protothread, until the
* specified condition evaluates to true.
*
*
* \hideinitializer
*/
#define PT_YIELD_UNTIL(pt, cond) \
do { \
PT_YIELD_FLAG = 0; \
LC_SET((pt)->lc); \
if((PT_YIELD_FLAG == 0) || !(cond)) { \
return PT_YIELDED; \
} \
} while(0)
/** @} */
#endif /* __PT_H__ */
#ifndef __PT_SEM_H__
#define __PT_SEM_H__
//#include "pt.h"
struct pt_sem {
unsigned int count;
};
/**
* Initialize a semaphore
*
* This macro initializes a semaphore with a value for the
* counter. Internally, the semaphores use an "unsigned int" to
* represent the counter, and therefore the "count" argument should be
* within range of an unsigned int.
*
* \param s (struct pt_sem *) A pointer to the pt_sem struct
* representing the semaphore
*
* \param c (unsigned int) The initial count of the semaphore.
* \hide initializer
*/
#define PT_SEM_INIT(s, c) (s)->count = c
/**
* Wait for a semaphore
*
* This macro carries out the "wait" operation on the semaphore. The
* wait operation causes the protothread to block while the counter is
* zero. When the counter reaches a value larger than zero, the
* protothread will continue.
*
* \param pt (struct pt *) A pointer to the protothread (struct pt) in
* which the operation is executed.
*
* \param s (struct pt_sem *) A pointer to the pt_sem struct
* representing the semaphore
*
* \hideinitializer
*/
#define PT_SEM_WAIT(pt, s) \
do { \
PT_WAIT_UNTIL(pt, (s)->count > 0); \
--(s)->count; \
} while(0)
/**
* Signal a semaphore
*
* This macro carries out the "signal" operation on the semaphore. The
* signal operation increments the counter inside the semaphore, which
* eventually will cause waiting protothreads to continue executing.
*
* \param pt (struct pt *) A pointer to the protothread (struct pt) in
* which the operation is executed.
*
* \param s (struct pt_sem *) A pointer to the pt_sem struct
* representing the semaphore
*
* \hideinitializer
*/
#define PT_SEM_SIGNAL(pt, s) ++(s)->count
#endif /* __PT_SEM_H__ */
//=====================================================================
//=== BRL4 additions for PIC 32 =======================================
//=====================================================================
// macro to time a thread execution interveal in millisec
// max time 4000 sec
//#include <plib.h>
//#include <limits.h>
//#include "config.h"
#define PT_YIELD_TIME_msec(delay_time) \
do { static unsigned int time_thread ;\
time_thread = time_tick_millsec + (unsigned int)delay_time ; \
PT_YIELD_UNTIL(pt, (time_tick_millsec >= time_thread)); \
} while(0);
// macro to return system time
#define PT_GET_TIME() (time_tick_millsec)
// init rate sehcduler
//#define PT_INIT(pt, priority) LC_INIT((pt)->lc ; (pt)->pri = priority)
//PT_PRIORITY_INIT
#define PT_RATE_INIT() int pt_pri_count = 0;
// maitain proority frame count
//PT_PRIORITY_LOOP maitains a counter used to control execution
#define PT_RATE_LOOP() pt_pri_count = (pt_pri_count+1) & 0xf ;
// schecedule priority thread
//PT_PRIORITY_SCHEDULE
// 5 levels
// rate 0 is highest -- every time thru loop
// priority 1 -- every 2 times thru loop
// priority 2 -- every 4 times thru loop
// 3 is -- every 8 times thru loop
#define PT_RATE_SCHEDULE(f,rate) \
if((rate==0) | \
(rate==1 && ((pt_pri_count & 0b1)==0) ) | \
(rate==2 && ((pt_pri_count & 0b11)==0) ) | \
(rate==3 && ((pt_pri_count & 0b111)==0)) | \
(rate==4 && ((pt_pri_count & 0b1111)==0))) \
PT_SCHEDULE(f);
// macro to use 4 bit DAC as debugger output
// level range 0-15; duration in microseconds
// -- with zero meaning HOLD it on forever
//while((signed int)ReadTimer45() <= time_hold){};
// time_hold = duration + ReadTimer45() ;
#define PT_DEBUG_VALUE(level, duration) \
do { static int i ; \
CVRCON = CVRCON_setup | (level & 0xf); \
if (duration>0){ \
for (i=0; i < duration*7; i++){};\
CVRCON = CVRCON_setup; \
} \
} while(0);
// macros to manipulate a semaphore without blocking
#define PT_SEM_SET(s) (s)->count=1
#define PT_SEM_CLEAR(s) (s)->count=0
#define PT_SEM_READ(s) (s)->count
#define PT_SEM_ACCEPT(s) \
s->count; \
if (s->count) s->count-- ; \
//====================================================================
//=== serial setup ===================================================
//#ifdef use_uart_serial
///////////////////////////
// UART parameters
#define PB_DIVISOR (1 << OSCCONbits.PBDIV) // read the peripheral bus divider, FPBDIV
#define PB_FREQ sys_clock/PB_DIVISOR // periperhal bus frequency
#define clrscr() printf( "\x1b[2J")
#define home() printf( "\x1b[H")
#define pcr() printf( '\r')
#define crlf putchar(0x0a); putchar(0x0d);
#define backspace 0x7f // make sure your backspace matches this!
#define max_chars 64 // for input/output buffer
//====================================================================
// build a string from the UART2 /////////////
//////////////////////////////////////////////
char PT_term_buffer[max_chars];
int num_char;
int PT_GetSerialBuffer(struct pt *pt)
{
static char character;
// mark the beginnning of the input thread
PT_BEGIN(pt);
num_char = 0;
//memset(term_buffer, 0, max_chars);
while(num_char < max_chars)
{
// get the character
// yield until there is a valid character so that other
// threads can execute
PT_YIELD_UNTIL(pt, UARTReceivedDataIsAvailable(UART2));
// while(!UARTReceivedDataIsAvailable(UART2)){};
character = UARTGetDataByte(UART2);
//PT_YIELD_UNTIL(pt, UARTTransmitterIsReady(UART2));
//UARTSendDataByte(UART2, character);
// unomment to check backspace character!!!
//printf("--%x--",character );
// end line
if(character == '\r'){
PT_term_buffer[num_char] = 0; // zero terminate the string
//crlf; // send a new line
//PT_YIELD_UNTIL(pt, UARTTransmitterIsReady(UART2));
//UARTSendDataByte(UART2, '\n');
break;
}
// backspace
/*else if (character == backspace){
PT_YIELD_UNTIL(pt, UARTTransmitterIsReady(UART2));
UARTSendDataByte(UART2, ' ');
PT_YIELD_UNTIL(pt, UARTTransmitterIsReady(UART2));
UARTSendDataByte(UART2, backspace);
num_char--;
// check for buffer underflow
if (num_char<0) {num_char = 0 ;}
}*/
else {PT_term_buffer[num_char++] = character ;}
//if (character == backspace)
} //end while(num_char < max_size)
// kill this input thread, to allow spawning thread to execute
PT_EXIT(pt);
// and indicate the end of the thread
PT_END(pt);
}
//====================================================================
// === send a string to the UART2 ====================================
char PT_send_buffer[max_chars];
int num_send_chars ;
int PutSerialBuffer(struct pt *pt)
{
PT_BEGIN(pt);
num_send_chars = 0;
while (PT_send_buffer[num_send_chars] != 0){
PT_YIELD_UNTIL(pt, UARTTransmitterIsReady(UART2));
UARTSendDataByte(UART2, PT_send_buffer[num_send_chars]);
num_send_chars++;
}
// kill this output thread, to allow spawning thread to execute
PT_EXIT(pt);
// and indicate the end of the thread
PT_END(pt);
}
//====================================================================
// === DMA send string to the UART2 ==================================
int PT_DMA_PutSerialBuffer(struct pt *pt)
{
PT_BEGIN(pt);
//mPORTBSetBits(BIT_0);
// check for null string
if (PT_send_buffer[0]==0)PT_EXIT(pt);
// sent the first character
PT_YIELD_UNTIL(pt, UARTTransmitterIsReady(UART2));
UARTSendDataByte(UART2, PT_send_buffer[0]);
//DmaChnStartTxfer(DMA_CHANNEL1, DMA_WAIT_NOT, 0);
// start the DMA
DmaChnEnable(DMA_CHANNEL1);
// wait for DMA done
//mPORTBClearBits(BIT_0);
PT_YIELD_UNTIL(pt, DmaChnGetEvFlags(DMA_CHANNEL1) & DMA_EV_BLOCK_DONE);
//wait until the transmit buffer is empty
PT_YIELD_UNTIL(pt, U2STA&0x100);
// kill this output thread, to allow spawning thread to execute
PT_EXIT(pt);
// and indicate the end of the thread
PT_END(pt);
}
//#endif //#ifdef use_uart_serial
//======================================================================
// vref confing (if used)
int CVRCON_setup ;
// system time
volatile unsigned int time_tick_millsec ;
// Timer 5 interrupt handler ///////
// ipl2 means "interrupt priority level 2"
void __ISR(_TIMER_5_VECTOR, IPL2AUTO) Timer5Handler(void) //_TIMER_5_VECTOR
{
// clear the interrupt flag
mT5ClearIntFlag();
//count milliseconds
time_tick_millsec++ ;
}
void PT_setup (void)
{
// Configure the device for maximum performance but do not change the PBDIV
// Given the options, this function will change the flash wait states, RAM
// wait state and enable prefetch cache but will not change the PBDIV.
// The PBDIV value is already set via the pragma FPBDIV option above..
SYSTEMConfig(sys_clock, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
ANSELA =0; //make sure analog is cleared
ANSELB =0;
#ifdef use_uart_serial
// === init the uart2 ===================
PPSInput (2, U2RX, RPB11); //Assign U2RX to pin RPB11 -- Physical pin 22 on 28 PDIP
PPSOutput(4, RPB10, U2TX); //Assign U2TX to pin RPB10 -- Physical pin 21 on 28 PDIP
UARTConfigure(UART2, UART_ENABLE_PINS_TX_RX_ONLY);
UARTSetLineControl(UART2, UART_DATA_SIZE_8_BITS | UART_PARITY_NONE | UART_STOP_BITS_1);
UARTSetDataRate(UART2, pb_clock, BAUDRATE);
UARTEnable(UART2, UART_ENABLE_FLAGS(UART_PERIPHERAL | UART_RX | UART_TX));
//printf("\n\r..protothreads start..\n\r");
// === set up DMA for UART output =========
// configure the channel and enable end-on-match
DmaChnOpen(DMA_CHANNEL1, DMA_CHN_PRI2, DMA_OPEN_MATCH);
// trigger a byte everytime the UART is empty
DmaChnSetEventControl(DMA_CHANNEL1, DMA_EV_START_IRQ_EN|DMA_EV_MATCH_EN|DMA_EV_START_IRQ(_UART2_TX_IRQ));
// source and destination
DmaChnSetTxfer(DMA_CHANNEL1, PT_send_buffer+1, (void*)&U2TXREG, max_chars, 1, 1);
// signal when done
DmaChnSetEvEnableFlags(DMA_CHANNEL1, DMA_EV_BLOCK_DONE);
// set null as ending character (of a string)
DmaChnSetMatchPattern(DMA_CHANNEL1, 0x00);
#endif //#ifdef use_uart_serial
// ===Set up timer5 ======================
// timer 5: on, interrupts, internal clock,
// set up to count millsec
OpenTimer5(T5_ON | T5_SOURCE_INT | T5_PS_1_1 , pb_clock/1000);
// set up the timer interrupt with a priority of 2
ConfigIntTimer5(T5_INT_ON | T5_INT_PRIOR_2);
mT5ClearIntFlag(); // and clear the interrupt flag
// zero the system time tick
time_tick_millsec = 0;
//=== Set up VREF as a debugger output =======
#ifdef use_vref_debug
// set up the Vref pin and use as a DAC
// enable module| eanble output | use low range output | use internal reference | desired step
CVREFOpen( CVREF_ENABLE | CVREF_OUTPUT_ENABLE | CVREF_RANGE_LOW | CVREF_SOURCE_AVDD | CVREF_STEP_0 );
// And read back setup from CVRCON for speed later
// 0x8060 is enabled with output enabled, Vdd ref, and 0-0.6(Vdd) range
CVRCON_setup = CVRCON; //CVRCON = 0x8060 from Tahmid http://tahmidmc.blogspot.com/
#endif //#ifdef use_vref_debug
}
Client-side python application:
import sys
import os
import logging
import serial
import serial.threaded
from serial.threaded import ReaderThread
from serial.threaded import LineReader
from midiutil.MidiFile import MIDIFile
TERMINATION_CHARACTER = 'end\r\n'
NEWLINE_CHARACTER = '\r\n'
class InvalidSerialDataException(Exception):
pass
def parseData( data ):
if ( data[-2:] != NEWLINE_CHARACTER ):
raise InvalidSerialDataException('Invalid endline character: [%s]'.format(data[-2:]))
data = data.replace('\0', '')
pitch = int( data[0:data.find(',')] )
duration = int( data[data.find(',') + 1 : data.find(NEWLINE_CHARACTER)] )
if ( pitch != -1 ):
pitch = pitch + 48
return { 'pitch': pitch, 'duration': duration }
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(
description="ECE 4760 Final Project python serial-to-file writer")
parser.add_argument('SERIALPORT')
parser.add_argument(
'TEMPO',
type=int,
help='the tempo at which the song is being played, default: %(default)s',
default=60)
parser.add_argument(
'-p', '--filepath',
type=str,
help='the filepath and filename relative to the current directory, default: %(default)s',
metavar='FILEPATH',
default='music.mid'
)
parser.add_argument(
'-v', '--verbose',
dest='verbosity',
action='count',
help='print more diagnostic messages (option can be given multiple times)',
default=0
)
parser.add_argument(
'-b', '--baud',
dest='BAUD',
help='BAUD rate to use for transmission, default: %(default)s',
default=9600
)
parser.add_argument(
'-d', '--debug',
dest='debug',
action='store_true',
help='print diagnostic messages on debug level'
)
args = parser.parse_args()
if args.verbosity > 3:
args.verbosity = 3
level = (
logging.WARNING,
logging.INFO,
logging.DEBUG,
logging.NOTSET,
)[args.verbosity]
logging.basicConfig(level=logging.DEBUG, filename='serial.log')
if args.debug:
level = logging.DEBUG
logging.getLogger('initialization').setLevel(level)
device_path = args.SERIALPORT
logging.debug('device path set to %s', device_path)
# Note, filepath is relative to current location
output_filepath = args.filepath
# Open file, append _{number} if file exists
filepath_append = ''
output_file_extension = output_filepath[output_filepath.rfind('.'):]
output_filepath = output_filepath[0:output_filepath.rfind('.')]
i = 0
while os.path.exists(output_filepath + filepath_append + output_file_extension):
i = i + 1
filepath_append = '_{}'.format(i)
output_filepath = output_filepath + filepath_append + output_file_extension
logging.debug('output filepath set to be %s', output_filepath)
ser = serial.Serial(
port=args.SERIALPORT,
baudrate=args.BAUD,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
rtscts = False,
dsrdtr = False
)
ser.write(str(args.TEMPO) + NEWLINE_CHARACTER);
logging.info('port opened')
midiFile = MIDIFile(1)
track = 0
time = 0
midiFile.addTempo(track, time, int(args.TEMPO))
channel = 0
volume = 100
sys.stdout.write('A connection has been made with the device. Collecting information...\n')
done = False
while( not done ):
data = ser.readline()
logging.debug('line received: {}'.format(repr(data)))
if ( data.replace('\0', '') == TERMINATION_CHARACTER ):
done = True
else:
data = parseData( data )
if ( data['pitch'] != -1 ):
logging.debug('Adding note: pitch=%d duration=%d, time=%f', data['pitch'], data['duration'], time)
midiFile.addNote(track, channel, data['pitch'], time, 1 / data['duration'], volume)
time = time + ( 4.0 / data['duration'] * 60 / args.TEMPO )
logging.debug('time: %f', time)
sys.stdout.write('Looks like we\'re done here. Go checkout the result in {}\n'
.format(output_filepath))
logging.info('connection closed')
logging.info('writing information')
mfile = open(output_filepath, 'wb')
midiFile.writeFile(mfile)
mfile.close()
logging.info('closed file {}'.format(output_filepath))
sys.exit(0)