school project

Dependencies:   PololuLedStrip mbed

Files at this revision

API Documentation at this revision

Comitter:
taptoneesarm
Date:
Thu Jan 26 11:00:15 2017 +0000
Commit message:
schooll led proiject

Changed in this revision

ADC_full/adc.cpp Show annotated file Show diff for this revision Revisions of this file
ADC_full/adc.h Show annotated file Show diff for this revision Revisions of this file
PololuLedStrip.lib Show annotated file Show diff for this revision Revisions of this file
TextLCD.cpp Show annotated file Show diff for this revision Revisions of this file
TextLCD.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADC_full/adc.cpp	Thu Jan 26 11:00:15 2017 +0000
@@ -0,0 +1,439 @@
+/* mbed Library - ADC
+ * Copyright (c) 2010, sblandford
+ * released under MIT license http://mbed.org/licence/mit
+ */
+#include "mbed.h"
+#include "adc.h"
+
+
+ADC *ADC::instance;
+
+ADC::ADC(int sample_rate, int cclk_div)
+    {
+
+    int i, adc_clk_freq, pclk, clock_div, max_div=1;
+
+    //Work out CCLK
+    adc_clk_freq=CLKS_PER_SAMPLE*sample_rate;
+    int m = (LPC_SC->PLL0CFG & 0xFFFF) + 1;
+    int n = (LPC_SC->PLL0CFG >> 16) + 1;
+    int cclkdiv = LPC_SC->CCLKCFG + 1;
+    int Fcco = (2 * m * XTAL_FREQ) / n;
+    int cclk = Fcco / cclkdiv;
+
+    //Power up the ADC        
+    LPC_SC->PCONP |= (1 << 12);
+    //Set clock at cclk / 1.
+    LPC_SC->PCLKSEL0 &= ~(0x3 << 24);    
+    switch (cclk_div) {
+        case 1:
+            LPC_SC->PCLKSEL0 |= 0x1 << 24;
+            break;
+        case 2:
+            LPC_SC->PCLKSEL0 |= 0x2 << 24;
+            break;
+        case 4:
+            LPC_SC->PCLKSEL0 |= 0x0 << 24;
+            break;
+        case 8:
+            LPC_SC->PCLKSEL0 |= 0x3 << 24;
+            break;
+        default:
+            fprintf(stderr, "Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n",
+                cclk_div);
+            fprintf(stderr, "Defaulting to 1.\n");
+            LPC_SC->PCLKSEL0 |= 0x1 << 24;
+            break;
+    }
+    pclk = cclk / cclk_div;
+    clock_div=pclk / adc_clk_freq;
+
+    if (clock_div > 0xFF) {
+        fprintf(stderr, "Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n",
+            clock_div);
+        clock_div=0xFF;
+    }
+    if (clock_div == 0) {
+        fprintf(stderr, "Warning: Clock division is 0. Re-Setting to 1.\n");
+        clock_div=1;
+    }
+
+    _adc_clk_freq=pclk / clock_div;
+    if (_adc_clk_freq > MAX_ADC_CLOCK) {
+        fprintf(stderr, "Warning: Actual ADC sample rate of %u which is above %u limit\n",
+            _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE);
+        while ((pclk / max_div) > MAX_ADC_CLOCK) max_div++;
+        fprintf(stderr, "Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE);
+    }
+
+    LPC_ADC->ADCR =
+        ((clock_div - 1 ) << 8 ) |    //Clkdiv
+        ( 1 << 21 );                  //A/D operational
+
+    //Default no channels enabled
+    LPC_ADC->ADCR &= ~0xFF;
+    //Default NULL global custom isr
+    _adc_g_isr = NULL;
+    //Initialize arrays
+    for (i=7; i>=0; i--) {
+        _adc_data[i] = 0;
+        _adc_isr[i] = NULL;
+    }
+
+
+    //* Attach IRQ
+    instance = this;
+    NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr);
+
+    //Disable global interrupt
+    LPC_ADC->ADINTEN &= ~0x100;
+
+};
+
+void ADC::_adcisr(void)
+{
+    instance->adcisr();
+}
+
+
+void ADC::adcisr(void)  
+{
+    uint32_t stat;
+    int chan;
+
+    // Read status
+    stat = LPC_ADC->ADSTAT;
+    //Scan channels for over-run or done and update array
+    if (stat & 0x0101) _adc_data[0] = LPC_ADC->ADDR0;
+    if (stat & 0x0202) _adc_data[1] = LPC_ADC->ADDR1;
+    if (stat & 0x0404) _adc_data[2] = LPC_ADC->ADDR2;
+    if (stat & 0x0808) _adc_data[3] = LPC_ADC->ADDR3;
+    if (stat & 0x1010) _adc_data[4] = LPC_ADC->ADDR4;
+    if (stat & 0x2020) _adc_data[5] = LPC_ADC->ADDR5;
+    if (stat & 0x4040) _adc_data[6] = LPC_ADC->ADDR6;
+    if (stat & 0x8080) _adc_data[7] = LPC_ADC->ADDR7;
+
+    // Channel that triggered interrupt
+    chan = (LPC_ADC->ADGDR >> 24) & 0x07;
+    //User defined interrupt handlers
+    if (_adc_isr[chan] != NULL)
+        _adc_isr[chan](_adc_data[chan]);
+    if (_adc_g_isr != NULL)
+        _adc_g_isr(chan, _adc_data[chan]); 
+    return;
+}
+
+int ADC::_pin_to_channel(PinName pin) {
+    int chan;
+    switch (pin) {
+        case p15://=p0.23 of LPC1768
+        default:
+            chan=0;
+            break;
+        case p16://=p0.24 of LPC1768
+            chan=1;
+            break;
+        case p17://=p0.25 of LPC1768
+            chan=2;
+            break;
+        case p18://=p0.26 of LPC1768
+            chan=3;
+            break;
+        case p19://=p1.30 of LPC1768
+            chan=4;
+            break;
+        case p20://=p1.31 of LPC1768
+            chan=5;
+            break;
+    }
+    return(chan);
+}
+
+PinName ADC::channel_to_pin(int chan) {
+    const PinName pin[8]={p15, p16, p17, p18, p19, p20, p15, p15};
+    
+    if ((chan < 0) || (chan > 5))
+        fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan);
+    return(pin[chan & 0x07]);
+} 
+
+
+int ADC::channel_to_pin_number(int chan) {
+    const int pin[8]={15, 16, 17, 18, 19, 20, 0, 0};
+    
+    if ((chan < 0) || (chan > 5))
+        fprintf(stderr, "ADC channel %u is outside range available to MBED pins.\n", chan);
+    return(pin[chan & 0x07]);
+} 
+
+
+uint32_t ADC::_data_of_pin(PinName pin) {
+    //If in burst mode and at least one interrupt enabled then
+    //take all values from _adc_data
+    if (burst() && (LPC_ADC->ADINTEN & 0x3F)) {
+        return(_adc_data[_pin_to_channel(pin)]);
+    } else {
+        //Return current register value or last value from interrupt
+        switch (pin) {
+            case p15://=p0.23 of LPC1768
+            default:
+                return(LPC_ADC->ADINTEN & 0x01?_adc_data[0]:LPC_ADC->ADDR0);
+            case p16://=p0.24 of LPC1768
+                return(LPC_ADC->ADINTEN & 0x02?_adc_data[1]:LPC_ADC->ADDR1);
+            case p17://=p0.25 of LPC1768
+                return(LPC_ADC->ADINTEN & 0x04?_adc_data[2]:LPC_ADC->ADDR2);
+            case p18://=p0.26 of LPC1768:
+                return(LPC_ADC->ADINTEN & 0x08?_adc_data[3]:LPC_ADC->ADDR3);
+            case p19://=p1.30 of LPC1768
+                return(LPC_ADC->ADINTEN & 0x10?_adc_data[4]:LPC_ADC->ADDR4);
+            case p20://=p1.31 of LPC1768
+                return(LPC_ADC->ADINTEN & 0x20?_adc_data[5]:LPC_ADC->ADDR5);
+        }
+    }
+}
+
+//Enable or disable an ADC pin
+void ADC::setup(PinName pin, int state) {
+    int chan;    
+    chan=_pin_to_channel(pin);
+    if ((state & 1) == 1) {
+        switch(pin) {
+            case p15://=p0.23 of LPC1768
+            default:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14;
+                break;
+            case p16://=p0.24 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16;
+                break;
+            case p17://=p0.25 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18;
+                break;
+            case p18://=p0.26 of LPC1768:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20;
+                break;
+            case p19://=p1.30 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28;
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28;
+                break;
+            case p20://=p1.31 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30;
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30;
+               break;
+        }
+        //Only one channel can be selected at a time if not in burst mode
+        if (!burst()) LPC_ADC->ADCR &= ~0xFF;
+        //Select channel
+        LPC_ADC->ADCR |= (1 << chan);
+    }
+    else {
+        switch(pin) {
+            case p15://=p0.23 of LPC1768
+            default:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+                break;
+            case p16://=p0.24 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+                break;
+            case p17://=p0.25 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+                break;
+            case p18://=p0.26 of LPC1768:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+                break;
+            case p19://=p1.30 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+                break;
+            case p20://=p1.31 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+                break;
+        }
+        LPC_ADC->ADCR &= ~(1 << chan);
+    }
+}
+//Return channel enabled/disabled state
+int ADC::setup(PinName pin) {
+    int chan;
+    
+    chan = _pin_to_channel(pin);
+    return((LPC_ADC->ADCR & (1 << chan)) >> chan);
+}
+
+//Select channel already setup
+void ADC::select(PinName pin) {
+    int chan;
+    
+    //Only one channel can be selected at a time if not in burst mode
+    if (!burst()) LPC_ADC->ADCR &= ~0xFF;
+    //Select channel
+    chan = _pin_to_channel(pin);
+    LPC_ADC->ADCR |= (1 << chan);
+}
+
+//Enable or disable burst mode
+void ADC::burst(int state) {
+    if ((state & 1) == 1) {
+        if (startmode(0) != 0)
+            fprintf(stderr, "Warning. startmode is %u. Must be 0 for burst mode.\n", startmode(0));
+        LPC_ADC->ADCR |= (1 << 16);
+    }
+    else 
+        LPC_ADC->ADCR &= ~(1 << 16);
+}
+//Return burst mode state
+int  ADC::burst(void) {
+    return((LPC_ADC->ADCR & (1 << 16)) >> 16);
+}
+
+//Set startmode and edge
+void ADC::startmode(int mode, int edge) {
+    int lpc_adc_temp;
+    
+    //Reset start mode and edge bit, 
+    lpc_adc_temp = LPC_ADC->ADCR & ~(0x0F << 24);
+    //Write with new values
+    lpc_adc_temp |= ((mode & 7) << 24) | ((edge & 1) << 27);
+    LPC_ADC->ADCR = lpc_adc_temp;
+}
+
+//Return startmode state according to mode_edge=0: mode and mode_edge=1: edge
+int ADC::startmode(int mode_edge){
+    switch (mode_edge) {
+        case 0:
+        default:
+            return((LPC_ADC->ADCR >> 24) & 0x07);
+        case 1:
+            return((LPC_ADC->ADCR >> 27) & 0x01);
+    }
+}
+
+//Start ADC conversion
+void ADC::start(void) {
+    startmode(1,0);
+}
+
+
+//Set interrupt enable/disable for pin to state
+void ADC::interrupt_state(PinName pin, int state) {
+    int chan;
+    
+    chan = _pin_to_channel(pin);
+    if (state == 1) {
+        LPC_ADC->ADINTEN &= ~0x100;
+        LPC_ADC->ADINTEN |= 1 << chan;
+        /* Enable the ADC Interrupt */
+        NVIC_EnableIRQ(ADC_IRQn);
+    } else {
+        LPC_ADC->ADINTEN &= ~( 1 << chan );
+        //Disable interrrupt if no active pins left
+        if ((LPC_ADC->ADINTEN & 0xFF) == 0)
+            NVIC_DisableIRQ(ADC_IRQn);
+    }
+}
+
+//Return enable/disable state of interrupt for pin
+int ADC::interrupt_state(PinName pin) {
+    int chan;
+        
+    chan = _pin_to_channel(pin);
+    return((LPC_ADC->ADINTEN >> chan) & 0x01);
+}
+
+
+//Attach custom interrupt handler replacing default
+void ADC::attach(void(*fptr)(void)) {
+    //* Attach IRQ
+    NVIC_SetVector(ADC_IRQn, (uint32_t)fptr);
+}
+
+//Restore default interrupt handler
+void ADC::detach(void) {
+    //* Attach IRQ
+    instance = this;
+    NVIC_SetVector(ADC_IRQn, (uint32_t)&_adcisr);
+}
+
+
+//Append interrupt handler for pin to function isr
+void ADC::append(PinName pin, void(*fptr)(uint32_t value)) {
+    int chan;
+        
+    chan = _pin_to_channel(pin);
+    _adc_isr[chan] = fptr;
+}
+
+//Append interrupt handler for pin to function isr
+void ADC::unappend(PinName pin) {
+    int chan;
+        
+    chan = _pin_to_channel(pin);
+    _adc_isr[chan] = NULL;
+}
+
+//Unappend global interrupt handler to function isr
+void ADC::append(void(*fptr)(int chan, uint32_t value)) {
+    _adc_g_isr = fptr;
+}
+
+//Detach global interrupt handler to function isr
+void ADC::unappend() {
+    _adc_g_isr = NULL;
+}
+
+//Set ADC offset
+void offset(int offset) {
+    LPC_ADC->ADTRM &= ~(0x07 << 4);
+    LPC_ADC->ADTRM |= (offset & 0x07) << 4;
+}
+
+//Return current ADC offset
+int offset(void) {
+    return((LPC_ADC->ADTRM >> 4) & 0x07);
+}
+
+//Return value of ADC on pin
+int ADC::read(PinName pin) {
+    //Reset DONE and OVERRUN flags of interrupt handled ADC data
+    _adc_data[_pin_to_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30));
+    //Return value
+    return((_data_of_pin(pin) >> 4) & 0xFFF);
+}
+
+//Return DONE flag of ADC on pin
+int ADC::done(PinName pin) {
+    return((_data_of_pin(pin) >> 31) & 0x01);
+}
+
+//Return OVERRUN flag of ADC on pin
+int ADC::overrun(PinName pin) {
+    return((_data_of_pin(pin) >> 30) & 0x01);
+}
+
+int ADC::actual_adc_clock(void) {
+    return(_adc_clk_freq);
+}
+
+int ADC::actual_sample_rate(void) {
+    return(_adc_clk_freq / CLKS_PER_SAMPLE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADC_full/adc.h	Thu Jan 26 11:00:15 2017 +0000
@@ -0,0 +1,131 @@
+/* mbed Library - ADC
+ * Copyright (c) 2010, sblandford
+ * released under MIT license http://mbed.org/licence/mit
+ */
+
+#ifndef MBED_ADC_H
+#define MBED_ADC_H
+ 
+#include "mbed.h"
+#define XTAL_FREQ       12000000
+#define MAX_ADC_CLOCK   13000000
+#define CLKS_PER_SAMPLE 64
+
+class ADC {
+public:
+
+    //Initialize ADC with ADC maximum sample rate of
+    //sample_rate and system clock divider of cclk_div
+    //Maximum recommened sample rate is 184000
+    ADC(int sample_rate, int cclk_div);
+
+    //Enable/disable ADC on pin according to state
+    //and also select/de-select for next conversion
+    void setup(PinName pin, int state);
+
+    //Return enabled/disabled state of ADC on pin
+    int setup(PinName pin);
+
+    //Enable/disable burst mode according to state
+    void burst(int state);
+
+    //Select channel already setup
+    void select(PinName pin);
+
+    //Return burst mode enabled/disabled
+    int burst(void);
+
+    /*Set start condition and edge according to mode:
+    0 - No start (this value should be used when clearing PDN to 0).
+    1 - Start conversion now.
+    2 - Start conversion when the edge selected by bit 27 occurs on the P2.10 / EINT0 / NMI pin.
+    3 - Start conversion when the edge selected by bit 27 occurs on the P1.27 / CLKOUT /
+        USB_OVRCRn / CAP0.1 pin.
+    4 - Start conversion when the edge selected by bit 27 occurs on MAT0.1. Note that this does
+        not require that the MAT0.1 function appear on a device pin.
+    5 - Start conversion when the edge selected by bit 27 occurs on MAT0.3. Note that it is not
+        possible to cause the MAT0.3 function to appear on a device pin.
+    6 - Start conversion when the edge selected by bit 27 occurs on MAT1.0. Note that this does
+        not require that the MAT1.0 function appear on a device pin.
+    7 - Start conversion when the edge selected by bit 27 occurs on MAT1.1. Note that this does
+        not require that the MAT1.1 function appear on a device pin.
+    When mode >= 2, conversion is triggered by edge:
+    0 - Rising edge
+    1 - Falling edge
+    */
+    void startmode(int mode, int edge);
+    
+    //Return startmode state according to mode_edge=0: mode and mode_edge=1: edge
+    int startmode(int mode_edge);
+    
+    //Start ADC conversion
+    void start(void);
+
+    //Set interrupt enable/disable for pin to state
+    void interrupt_state(PinName pin, int state);
+    
+    //Return enable/disable state of interrupt for pin
+    int interrupt_state(PinName pin);
+
+    //Attach custom interrupt handler replacing default
+    void attach(void(*fptr)(void));
+
+    //Restore default interrupt handler
+    void detach(void);
+
+    //Append custom interrupt handler for pin
+    void append(PinName pin, void(*fptr)(uint32_t value));
+
+    //Unappend custom interrupt handler for pin
+    void unappend(PinName pin);
+
+    //Append custom global interrupt handler
+    void append(void(*fptr)(int chan, uint32_t value));
+
+    //Unappend custom global interrupt handler
+    void unappend(void);
+
+    //Set ADC offset to a value 0-7
+    void offset(int offset);
+    
+    //Return current ADC offset
+    int offset(void);
+
+    //Return value of ADC on pin
+    int read(PinName pin);
+
+    //Return DONE flag of ADC on pin
+    int done(PinName pin);
+    
+    //Return OVERRUN flag of ADC on pin
+    int overrun(PinName pin);
+
+    //Return actual ADC clock
+    int actual_adc_clock(void);
+    
+    //Return actual maximum sample rate
+    int actual_sample_rate(void);
+
+    //Return pin ID of ADC channel
+    PinName channel_to_pin(int chan);
+
+    //Return pin number of ADC channel
+    int channel_to_pin_number(int chan);
+
+
+private:
+    int _pin_to_channel(PinName pin);
+    uint32_t _data_of_pin(PinName pin);
+
+    int _adc_clk_freq;
+    void adcisr(void);
+    static void _adcisr(void);
+    static ADC *instance;
+    
+    uint32_t _adc_data[8];
+    void(*_adc_isr[8])(uint32_t value);
+    void(*_adc_g_isr)(int chan, uint32_t value);
+    void(*_adc_m_isr)(void);
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PololuLedStrip.lib	Thu Jan 26 11:00:15 2017 +0000
@@ -0,0 +1,1 @@
+https://developer.mbed.org/users/taptoneesarm/code/PololuLedStrip/#8dc878594d24
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD.cpp	Thu Jan 26 11:00:15 2017 +0000
@@ -0,0 +1,159 @@
+/* mbed TextLCD Library, for a 4-bit LCD based on HD44780
+ * Copyright (c) 2007-2010, sford, http://mbed.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "TextLCD.h"
+#include "mbed.h"
+
+TextLCD::TextLCD(PinName rs, PinName e, PinName d4, PinName d5,
+                 PinName d6, PinName d7, LCDType type) : _rs(rs),
+        _e(e), _d(d4, d5, d6, d7),
+        _type(type) {
+
+    _e  = 1;
+    _rs = 0;            // command mode
+
+    wait(0.015);        // Wait 15ms to ensure powered up
+
+    // send "Display Settings" 3 times (Only top nibble of 0x30 as we've got 4-bit bus)
+    for (int i=0; i<3; i++) {
+        writeByte(0x3);
+        wait(0.00164);  // this command takes 1.64ms, so wait for it
+    }
+    writeByte(0x2);     // 4-bit mode
+    wait(0.000040f);    // most instructions take 40us
+
+    writeCommand(0x28); // Function set 001 BW N F - -
+    writeCommand(0x0C);
+    writeCommand(0x6);  // Cursor Direction and Display Shift : 0000 01 CD S (CD 0-left, 1-right S(hift) 0-no, 1-yes
+    cls();
+}
+
+void TextLCD::character(int column, int row, int c) {
+    int a = address(column, row);
+    writeCommand(a);
+    writeData(c);
+}
+
+void TextLCD::cls() {
+    writeCommand(0x01); // cls, and set cursor to 0
+    wait(0.00164f);     // This command takes 1.64 ms
+    locate(0, 0);
+}
+
+void TextLCD::locate(int column, int row) {
+    _column = column;
+    _row = row;
+}
+
+int TextLCD::_putc(int value) {
+    if (value == '\n') {
+        _column = 0;
+        _row++;
+        if (_row >= rows()) {
+            _row = 0;
+        }
+    } else {
+        character(_column, _row, value);
+        _column++;
+        if (_column >= columns()) {
+            _column = 0;
+            _row++;
+            if (_row >= rows()) {
+                _row = 0;
+            }
+        }
+    }
+    return value;
+}
+
+int TextLCD::_getc() {
+    return -1;
+}
+
+void TextLCD::writeByte(int value) {
+    _d = value >> 4;
+    wait(0.000040f); // most instructions take 40us
+    _e = 0;
+    wait(0.000040f);
+    _e = 1;
+    _d = value >> 0;
+    wait(0.000040f);
+    _e = 0;
+    wait(0.000040f);  // most instructions take 40us
+    _e = 1;
+}
+
+void TextLCD::writeCommand(int command) {
+    _rs = 0;
+    writeByte(command);
+}
+
+void TextLCD::writeData(int data) {
+    _rs = 1;
+    writeByte(data);
+}
+
+int TextLCD::address(int column, int row) {
+    switch (_type) {
+        case LCD20x4:
+            switch (row) {
+                case 0:
+                    return 0x80 + column;
+                case 1:
+                    return 0xc0 + column;
+                case 2:
+                    return 0x94 + column;
+                case 3:
+                    return 0xd4 + column;
+            }
+        case LCD16x2B:
+            return 0x80 + (row * 40) + column;
+        case LCD16x2:
+        case LCD20x2:
+        default:
+            return 0x80 + (row * 0x40) + column;
+    }
+}
+
+int TextLCD::columns() {
+    switch (_type) {
+        case LCD20x4:
+        case LCD20x2:
+            return 20;
+        case LCD16x2:
+        case LCD16x2B:
+        default:
+            return 16;
+    }
+}
+
+int TextLCD::rows() {
+    switch (_type) {
+        case LCD20x4:
+            return 4;
+        case LCD16x2:
+        case LCD16x2B:
+        case LCD20x2:
+        default:
+            return 2;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD.h	Thu Jan 26 11:00:15 2017 +0000
@@ -0,0 +1,111 @@
+/* mbed TextLCD Library, for a 4-bit LCD based on HD44780
+ * Copyright (c) 2007-2010, sford, http://mbed.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef MBED_TEXTLCD_H
+#define MBED_TEXTLCD_H
+
+#include "mbed.h"
+
+/**  A TextLCD interface for driving 4-bit HD44780-based LCDs
+ *
+ * Currently supports 16x2, 20x2 and 20x4 panels
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "TextLCD.h"
+ * 
+ * TextLCD lcd(p10, p12, p15, p16, p29, p30); // rs, e, d4-d7
+ * 
+ * int main() {
+ *     lcd.printf("Hello World!\n");
+ * }
+ * @endcode
+ */
+class TextLCD : public Stream {
+public:
+
+    /** LCD panel format */
+    enum LCDType {
+        LCD16x2     /**< 16x2 LCD panel (default) */
+        , LCD16x2B  /**< 16x2 LCD panel alternate addressing */
+        , LCD20x2   /**< 20x2 LCD panel */
+        , LCD20x4   /**< 20x4 LCD panel */
+    };
+
+    /** Create a TextLCD interface
+     *
+     * @param rs    Instruction/data control line
+     * @param e     Enable line (clock)
+     * @param d4-d7 Data lines for using as a 4-bit interface
+     * @param type  Sets the panel size/addressing mode (default = LCD16x2)
+     */
+    TextLCD(PinName rs, PinName e, PinName d4, PinName d5, PinName d6, PinName d7, LCDType type = LCD16x2);
+
+#if DOXYGEN_ONLY
+    /** Write a character to the LCD
+     *
+     * @param c The character to write to the display
+     */
+    int putc(int c);
+
+    /** Write a formated string to the LCD
+     *
+     * @param format A printf-style format string, followed by the
+     *               variables to use in formating the string.
+     */
+    int printf(const char* format, ...);
+#endif
+
+    /** Locate to a screen column and row
+     *
+     * @param column  The horizontal position from the left, indexed from 0
+     * @param row     The vertical position from the top, indexed from 0
+     */
+    void locate(int column, int row);
+
+    /** Clear the screen and locate to 0,0 */
+    void cls();
+
+    int rows();
+    int columns();
+
+protected:
+
+    // Stream implementation functions
+    virtual int _putc(int value);
+    virtual int _getc();
+
+    int address(int column, int row);
+    void character(int column, int row, int c);
+    void writeByte(int value);
+    void writeCommand(int command);
+    void writeData(int data);
+
+    DigitalOut _rs, _e;
+    BusOut _d;
+    LCDType _type;
+
+    int _column;
+    int _row;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Jan 26 11:00:15 2017 +0000
@@ -0,0 +1,570 @@
+#include "mbed.h"
+#include "PololuLedStrip.h"
+#include <iostream>
+
+#define LED_COUNT 60
+
+Serial pc(USBTX, USBRX); // tx, rx
+
+PololuLedStrip ledStrip(p11);
+
+rgb_color colors[LED_COUNT];
+
+Timer timer;
+
+int aMajor[6] = {0,2,2,2,0,-1};
+int bMajor[6] = {2,4,4,4,2,-1};
+int cMajor[6] = {0,1,0,2,3,-1};
+int dMajor[6] = {2,3,2,0,-1,-1};
+int eMajor[6] = {0,0,1,2,2,0};
+int fMajor[6] = {1,-1,2,3,3,1};
+int gMajor[6] = {3,0,0,0,2,3};
+
+int aMinor[6] = {0,1,2,2,0,-1};
+int bMinor[6] = {2,3,4,4,2,-1};
+int cMinor[6] = {3,4,5,5,3,-1};
+int dMinor[6] = {1,3,2,0,-1,-1};
+int eMinor[6] = {0,0,0,2,2,0};
+int fMinor[6] = {1,1,1,3,3,1};
+int gMinor[6] = {-1,-1,-1,-1,0,0};
+int gMinorActual[6] = {3,3,3,5,5,3};
+
+int aSharpMajor[6] = {1,3,3,3,1,-1};
+int cSharpMajor[6] = {1,2,1,3,4,-1};
+int fSharpMajor[6] = {2,-1,3,4,4,2};
+int gSharpMajor[6] = {4,1,1,1,3,4};
+
+int aSharpMinor[6] = {1,2,3,3,1,-1};
+int fSharpMinor[6] = {2,2,2,4,4,2};
+
+/* 
+   Above are the various arrays which represent the location of the string
+   played in a given fret to achieve a certain chord. The layout
+   of this arrangement helps the mBed light up the correct pattern
+   of LEDs on the LED strip. Not all of the chords can be played as the
+   Solo only includes the first six frets.
+*/
+
+int chordCode = 0;
+int testResult = 0;
+int interpretedChordCode = 0;
+
+using namespace std;
+
+int ledMapping(int fret, int str)
+{
+    int result;
+    // Initialise a variable that represents the specific number of the LED
+    // that is to be lit up
+    if(fret != -1)
+    {
+    if(fret%2 != 0)
+    // If the fret number is odd...
+    {
+        result = 6*fret + str - 1;
+        // ...then the LED number for that fret corresponds to the above formula
+    } else
+    // Otherwise if the fret number is even...
+    {
+        result = 6*(fret + 1) - str;
+        // ...then the LED number for that fret corresponds to the above formula
+    }
+    return result;
+    }
+    return -1;
+    // Output the LED number as a return value so that other parts of the code can
+    // use it when this function is called for a specific chord.
+}
+
+/*
+ * The decoder takes a char value that represents a chord, whether it is sharp or natural and whether
+ * it is major or minor. The inputted value for chord is either a capital (major) or lowercase letter (minor).
+ * Also assigned is a sharp value (either 0 or 1, where 0 is a natural note and 1 is a sharp)
+ */
+
+int decoder(string typed)
+{
+        int ascii = int(typed[0]);
+        // Convert the first character in the array 's' and find its
+        // hexadecimal code as a reference
+        int chordValue;
+        // Initialise a variable associated with the result so we can
+        // assess it later
+        if (65 <= ascii && ascii <= 71)
+        // If its hexadecimal number corresponds to a uppercase letter
+        // such that it is a major chord and...
+        {
+            if (typed.size() == 1)
+            // ...if the input is only one letter/symbol...
+            {
+                chordValue = ascii - 65;
+                // ...then offset the hexadecimal code of that letter by 65
+                // to map it back to the original list of non-sharp major chords and
+                // store that value.
+            }
+            else
+            // ...if the input is more than one letter/symbol then assume
+            // a hash (#) was entered to indicate a sharp value so...
+            {
+                chordValue = ascii - 51;
+                // ...offset the hexadecimal code by 51 to map it back to
+                // the original list of sharp major chords and store that value.
+            }
+        }
+        else if (97 <= ascii && ascii <= 103)
+        // If its hexadecimal number corresponds to a lowercase letter
+        // such that it is a minor chord and...
+        {
+            if (typed.size() == 1)
+            // ...if the input is only one letter/symbol...
+            {
+                chordValue = ascii - 90;
+                // ...then offset the hexadecimal code by 90 to map it back to
+                // the original list of non-sharp minor chords and store that value.
+            }
+            else
+            // ...if the input is more than one letter/symbol then assume
+            // a hash (#) was entered to indicate a sharp value so...
+            {
+                chordValue = ascii - 76;
+                // ...offset the hexadecimal code by 51 to map it back to
+                // the original list of sharp minor chords and store that value.
+            }
+        } 
+        else
+        // If the typed character does not correspond to an existing chord then...
+        {
+            pc.printf("Incorrect input\n");
+            // Output an error message that indicates an incorrect input by the
+            // user and...
+            return(-99);
+            // ...output -99 to indicate an error has occured to other parts of
+            // the code.
+        }
+        
+        if (typed.size() > 2)
+        // If the user input is greater than two characters then...
+        {
+            pc.printf("Incorrect input\n");
+            // Output an error message that indicates an incorrect input by the
+            // user and...
+            return(-99);
+            // ...output -99 to indicate an error has occured to other parts of
+            // the code. 
+        }
+        
+        if (chordValue != 15 && chordValue != 17 && chordValue != 18 && chordValue != 22 && chordValue != 23 && chordValue != 24 && chordValue != 25 && chordValue != 27)
+        // If the obtained chord value corresponds to a chord that can actually be
+        // played by Solo then...
+        {
+             return chordValue;
+             // ...output that chord value
+         }
+         else
+         // If the chord value corresponds to a chord that cannot be played by Solo or 
+         // is not even in the original table of chords then...
+         {
+             pc.printf("This chord will not be played by the Solo\n");
+             // Output an error message that indicates a chord that cannot be played by the
+             // Solo and...
+             return -1;
+             // ...output -1 to indicate an error with the input
+         }
+}
+
+void illuminator (int position, int colour)
+{
+     //pc.printf ("Position: %d Colour: %d\n",position,colour);
+     if (position != -1)
+     {
+         if (colour == -1)
+         {
+            colors[position] = (rgb_color){40,0,0};
+         }
+         else if (colour == 0)
+         {
+            colors[position] = (rgb_color){40,40,40};
+         }
+         else if (colour == 1)
+         {
+            colors[position] = (rgb_color){0,40,0};
+         }
+         else
+         {
+            pc.printf ("Error: invalid colour number in illuminator\n");
+         }
+    }
+}
+
+void feedback(int colour)
+{
+    if (colour == 1)
+    {
+        pc.printf("Great Job!\n");
+    }
+    else if (colour == 0)
+    {
+        pc.printf("Processing...\n");
+    }
+    else if (colour == -1)
+    {
+        pc.printf("You suck!\n");
+    }
+    else
+    {
+        pc.printf("Error: Colour input incorrect");
+    }
+}
+
+void identifier(int shape)
+{
+    int list;
+    // Initialise an integer variable 'result' that represents the list a chord belongs to.
+    int position;
+    // Initialise an integer variable 'position' that represents the chord's relative position
+    // in its allocated list.
+    const char* letter;
+    // Intialise a character variable 'letter' that will store the letter of the chord (A to G)
+    string type;
+    // Initialise a string (sentence) variable 'type' that will store the type of the chord
+    // e.g. "Sharp Minor" or "Major" etc.
+
+    list = int(shape/7);
+    // Set the value of the 'list' variable to correspond to the list to which
+    // the chord (represented by 'shape') belongs to. Each list holds 7 chords
+    // relating to the first 7 letters of the alphabet (A to G). Therefore the list
+    // a chord originates from can be determined by interpreting the integer value
+    // the chord rounds down to, when its value is divided by seven. For example...
+
+    if (list == 0)
+        // ...if the chord comes from list '0', then it must be...
+    {
+        type = "Major";
+        // ... a Major chord.
+    } else if (list == 1)
+        // ...if the chord comes from list '1', then it must be...
+    {
+        type = "Minor";
+        // ... a Minor chord.
+    } else if (list == 2)
+        // ...if the chord comes from list '2', then it must be...
+    {
+        type = "Sharp Major";
+        // ... a Sharp Major chord.
+    } else if (list == 3)
+        // ...if the chord comes from list '3', then it must be...
+    {
+        type = "Sharp Minor";
+        // ... a Sharp Minor chord.
+    }
+
+    position = 7*(list + 1) - shape;
+    // Set the value of the 'position' as an indication of the position in the given list,
+    // that is to say that higher chord values in the same list will have a lower
+    // 'letter' due to the layout of the equation above. Irrespective of the list in
+    // which it occurs...
+
+    if (position == 1)
+        // ...if the position value is 1, then it must be...
+    {
+        letter = "G";
+        // ... a G chord.
+    } else if (position == 2)
+        // ...if the position value is 2, then it must be...
+    {
+        letter = "F";
+        // ... an F chord.
+    } else if (position == 3)
+        // ...if the position value is 2, then it must be...
+    {
+        letter = "E";
+        // ... an E chord.
+    } else if (position == 4)
+        // ...if the position value is 4, then it must be...
+    {
+        letter = "D";
+        // ... a D chord.
+    } else if (position == 5)
+        // ...if the position value is 5, then it must be...
+    {
+        letter = "C";
+        // ... a C chord.
+    } else if (position == 6)
+        // ...if the position value is 6, then it must be...
+    {
+        letter = "B";
+        // ... a B chord.
+    } else if (position == 7)
+        // ...if the position value is 7, then it must be...
+    {
+        letter = "A";
+        // ... an A chord.
+    }
+    pc.printf("%s %s\n",letter,type.c_str());
+    // Output the letter of the chord, followed by its type separated by a
+    // space such that the full name of the chord is displayed.
+}
+               
+//TODO fix return values
+//Change to a case/switch structure
+int shaper (int shape, int colour) 
+{
+    if (shape == 0)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(aMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 1)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(bMajor[i],i+1), colour);
+        }
+        feedback(colour);
+ 
+    }
+    else if (shape == 2)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(cMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 3)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(dMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+
+    else if (shape == 4)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(eMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 5)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(fMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 6)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(gMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 7)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(aMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 8)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(bMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 9)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(cMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 10)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(dMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 11)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(eMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 12)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(fMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 13)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(gMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 14)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(aSharpMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 16)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator (ledMapping(cSharpMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 19)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator(ledMapping(fSharpMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 20)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator(ledMapping(gSharpMajor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 21)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator(ledMapping(aSharpMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else if (shape == 26)
+    {
+        for(int i = 0; i<6; i++)
+        {
+            illuminator(ledMapping(fSharpMinor[i],i+1), colour);
+        }
+        feedback(colour);
+    }
+    else
+    {
+        //this point should never be reached; there are no other chords
+        pc.printf ("Error: shaper failed\n");
+    }
+    // Send the colors to the LED strip.
+    ledStrip.write(colors, LED_COUNT);
+    if(colour == 1)
+    {
+       wait(0.5);
+       for(int i=0; i < LED_COUNT ; i++)
+       {
+          colors[i] = (rgb_color){ 0, 0, 0 };
+       }
+       ledStrip.write(colors, LED_COUNT);
+    }
+
+    return (0);
+}
+
+int tester (int testChord, int requestChord)
+{
+ /* if (testChord == 0 || requestChord == 0)
+    {
+        return (99);
+    }
+ */
+    if (testChord == requestChord)
+    {
+        return (1);
+    }
+    else
+    {
+        return (-1);
+    }
+}
+
+int interpreter(int requestedCode)
+{
+/*   int r = 0;
+    int chord = 0;
+ 
+    r = rand() % 10;
+    chord = rand() % 24;
+    if(chord == 0) chord=1;
+        
+    if(r>1) return(requestedCode);
+    else return(chord); */    
+}
+
+
+int main()
+{
+    pc.baud (921600);
+    string chord;
+    char typed[20];
+    while(1)
+    {
+        pc.printf("Type in the chord you would like to use \n");
+        pc.scanf("%s",typed);
+        chordCode = decoder(typed);
+        if (chordCode != -99 && chordCode != -1)
+        {
+            identifier(chordCode);
+            shaper(chordCode, 0);
+            interpretedChordCode = interpreter(chordCode);
+            testResult = tester(interpretedChordCode, chordCode);
+        
+            while(1)
+            {
+                if(testResult == -1)
+                {
+                   wait(2);
+                   shaper(chordCode, testResult);
+                   wait(0.5);
+                   shaper(chordCode, 0);
+                   wait(1);
+                   interpretedChordCode = interpreter(chordCode);
+                   testResult = tester(interpretedChordCode, chordCode);
+                }
+                else if(testResult == 1)
+                {
+                   wait(2);
+                   shaper(chordCode, testResult);
+                   wait(0.5);
+                   break;
+                }
+                else if(testResult == 99)
+                {
+                    pc.printf("Error");
+                    break;
+                }
+            }
+        wait_ms(10);
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Thu Jan 26 11:00:15 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/6c34061e7c34
\ No newline at end of file