ECE 4760 Final Project
 All Files Functions Variables Enumerations Enumerator Macros
project.c
Go to the documentation of this file.
1 // This pgm just blinks D.2 for testing the protoboard.
2 #include <inttypes.h>
3 #include <avr/io.h>
4 #include <avr/interrupt.h>
5 #include <stdio.h>
6 #include <math.h>
7 #include "imu.h"
8 #include <util/delay.h>
9 #include "midi.h"
10 // serial communication library
11 #include "uart.h"
12 // UART file descriptor
13 // putchar and getchar are in uart.c
14 FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
15 
16 
17 //timeout values for each task
18 #define t1 25
19 
20 // task subroutines
21 /**
22 * @brief if 1 a note is played
23 */
24 char play_on = 0;
25 /**
26 * @brief if 0 a note is turned off
27 */
28 char play_off = 0;
29 void task1(void); //blink at 2 or 8 Hz
30 void initialize(void); //all the usual mcu stuff
31 void get_pitch_roll(float *pitch, float *roll);
32 void play_note_off(void);
33 void play_note_on(uint8_t y);
34 volatile char chan1;
35 /**
36 * @brief whether or not the middle note is held down.
37 */
38 volatile char adc_chan1_on;
39 /**
40 * @brief task1 counter
41 */
42 volatile unsigned int time1; //timeout counter
43 
44 //**********************************************************
45 //timer 0 compare ISR
46 /**
47  * @brief This ISR keeps a millisecond timer for timing the main task.
48  *
49  */
50 ISR (TIMER0_COMPA_vect)
51 {
52  //Decrement the time if they are not already zero
53  if (time1>0) --time1;
54 }
55 
56 //**********************************************************
57 
58 /**
59  *Entry point and task scheduler loop
60  *
61  */
62 int main(void)
63 {
64  initialize();
65  //main task scheduler loop
66  while(1)
67  {
68  if (time1==0){time1=t1; task1();}
69  }
70 }
71 char channel = 0;
72 
73 /**
74  * @brief Rather than waiting in while loop for the ADC readings, we use the ADC interupt that sets the values asynchronously.
75  * This function checks to see if the values of the flex resistors have indicted whether a finger has been bent or not. If the
76  * value of Chanel 0 of the ADC is greater than 250 we set a flag to play a note, if it is less than 245, we set a flag to end
77  * the note. The other flex resitor, on the middle finger has a larger range so determining whether it is on or off is easier,
78  * we merely check to see if it is greater than 240, and set the appropiate flag.
79  *
80  */
81 ISR(ADC_vect) {
82 
83  uint8_t analog = ADCH;
84  if (channel == 0) {
85 
86 
87  if (analog > 250 && !chan1) {
88  play_on = 1;
89  chan1= 1;
90  }
91 
92  if (analog < 245 && chan1) {
93  chan1 = 0;
94  play_off = 1;
95  }
96 
97  ADMUX |= (1<<MUX0);
98  ADCSRA |= (1<<ADSC);
99  channel = 1;
100 
101  } else if (channel == 1) {
102  ADMUX &= ~(1<<MUX0);
103  if (analog > 240) {
104  adc_chan1_on = 1;
105  } else {
106  adc_chan1_on = 0;
107  }
108  channel = 0;
109  }
110 }
111 
112 //**********************************************************
113 
114 /**
115  * @brief This function reads the accelerometer and calculates the pitch and roll of the device.
116  *
117  * @param pitch pointer to a float that will contain the current pitch measured by the accelerometer.
118  * @param roll pointer to a float that will contain the current roll measured by the accelerometer.
119  */
120 void get_pitch_roll(float *pitch, float *roll) {
121 
122  float x,y,z;
123  read_accel(&x,&y,&z);
124  *pitch = atan2(x, sqrt((y * y) + (z * z)));
125  *roll = atan2(y, sqrt((x * x) + (z * z)));
126  *pitch *= 180.0 / M_PI;
127  *roll *= 180.0 / M_PI;
128 
129 }
130 
131 /**
132 * @brief counter to keep track of what instrument is on the MIDI channel
133 */
134 uint8_t instrument = 0;
135 /**
136 * @brief Current note being played
137 */
138 uint8_t note = 60;
139 /**
140 * @brief base_note for transposing the C-major scale
141 */
142 uint8_t base_note = 0;
143 uint16_t time = 0;
144 #define WAIT_VALUE (300 / t1)
145 #define RATE_THRES (240)
146 
147 uint16_t wait = WAIT_VALUE;
148 char handle_notes(float);
149 
150 /**
151 * @brief used to keep track of the roll so we can bend the pitch relative to the note it was played at.
152 */
153 float old_roll;
154 
155 /**
156  * @brief This function is the function that gets called every 25ms that handles the main logic, it reads the sensor values, checks flags set by the
157  * ADC interrupt vector, and sends the according MIDI signals.
158  */
159 void task1(void)
160 {
161 
162  ADCSRA |= (1<<ADSC);
163  float x,y,z;
164 
165  float roll,pitch;
166  get_pitch_roll(&pitch,&roll);
167  //Save the old roll if we play a new note so we don't bend the note too much.
168  if(handle_notes(roll)) {
169  old_roll = roll;
170  }
171 
172 
173  // bend the pitch if the middle finger is down.
174  if (chan1 && adc_chan1_on) {
175  roll -= old_roll;
176  roll/=90.0;
177  roll*=2000;
178  pitch_bend(2,(2000+((int16_t)roll))<<2);
179  } else {
180  read_gyro(&x,&y,&z);
181  // We don't want to change the instrument or key too often
182  if (wait) wait--;
183  if (x > RATE_THRES && !wait) {
184  instrument++;
186  wait = WAIT_VALUE;
187  } else if (x<-RATE_THRES && !wait) {
188  wait = WAIT_VALUE;
189  instrument--;
191  } else if(y > RATE_THRES && !wait) {
192  wait = WAIT_VALUE;
193  base_note -= 1;
194  } else if(y < -RATE_THRES && !wait) {
195  wait = WAIT_VALUE;
196  base_note += 1;
197  }
198  }
199 }
200 /**
201 * @brief C-major scale in MIDI notes can add or subtract a value to transpose to different keys
202 */
203 char cmajor[15] = {48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72};
204 /**
205 * @brief Takes the current roll and determines if a note needs to be played or turned off. It calculates the note from the roll. We are playing notes only on a major scale. It can be transposed up or down.
206 *
207 * @param roll - roll of the glove
208 *
209 * @return 1 if a new note was played, 0 otherwise
210 */
211 char handle_notes(float roll)
212 {
213  int8_t sharp = 0;
214  if (adc_chan1_on) {
215  sharp = 1;
216  }
217  if (play_on) {
218  float froll = roll;
219  froll /= 90.0;
220  froll *= 7;
221  int8_t iroll = (int8_t)froll;
222  iroll += 8;
223 
224  play_note_on(base_note+cmajor[iroll]);
225  PORTD |= (1<<PD2);
226  play_on = 0;
227  return 1;
228  }else
229  if (play_off) {
230  play_note_off();
231  PORTD &= ~(1<<PD2);
232  play_off = 0;
233  }
234  return 0;
235 }
236 
237 /**
238 * @brief Sends the MIDI note on signal on the serial line with the note specified in the parameter at velocity 100.
239 * stores the note in a global variable so we can turn it off at a later time.
240 *
241 * @param n - note to play.
242 */
243 void play_note_on(uint8_t n) {
244 
245  note =(uint8_t)n;
246  send_note(2,100,note);
247 }
248 
249 
250 /**
251 * @brief Turns the note we had stored in the note global varible off.
252 */
253 void play_note_off(void) {
254 
255  send_note_off(2,100,note);
256 
257 }
258 
259 //**********************************************************
260 //Set it all up
261 void initialize(void)
262 {
263 
264  //set up the LED port
265  DDRD = (1<<PD2) ; // PORT D.2 is an ouput
266  PORTD &= ~(1<<PD2);
267 
268  //set up timer 0 for 1 mSec timebase
269  TIMSK0= (1<<OCIE0A); //turn on timer 0 cmp match ISR
270  OCR0A = 249; //set the compare register to 250 time ticks
271  //set prescalar to divide by 64
272  TCCR0B= 3;
273  // turn on clear-on-match
274  TCCR0A= (1<<WGM01) ;
275 
276  //init the task timer
277  time1=t1;
278 
279  //init the UART -- uart_init() is in uart.c
280  uart_init();
281  stdout = stdin = stderr = &uart_str;
282  ADMUX = (1<<ADLAR) | (1<<REFS0);
283  ADCSRA = (1<<ADEN) | (1<<ADIF) | (1<<ADIE) + 7;
284  sei();
285 
286 
288  // 1600 Hz (0xA)
289  //crank up the ISRs
290  ADCSRA |= (1<<ADSC);
291 }
292 
293 
uint8_t instrument
counter to keep track of what instrument is on the MIDI channel
Definition: project.c:134
uint16_t wait
Definition: project.c:147
volatile unsigned int time1
task1 counter
Definition: project.c:42
FILE uart_str
Definition: project.c:14
void uart_init(void)
Definition: uart.c:35
void get_pitch_roll(float *pitch, float *roll)
This function reads the accelerometer and calculates the pitch and roll of the device.
Definition: project.c:120
#define t1
Definition: project.c:18
char play_off
if 0 a note is turned off
Definition: project.c:28
char channel
Definition: project.c:71
char handle_notes(float)
Takes the current roll and determines if a note needs to be played or turned off. It calculates the n...
Definition: project.c:211
void pitch_bend(uint8_t channel, int16_t value)
Definition: midi.c:29
#define RATE_THRES
Definition: project.c:145
Definition: imu.h:183
int uart_putchar(char c, FILE *stream)
Definition: uart.c:51
float old_roll
used to keep track of the roll so we can bend the pitch relative to the note it was played at...
Definition: project.c:153
volatile char chan1
Definition: project.c:34
void play_note_off(void)
Turns the note we had stored in the note global varible off.
Definition: project.c:253
#define WAIT_VALUE
Definition: project.c:144
void play_note_on(uint8_t y)
Sends the MIDI note on signal on the serial line with the note specified in the parameter at velocity...
Definition: project.c:243
uint8_t note
Current note being played.
Definition: project.c:138
int uart_getchar(FILE *stream)
Definition: uart.c:102
char play_on
if 1 a note is played
Definition: project.c:24
ISR(TIMER0_COMPA_vect)
This ISR keeps a millisecond timer for timing the main task.
Definition: project.c:50
Definition: imu.h:158
void initialize(void)
Definition: project.c:261
uint16_t init_imu(enum gyro_scale gScl, enum accel_scale aScl, enum mag_scale mScl, enum gyro_odr gODR, enum accel_odr aODR, enum mag_odr mODR)
Initilizes the IMU and enables all 9 axes and set the respective scales and data rates.
Definition: imu.c:32
void read_gyro(float *x, float *y, float *z)
Reads the gyroscope and calculates the real value from the raw reading.
Definition: imu.c:233
void change_instrument(uint8_t channel, uint8_t instrument)
Definition: midi.c:37
uint16_t time
Definition: project.c:143
void task1(void)
This function is the function that gets called every 25ms that handles the main logic, it reads the sensor values, checks flags set by the ADC interrupt vector, and sends the according MIDI signals.
Definition: project.c:159
void send_note(uint8_t channel, uint8_t vel, uint8_t pitch)
Definition: midi.c:52
int main(void)
Definition: project.c:62
volatile char adc_chan1_on
whether or not the middle note is held down.
Definition: project.c:38
void read_accel(float *x, float *y, float *z)
Reads the accelometer and calculates the real value from the raw reading.
Definition: imu.c:241
char cmajor[15]
C-major scale in MIDI notes can add or subtract a value to transpose to different keys...
Definition: project.c:203
uint8_t base_note
base_note for transposing the C-major scale
Definition: project.c:142
void send_note_off(uint8_t channel, uint8_t vel, uint8_t pitch)
Definition: midi.c:58