Jump to content
43oh

NatureTM

Members
  • Content Count

    218
  • Joined

  • Last visited

  • Days Won

    13

Reputation Activity

  1. Like
    NatureTM got a reaction from dangpzanco in Generating random numbers.   
    I ran my ADC-based random number generator's output through the DIEHARD battery of tests for randomness quality and it failed miserably. I tried different pins for the ADC and a few different things in the code and all the results were terrible. That should probably have been expected, but a little part of me was still hopeful. I guess we shouldn't use it for any kind of cryptography.
     
    I also took a closer look at how I was removing the bias. I think I just pulled that code out of my booty, because the fact that it led to low bias output seems to just have been luck. It works when the chances of getting a 1 (or 0, I forgot) are around 30%. DIEHARD thought either method had too much bias anyway.
     
    The good news is that I wrote a tiny bit of java to take the serial output and put it into a binary file for consumption by DIEHARD, along with a little MSP430 prog to send the data. 9600 is way too slow, so I run it through an FTDI. My point is, if anyone feels like playing around with randomness and has a function they want tested, I'd gladly run it through my setup, saving you the hassle.
  2. Like
    NatureTM got a reaction from dangpzanco in Generating random numbers.   
    I was looking for example code for RF2500 last night and came across some TI code for using the ADC as a random number generator. The function was in assembly, so I rewrote it in C:
     

    bool getRandomBit(){ ADC10CTL1 |= INCH_5; ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON; ADC10CTL0 |= ENC + ADC10SC; while(ADC10CTL1 & ADC10BUSY); return ADC10MEM & 0x01; }
     
    Pin 1.5 is floating unconnected, and is measured by the adc. The LSB is used as the random bit. There's a twist though. Pin 1.4 is also floating unconnected, and is used as Vref+, so the top end of the range is floating as well. I thought that was pretty clever. Nice, TI!
     
    I wrote a few console programs to help me visualize the randomness. It turned out the generator was biased toward producing 0's. This function used with the previous function seemed to remove the bias:
     

    bool get0BiasRandomBit(){ if(getRandomBit()){ if(getRandomBit()) return 0; else return 1; } else{ if(getRandomBit()) return 1; else return 0; } }
     
    To test for bias, I displayed a meandering line in a serial console. If I get more 0's than 1's, the line should slowly skew to the left or right:
     

    #include "msp430g2231.h" #include "config.h" #include "softserial.h" #include #define CONSOLE_WIDTH 80 bool getRandomBit(); bool get0BiasRandomBit(); void main(){ int linePositon = CONSOLE_WIDTH / 2; char cursorPosition; DCOCTL = CALDCO_16MHZ; BCSCTL1 = CALBC1_16MHZ; WDTCTL = WDTPW + WDTHOLD; // Stop WDT SoftSerial_init(); _enable_interrupts(); while(1){ for(cursorPosition = 0; cursorPosition < linePositon; cursorPosition++) SoftSerial_xmit('8'); SoftSerial_xmit(' '); cursorPosition++; while(cursorPosition < CONSOLE_WIDTH){ SoftSerial_xmit('8'); cursorPosition++; } if(get0BiasRandomBit()) linePositon++; else linePositon--; if(linePositon < 0) linePositon = CONSOLE_WIDTH + linePositon; else if(linePositon >= CONSOLE_WIDTH - 1) linePositon = linePositon - CONSOLE_WIDTH; } } bool get0BiasRandomBit(){ if(getRandomBit()){ if(getRandomBit()) return 0; else return 1; } else{ if(getRandomBit()) return 1; else return 0; } } bool getRandomBit(){ ADC10CTL1 |= INCH_5; ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON; ADC10CTL0 |= ENC + ADC10SC; while(ADC10CTL1 & ADC10BUSY); return ADC10MEM & 0x01; }
     
    and some example output from this code:

     
    This sends a comma-separated list of random ints to the console:

    #include "msp430g2231.h" #include "config.h" #include "softserial.h" #include #include int adcGenRand16(); void reverse(char s[]); void itoa(int n, char s[]); void txString(char string[]); bool getRandomBit(); bool get0BiasRandomBit(); void main(){ int random; char string[7]; DCOCTL = CALDCO_16MHZ; BCSCTL1 = CALBC1_16MHZ; WDTCTL = WDTPW + WDTHOLD; // Stop WDT SoftSerial_init(); _enable_interrupts(); while(1){ random = adcGenRand16(); itoa(random, string); txString(string); } } void txString(char string[]){ int iString = 0; while(string[iString] != 0){ SoftSerial_xmit(string[iString]); iString++; } SoftSerial_xmit(','); SoftSerial_xmit(' '); } bool get0BiasRandomBit(){ if(getRandomBit()){ if(getRandomBit()) return 0; else return 1; } else{ if(getRandomBit()) return 1; else return 0; } } bool getRandomBit(){ ADC10CTL1 |= INCH_5; ADC10CTL0 |= SREF_1 + ADC10SHT_1 + REFON + ADC10ON; ADC10CTL0 |= ENC + ADC10SC; while(ADC10CTL1 & ADC10BUSY); return ADC10MEM & 0x01; } int adcGenRand16(){ char bit; unsigned int random; for(bit = 0; bit < 16; bit++){ random <<= 1; random |= get0BiasRandomBit(); } return random; } /* itoa: convert n to characters in s */ void itoa(int n, char s[]) { int i, sign; if ((sign = n) < 0) /* record sign */ n = -n; /* make n positive */ i = 0; do { /* generate digits in reverse order */ s[i++] = n % 10 + '0'; /* get next digit */ } while ((n /= 10) > 0); /* delete it */ if (sign < 0) s[i++] = '-'; s[i] = '\0'; reverse(s); } /* reverse: reverse string s in place */ void reverse(char s[]) { int i, j; char c; for (i = 0, j = strlen(s)-1; i c = s[i]; s[i] = s[j]; s[j] = c; } }
     

     
    I guess I got a little sidetracked from my original purpose of getting started with the RF2500, but I had fun.
  3. Like
    NatureTM got a reaction from russcky in Music with only a speaker, a LP, and 30 lines of code   
    Hi everyone, it's been awhile.
     
    I just wanted to share something interesting lasershark mentioned on my blog awhile ago.  It's basically a way to make music with one line of code.  It took about 15 minutes to implement on a g2553 and only requires a launchpad and a speaker.  I found it pretty entertaining.  Just connect one terminal of the speaker to Port 1.2 and the other to ground, or drive it with a transistor.
     
    Original project @ http://canonical.org/~kragen/bytebeat/
     
    #include "msp430g2553.h" #define MCLK 8000000 #define OUTPUT_SAMPLES_PER_SECOND 8000 #define PIN_SPEAKER BIT2 unsigned long t = 0; unsigned char sample; void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; P1SEL |= PIN_SPEAKER; P1DIR |= PIN_SPEAKER; TA0CTL = TASSEL_2 | MC_1; TA0CCR0 = (0x0001 << 8) - 1; TA0CCTL1 |= OUTMOD_7; TA1CTL = TASSEL_2 | MC_1; TA1CCR0 = MCLK / OUTPUT_SAMPLES_PER_SECOND - 1; TA1CCTL0 |= CCIE; _enable_interrupts(); while(1){ sample = ((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7; // sample = t*(((t>>12)|(t>>8))&(63&(t>>4))); // sample = (t*(t>>5|t>>8))>>(t>>16); // sample = t*(((t>>9)|(t>>13))&(25&(t>>6))); // sample = t*(((t>>11)&(t>>8))&(123&(t>>3))); // sample = (t*5&t>>7)|(t*3&t>>10); // sample = (t&t%255)-(t*3&t>>13&t>>6); // sample = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15))); // sample = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; // sample = ((t*(t>>12)&(201*t/100)&(199*t/100))&(t*(t>>14)&(t*301/100)&(t*399/100)))+((t*(t>>16)&(t*202/100)&(t*198/100))-(t*(t>>17)&(t*302/100)&(t*298/100))); // sample = t*(t^t+(t>>15|1)^(t-1280^t)>>10); // sample = t&t>>8; //44 khz // sample = ((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24); // sample = ((t*("36364689"[t>>13&7]&15))/12&128)+(((((t>>12)^(t>>12)-2)%11*t)/4|t>>13)&127); t++; LPM0; } } #pragma vector = TIMER1_A0_VECTOR __interrupt void T1A0_ISR(void) { TA0CCR1 = sample; LPM0_EXIT; }  
    Switch which "sample =" line is uncommented to change the "song."  The songs are from http://pelulamu.net/countercomplex/music_formula_collection.txt and there are more in that file.
     
     
  4. Like
    NatureTM reacted to multivac in The Cramp! 430 powered desktop crane lamp   
    Hey guys, so I`ve been working on this project for a while now, and wanted to show it here because you are a cool bunch.
    if i mess up, the pics are too big, or  whatever, im sorry. let me know and ill fix it asap.
    the title is pretty explanatory so here are some pics of the thing, and the associated mess (if i actually manage to attach pictures correctly)



    i know there is nothing particularly interesting in this from the microcontroller point of view, but maybe someone will like it.
    its sort of based on a liebherr lr1750 crawler crane in case anyone is interested. it is controlled with an ir remote kindly donated to the project, mostly made from scrap from my junk bin and some chips i got from the VERY generous TI`s sample program. The base, for example, is made from an old hdd, and the hole thing is screwed to, and pivots around, the main bearing.




     
    Some stuff i had to buy though, like the leds.
    I was thinking what could I do with a lot of solid conductor wire i pulled from the walls in my house when i changed the [way too old] electrical installation. i thought a lattice structure would be fun, and i have to say, its amazing how rigid the thing got. i also wanted to do some switching led driver experimenting. I used a TI TPS61199 led driver with 32 "5050" leds, that have 3 chips per package, so its 96 leds in all, at 17 mA each. the leds are mounted on a structure that is supposed to resemble a section of Eero Saarinen`s twa flight center. a sort of rebar core type thingy for the roof.



    i wanted to do a solid thing for the lamp, but it proved somewhat hard, i still might though. I





  5. Like
    NatureTM got a reaction from SirPatrick in Parking Sensors   
    I did a lot of work on battery-operated parking sensors for a startup recently.  We used magnetic sensors that are sensitive enough to detect distortions in the earth's magnetic field.  The iron an an automobile distorts the field and either increases or decreases the detected field which is read from the sensor by ADC.  They're quite low power to begin with, plus you can switch them on with a transistor for low duty-cycle sampling, leading to very long operation from battery.
     
    http://www.magneticsensors.com/vehicle-detection-solutions.php
     
    In our project we used the HMC1021 in our case.  I don't think I'm able to share code, but they were very simple to operate, and the datasheet has nice example circuits.
  6. Like
    NatureTM got a reaction from oPossum in Parking Sensors   
    I did a lot of work on battery-operated parking sensors for a startup recently.  We used magnetic sensors that are sensitive enough to detect distortions in the earth's magnetic field.  The iron an an automobile distorts the field and either increases or decreases the detected field which is read from the sensor by ADC.  They're quite low power to begin with, plus you can switch them on with a transistor for low duty-cycle sampling, leading to very long operation from battery.
     
    http://www.magneticsensors.com/vehicle-detection-solutions.php
     
    In our project we used the HMC1021 in our case.  I don't think I'm able to share code, but they were very simple to operate, and the datasheet has nice example circuits.
  7. Like
    NatureTM got a reaction from hoangnguyen578 in Launchpad TV output   
    I went to a white elephant "The Office" watching party last Thursday. I gave my TVOut demo with the emergency broadcast system screen as my white elephant gift. Anyway, it got me looking at the code again. I realized it would be really easy to increase the resolution now that we have chips with greater than 2K flash.
     
    I pretty much changed two lines of code, created some new images, and uploaded it to an MSP430G2452 with 8K. The resolution is now 192x240. It's still a funky aspect ratio limited by code space and USI frequency, but I expect to get it to do 384x240 when I get my hands on one of the new 16K chips. I also realized I had some really goofy code due to a misunderstanding of pointers. I fixed that, and it no longer needs to compile as C++.
     

    /* Monochrome NTSC composite video output for TI's Launchpad. * * Schmatic: * 470 * P1.2 SYNC --------/\/\/\--------| * |----- RCA jack Signal * 220 | * P1.6 VOUT --------/\/\/\--------| * * Launchpad GND ------------------------ RCA jack GND * * * By: NatureTM * more info on naturetm.com */ #include #define TACCR0 TA0CCR0 #define TACCR1 TA0CCR1 #define TACCTL0 TA0CCTL0 #define TACCTL1 TA0CCTL1 #define TIMERA0_VECTOR TIMER0_A0_VECTOR #define TIMERA1_VECTOR TIMER0_A1_VECTOR // Image file #include "teh_real_nature.h" //#include "gina_close.h" //#include "gina_wide.h" //#include "ebs2.h" #define SYNC BIT2 #define VOUT BIT6 //#define NS_TICK 62.5 // NS = nanoseconds #define TICKS_HSYNC 72 #define TICKS_SHORT_SYNC 38 #define TICKS_HBLANK 163 #define TICKS_SCANLINE 1017 #define TICKS_HALF_SCANLINE 508 #define TICKS_RIGHT_EDGE 973 #define ROW_HEIGHT 1 // in scanlines #define WORDS_SCANLINE 12 unsigned int* currentRowPtr; unsigned int imageOffset; unsigned int scanline = 0; char imageLine; char subRowCounter; unsigned int sleep; int main(){ DCOCTL = CALDCO_16MHZ; BCSCTL1 = CALBC1_16MHZ; WDTCTL = WDTPW + WDTHOLD; // Stop WDT _BIS_SR(GIE); //enable interrupts USICKCTL = USIDIV1 + USISSEL1 + USISSEL0;// + USICKPL; // USI Clock = SMCLK / 4 USICNT = USI16B; // 16 bit shift out USICTL0 |= USIMST; // Use internal clock source USICTL0 &= ~USISWRST; // Clear USI reset bit (enable USI) USICTL0 |= USILSB; // USISR output LSB first USICTL0 |= USIGE; TACTL |= MC_0; // Stop timer A TACTL |= TASSEL1; // TASrc = SMClk = MClk TACTL &= ~TASSEL0; TACTL |= TACLR; // Clear the timer CCR0 = TICKS_SCANLINE; // Scanline timing - CCR0 triggers hSync CCR1 = TICKS_HSYNC; // End of hSync TACTL |= MC_1; // Up mode, Start TACCTL0 |= CCIE; // Enable Timer A CCR0 interrupt TACCTL1 |= OUTMOD_3; // TimerA reset sync pin on CCR0 TACCTL1 |= CCIE; // Enable Timer A CCR1 interrupt P1SEL = SYNC; // Allow timerA to set sync pin low on beginning of scanline P1DIR = SYNC + VOUT + BIT5; } // vsync broad sync pulse section void vSyncTripleBroad(){ int count; for(count = 0; count < 3; count++){ while(TAR < TICKS_HALF_SCANLINE - TICKS_HSYNC){} P1OUT = SYNC; while(TAR < TICKS_HALF_SCANLINE){} P1OUT = 0; while(TAR < TICKS_SCANLINE - TICKS_HSYNC){} P1OUT = SYNC; while(TAR > TICKS_SCANLINE - TICKS_HSYNC){} P1OUT = 0; } } // vsync short sync pulse section void vSyncTripleShort(){ int count; for (count = 0; count < 3; count++){ while(TAR < TICKS_SHORT_SYNC){} P1OUT = SYNC; while(TAR < TICKS_HALF_SCANLINE){} P1OUT = 0; while(TAR < TICKS_HALF_SCANLINE + TICKS_SHORT_SYNC){} P1OUT = SYNC; while(TAR > TICKS_HALF_SCANLINE + TICKS_SHORT_SYNC){} P1OUT = 0; } } // This ISR is run at the beginning of every scanline and keeps track of which // scanline we are on. At the beginning of a frame, it handles vSync output in // software. Next, it hands over control of the sync pin to TimerA, allowing // hsync timing to be done in hardware. #pragma vector=TIMERA0_VECTOR __interrupt void Timer_A0 (void){ // software vSync if(scanline == 0){ P1OUT = 0; P1SEL = 0; // let software control P1.5 instead of TACCR0 vSyncTripleShort(); vSyncTripleBroad(); vSyncTripleShort(); scanline = 8; // vSync takes several scanlines P1SEL = SYNC; // let TimerA handle hSync } // visible area starts/vBlank over else if(scanline == 21){ scanline++; TACCTL1 |= CCIE; // Start drawing picture subRowCounter = 0; imageOffset = 0; } else if(scanline < 263){ scanline++; } // bottom of screen, get ready for vSync else{ TACCTL1 &= ~CCIE; // Stop drawing picture scanline = 0; } } // After vSync, this ISR begins USI output of the image. #pragma vector=TIMERA1_VECTOR __interrupt void Timer_A1 (void){ int wordCounter = 0; TAIV = 0; // Clear TimerA's interrupt vector register; USICTL0 |= USIOE; // USI output enabled USICTL0 |= USIPE6; // Port 1.6 USI data out while (TAR < TICKS_HBLANK){} do{ USICNT |= 16; USISR = currentRowPtr[wordCounter]; wordCounter++; // software delay allowing full USI shift out sleep = 0; while(sleep < 2) sleep++; _nop(); _nop(); _nop(); _nop(); _nop(); _nop(); _nop(); }while(wordCounter < WORDS_SCANLINE); if(subRowCounter == ROW_HEIGHT){ subRowCounter = 0; imageOffset += WORDS_SCANLINE; } subRowCounter++; currentRowPtr = (unsigned int*)image + imageOffset; while(TAR < TICKS_RIGHT_EDGE){} // Wait for edge of screen USICTL0 &= ~(USIPE6 + USIOE); // Release control of video pin to software }
     
    It needs to be flashed in CCS under Debug mode (without optimizations.) Compiling in Release mode with optimizations leads to distorted video. It probably could be fixed for release mode or mspgcc by tweaking the software delay toward the bottom. Changing that "sleep" variable to volatile might just be enough (a thought I had just now, not tested.)
     
    Files, demo images, and information on how to create your own images available on my bloggity-bloggy-blog. I find the word blog amusing.
    http://naturetm.com/?p=147
  8. Like
    NatureTM got a reaction from hoangnguyen578 in Launchpad TV output   
    Here's the writeup:
    http://naturetm.com/?p=47
     
    I've attached two versions of the code, one for use with an external 16MHz oscillator, and one for if you have your 16MHz DCO constant calibrated.
    Launchpad_TV_Out.rar
    Launchpad_TV_Out_for_calibrated.rar
  9. Like
    NatureTM got a reaction from hoangnguyen578 in Launchpad TV output   
    I got my Launchpad to output B/W composite video. It only displays one image without re-flashing though, so it's not extremely useful. The only external components are two resistors and a crystal oscillator. I plan on writing it up on my blog and releasing the source sometime this week, unless I get too busy with school. I wasn't sure if I'd be able to ever get this working, so I'm feelin' pretty good right now!
    Proof ;-)

     
    Code for use with an external oscillator:
    Launchpad_TV_Out.rar
     
    Code for if you have your DCO configuration constants calibrated:
    Launchpad_TV_Out_for_calibrated.rar
     
    EDIT: Here's the writeup!
    http://naturetm.com/?p=47
  10. Like
    NatureTM reacted to oPossum in Efficient muldiv() and scale() functions for 2000 series   
    One of the limitations of the C language is that multiplication truncates the result to match the size of the multiplicands. So if you multiply two 16 bit numbers, the result is limited to 16 bits despite 32 bits being needed to represent all possible results. The fix for this is to cast one of the multiplicands to a larger type. Unfortunately this results in the use of a multiply library routine that is larger and slower than necessary. Division has a similar problem - the quotient, dividend, and divisor are all the same size.
     
    The way I fix these limitations is to write assembly code that is compact, efficient, and well suited to specific tasks. The muldiv() function provided here will take 16 bit unsigned values and do a multiply to a 32 bit intermediate result and then divide back down to 16 bits. It makes some simple tests on the arguments to optimize the code path.
     
    One specific case where this muldiv() function is useful is scaling one range of values to another. For example taking the 0 to 1023 range of a 10 bit ADC and scaling to some actual unit of measure.
     
    A helper function function can be used to allow clearly written code...
     
    uint16_t muldiv_u16(uint16_t a, uint16_t b, uint16_t c); inline uint16_t scale_u16(uint16_t in, uint16_t in_low, uint16_t in_high, uint16_t out_low, uint16_t out_high) { // For guaranteed correct results: // in_low <= in <= in_high // in_low < in_high // out_low < out_high // return out_low + muldiv_u16(in - in_low, out_high - out_low + 1, in_high - in_low + 1); } Sample usage...
     
    // Read 10 bit ADC value - range of 0 to 1023 const uint16_t adc = readADC(); // ADC reference votlage is 2.5V, so convert to a range of 0 to 2500 mV const uint16_t mv = scale_u16(adc, 0, 1023, 0, 2500);    
    Note that there are constraints on the values passed to the function. All are unsinged 16 bit integers, and must fall withing the constraints to ensure a correct return value.
     
    Signed integers can also be used because the constraints will result in unsigned values being passed to the muldiv() function...
    inline int16_t scale_i16(int16_t in, int16_t in_low, int16_t in_high, int16_t out_low, int16_t out_high) { // For guaranteed correct results: // in_low <= in <= in_high // in_low < in_high // out_low < out_high // return out_low + muldiv_u16(in - in_low, out_high - out_low + 1, in_high - in_low + 1); }    
    The in_low <= in constraint can easily be removed if needed...
     
    int16_t scale_i16x(int16_t in, int16_t in_low, int16_t in_high, int16_t out_low, int16_t out_high) { // For guaranteed correct results: // in_low < in_high // out_low < out_high // -32768 <= return <= 32767 // return (in < in_low) ? out_low - muldiv_u16(in_low - in, out_high - out_low + 1, in_high - in_low + 1) : out_low + muldiv_u16(in - in_low, out_high - out_low + 1, in_high - in_low + 1); }  
     
    The other constraints could be reduced or eliminated using C++ templates. (exercise for the user)
    ; ;    Copyright (C) 2013  Kevin Timmerman ; ;   This program is free software: you can redistribute it and/or modify ;   it under the terms of the GNU General Public License as published by ;   the Free Software Foundation, either version 3 of the License, or ;   (at your option) any later version. ; ;   This program is distributed in the hope that it will be useful, ;   but WITHOUT ANY WARRANTY; without even the implied warranty of ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the ;   GNU General Public License for more details. ; ;   You should have received a copy of the GNU General Public License ;   along with this program.  If not, see <http://www.gnu.org/licenses/>. ;     .cdecls C, LIST, "msp430.h"                 ;                                                 ;     .text                                       ;                                                 ;                                                 ; return a * b / c     .def    muldiv_u16                          ; uint16_t muldiv_u16(uint16_t a, uint16_t b, uint16_t c)     .def    muldiv_u8                           ; uint8_t muldiv_u8(uint8_t a, uint8_t b, uint8_t c)                                                 ;                                                 ; muldiv_u16:                                     ;                                                                                               ; --- Multiply ---     push    R10                                 ; Accumulator MSW     push    R11                                 ; Accumulator LSW                                                                                               ; R12 a                                                 ; R13 b                                                 ; R14 c                                                 ; R15 b shift register LSW                                                 ;     clr     R11                                 ; Clear accumulator LSW     mov     R12, R10                            ; Copy a to accumulator MSW                                                 ;  (assume multiply by zero)     tst     R13                                 ; Multiply by zero?     jeq     div16                               ; Yes, skip multiply...     cmp     R12, R13                            ; Make R12 smaller than R13 to     jhs     no_swap16                           ;  do multiply as quickly as possible     xor     R12, R13                            ;     xor     R13, R12                            ;     xor     R12, R13                            ; no_swap16:                                      ;                                                   clr     R10                                 ; Clear accumulator MSW     clr     R15                                 ; Clear MSW of b shift                                                  clrc                                        ;                                                   jmp     mul16_begin                         ; mul16_add:                                      ;     add     R13, R11                            ; Add b to accumulator     addc    R15, R10                            ; mul16_shift:                                    ;     rla     R13                                 ; Shift b     rlc     R15                                 ; mul16_begin:                                    ;     rrc     R12                                 ; Shift a, test lsb     jc      mul16_add                           ; lsb is 1, add b...     jne     mul16_shift                         ; lsb is 0, but more 1 bits remain...                                                 ;                                                 ; --- Divide ---                                                 ; R13/R10/R11 Dividend / Quotient shift register                                                 ; R14 Divisor                                                 ; R15 Bit count div16:                                          ;     mov     R10, R12                            ; Copy MSW of accumulator to result                                                 ;   (assume divide by zero)     tst     R14                                 ; Divide by zero?     jz      div16_exit                          ; Yes, all done...     mov     #1, R15                             ; 16 bit quotient div16_shift:                                    ; R10 <- R11     rla     R11                                 ; Shift     rlc     R10                                 ;                                                 ; Greater than or equal to divisor?     jc      div16_sub                           ; Yes...     cmp     R14, R10                            ;     jlo     div16_next                          ; No... div16_sub:                                      ;       sub     R14, R10                            ; Yes, subtract     bis     #1, R11                             ; Set bit in result div16_next:                                     ;     rla     R15                                 ; Dec bit count     jne     div16_shift                         ; Next bit...     mov     R11, R12                            ; Return result in R12 div16_exit:                                     ;     pop     R11                                 ;     pop     R10                                 ;     ret                                         ;
  11. Like
    NatureTM reacted to simpleavr in webbotlib speech synthesizer on launchpad   
    not sure this is considered a project. the package i am providing allows one to play w/ TTS on a launchpad w/ g2553 chip.
    i did not create the TTS s/w, credit belongs to webbot at http://webbot.org.uk
    instead of publishing derived code, i am publishing a perl script to convert his code for msp430 use.
    this allows me to avoid licensing issues and i think anyone want to implement this should look at the original source.
     
    the speech system is not high quality but fun to use in projects. i.e. talking clocks, alert / alarm modules, robot voices, etc.
     
     
    . s/w only speech synthesizer technique from webbot
    . one pin pwm.
    . text-to-speech and synthesizer code under 10k.
    . fits in a g2553 mcu.
     
    source
     
    https://github.com/simpleavr/webbot_talk430
    . hosted in github, search for "webbot_talk430" project
    . there are 4 files.
    . zconv.pl converts the webbotlib speech source codes to a msp430 compatible source.
    . talk.c TTS exercising firmware, talks to PC at 9600bps turns what u type into speech.
    . uart.h uart code
    . common.h some macros i used.
    . pwm hardware hook-up is in talk.c, just one resistor and one capacitor, can be improved by using better filters.
     


     
    instructions (as in README and help from conversion script)
     
    this is a perl script to convert the webbotlib speech sythesizer code from avr
    to msp430 usage. this script is use at your own risk giftware. webbotlib has
    it's own license terms.
     
    . locate "webbotlib" and download version 1.x, i tried w/ webbotavrclib-1.35
    . inside the package locate and extract the Audio/Text2Speech directory
    . u will see the following files
     
    Text2Speech.h
    phoneme2sound.c
    phonemeWriter.c
    sound2noise.c
    speech2phoneme.c
    speechWriter.c
    vocab_en.c
     
    . place these files in a same directory as this (zconv.pl) script
    . run it again and this script will
    . extract what's needed, ignore others
    . combine into a tighter package name "../webbot_speech.h"
    . replace avr controls w/ msp430 controls
    . to use it in your firmware, do
    . include "webbot_speech.h"
    . say("Hello World");
    . enjoy and thanks webbot
     
    <<< IMPORTANT >>>
    please observe the fact that webbotlib is GPL licensed and use / share accordingly
     
     
     
    will be glad to answer questions anyone may have in this thread. happy hacking.
  12. Like
    NatureTM got a reaction from artifus in Music with only a speaker, a LP, and 30 lines of code   
    Hi everyone, it's been awhile.
     
    I just wanted to share something interesting lasershark mentioned on my blog awhile ago.  It's basically a way to make music with one line of code.  It took about 15 minutes to implement on a g2553 and only requires a launchpad and a speaker.  I found it pretty entertaining.  Just connect one terminal of the speaker to Port 1.2 and the other to ground, or drive it with a transistor.
     
    Original project @ http://canonical.org/~kragen/bytebeat/
     
    #include "msp430g2553.h" #define MCLK 8000000 #define OUTPUT_SAMPLES_PER_SECOND 8000 #define PIN_SPEAKER BIT2 unsigned long t = 0; unsigned char sample; void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; P1SEL |= PIN_SPEAKER; P1DIR |= PIN_SPEAKER; TA0CTL = TASSEL_2 | MC_1; TA0CCR0 = (0x0001 << 8) - 1; TA0CCTL1 |= OUTMOD_7; TA1CTL = TASSEL_2 | MC_1; TA1CCR0 = MCLK / OUTPUT_SAMPLES_PER_SECOND - 1; TA1CCTL0 |= CCIE; _enable_interrupts(); while(1){ sample = ((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7; // sample = t*(((t>>12)|(t>>8))&(63&(t>>4))); // sample = (t*(t>>5|t>>8))>>(t>>16); // sample = t*(((t>>9)|(t>>13))&(25&(t>>6))); // sample = t*(((t>>11)&(t>>8))&(123&(t>>3))); // sample = (t*5&t>>7)|(t*3&t>>10); // sample = (t&t%255)-(t*3&t>>13&t>>6); // sample = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15))); // sample = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; // sample = ((t*(t>>12)&(201*t/100)&(199*t/100))&(t*(t>>14)&(t*301/100)&(t*399/100)))+((t*(t>>16)&(t*202/100)&(t*198/100))-(t*(t>>17)&(t*302/100)&(t*298/100))); // sample = t*(t^t+(t>>15|1)^(t-1280^t)>>10); // sample = t&t>>8; //44 khz // sample = ((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24); // sample = ((t*("36364689"[t>>13&7]&15))/12&128)+(((((t>>12)^(t>>12)-2)%11*t)/4|t>>13)&127); t++; LPM0; } } #pragma vector = TIMER1_A0_VECTOR __interrupt void T1A0_ISR(void) { TA0CCR1 = sample; LPM0_EXIT; }  
    Switch which "sample =" line is uncommented to change the "song."  The songs are from http://pelulamu.net/countercomplex/music_formula_collection.txt and there are more in that file.
     
     
  13. Like
    NatureTM reacted to RobG in RGB Globe   
    Boards are assembled, now on to the base and the controller.

     
    Here's the short assembly video
     

     
     
    And here's the schematic of a single section

  14. Like
    NatureTM got a reaction from pine in Numitron Clock   
    I just wanted to share a clock I made as a Christmas present for my dad.  I just finished it, but luckily he's patient.
     

     
    I have a video, schematic, code, and more details on my blog, www.naturetm.com.
  15. Like
    NatureTM got a reaction from GeekDoc in Numitron Clock   
    I just wanted to share a clock I made as a Christmas present for my dad.  I just finished it, but luckily he's patient.
     

     
    I have a video, schematic, code, and more details on my blog, www.naturetm.com.
  16. Like
    NatureTM got a reaction from PentiumPC in Numitron Clock   
    I just wanted to share a clock I made as a Christmas present for my dad.  I just finished it, but luckily he's patient.
     

     
    I have a video, schematic, code, and more details on my blog, www.naturetm.com.
  17. Like
    NatureTM got a reaction from bluehash in Numitron Clock   
    I just wanted to share a clock I made as a Christmas present for my dad.  I just finished it, but luckily he's patient.
     

     
    I have a video, schematic, code, and more details on my blog, www.naturetm.com.
  18. Like
    NatureTM got a reaction from spirilis in Numitron Clock   
    I just wanted to share a clock I made as a Christmas present for my dad.  I just finished it, but luckily he's patient.
     

     
    I have a video, schematic, code, and more details on my blog, www.naturetm.com.
  19. Like
    NatureTM got a reaction from cubeberg in Numitron Clock   
    I just wanted to share a clock I made as a Christmas present for my dad.  I just finished it, but luckily he's patient.
     

     
    I have a video, schematic, code, and more details on my blog, www.naturetm.com.
  20. Like
    NatureTM got a reaction from larryfraz in Music with only a speaker, a LP, and 30 lines of code   
    Hi everyone, it's been awhile.
     
    I just wanted to share something interesting lasershark mentioned on my blog awhile ago.  It's basically a way to make music with one line of code.  It took about 15 minutes to implement on a g2553 and only requires a launchpad and a speaker.  I found it pretty entertaining.  Just connect one terminal of the speaker to Port 1.2 and the other to ground, or drive it with a transistor.
     
    Original project @ http://canonical.org/~kragen/bytebeat/
     
    #include "msp430g2553.h" #define MCLK 8000000 #define OUTPUT_SAMPLES_PER_SECOND 8000 #define PIN_SPEAKER BIT2 unsigned long t = 0; unsigned char sample; void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; P1SEL |= PIN_SPEAKER; P1DIR |= PIN_SPEAKER; TA0CTL = TASSEL_2 | MC_1; TA0CCR0 = (0x0001 << 8) - 1; TA0CCTL1 |= OUTMOD_7; TA1CTL = TASSEL_2 | MC_1; TA1CCR0 = MCLK / OUTPUT_SAMPLES_PER_SECOND - 1; TA1CCTL0 |= CCIE; _enable_interrupts(); while(1){ sample = ((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7; // sample = t*(((t>>12)|(t>>8))&(63&(t>>4))); // sample = (t*(t>>5|t>>8))>>(t>>16); // sample = t*(((t>>9)|(t>>13))&(25&(t>>6))); // sample = t*(((t>>11)&(t>>8))&(123&(t>>3))); // sample = (t*5&t>>7)|(t*3&t>>10); // sample = (t&t%255)-(t*3&t>>13&t>>6); // sample = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15))); // sample = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; // sample = ((t*(t>>12)&(201*t/100)&(199*t/100))&(t*(t>>14)&(t*301/100)&(t*399/100)))+((t*(t>>16)&(t*202/100)&(t*198/100))-(t*(t>>17)&(t*302/100)&(t*298/100))); // sample = t*(t^t+(t>>15|1)^(t-1280^t)>>10); // sample = t&t>>8; //44 khz // sample = ((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24); // sample = ((t*("36364689"[t>>13&7]&15))/12&128)+(((((t>>12)^(t>>12)-2)%11*t)/4|t>>13)&127); t++; LPM0; } } #pragma vector = TIMER1_A0_VECTOR __interrupt void T1A0_ISR(void) { TA0CCR1 = sample; LPM0_EXIT; }  
    Switch which "sample =" line is uncommented to change the "song."  The songs are from http://pelulamu.net/countercomplex/music_formula_collection.txt and there are more in that file.
     
     
  21. Like
    NatureTM got a reaction from simpleavr in Music with only a speaker, a LP, and 30 lines of code   
    Hi everyone, it's been awhile.
     
    I just wanted to share something interesting lasershark mentioned on my blog awhile ago.  It's basically a way to make music with one line of code.  It took about 15 minutes to implement on a g2553 and only requires a launchpad and a speaker.  I found it pretty entertaining.  Just connect one terminal of the speaker to Port 1.2 and the other to ground, or drive it with a transistor.
     
    Original project @ http://canonical.org/~kragen/bytebeat/
     
    #include "msp430g2553.h" #define MCLK 8000000 #define OUTPUT_SAMPLES_PER_SECOND 8000 #define PIN_SPEAKER BIT2 unsigned long t = 0; unsigned char sample; void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; P1SEL |= PIN_SPEAKER; P1DIR |= PIN_SPEAKER; TA0CTL = TASSEL_2 | MC_1; TA0CCR0 = (0x0001 << 8) - 1; TA0CCTL1 |= OUTMOD_7; TA1CTL = TASSEL_2 | MC_1; TA1CCR0 = MCLK / OUTPUT_SAMPLES_PER_SECOND - 1; TA1CCTL0 |= CCIE; _enable_interrupts(); while(1){ sample = ((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7; // sample = t*(((t>>12)|(t>>8))&(63&(t>>4))); // sample = (t*(t>>5|t>>8))>>(t>>16); // sample = t*(((t>>9)|(t>>13))&(25&(t>>6))); // sample = t*(((t>>11)&(t>>8))&(123&(t>>3))); // sample = (t*5&t>>7)|(t*3&t>>10); // sample = (t&t%255)-(t*3&t>>13&t>>6); // sample = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15))); // sample = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; // sample = ((t*(t>>12)&(201*t/100)&(199*t/100))&(t*(t>>14)&(t*301/100)&(t*399/100)))+((t*(t>>16)&(t*202/100)&(t*198/100))-(t*(t>>17)&(t*302/100)&(t*298/100))); // sample = t*(t^t+(t>>15|1)^(t-1280^t)>>10); // sample = t&t>>8; //44 khz // sample = ((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24); // sample = ((t*("36364689"[t>>13&7]&15))/12&128)+(((((t>>12)^(t>>12)-2)%11*t)/4|t>>13)&127); t++; LPM0; } } #pragma vector = TIMER1_A0_VECTOR __interrupt void T1A0_ISR(void) { TA0CCR1 = sample; LPM0_EXIT; }  
    Switch which "sample =" line is uncommented to change the "song."  The songs are from http://pelulamu.net/countercomplex/music_formula_collection.txt and there are more in that file.
     
     
  22. Like
    NatureTM got a reaction from oPossum in Music with only a speaker, a LP, and 30 lines of code   
    Hi everyone, it's been awhile.
     
    I just wanted to share something interesting lasershark mentioned on my blog awhile ago.  It's basically a way to make music with one line of code.  It took about 15 minutes to implement on a g2553 and only requires a launchpad and a speaker.  I found it pretty entertaining.  Just connect one terminal of the speaker to Port 1.2 and the other to ground, or drive it with a transistor.
     
    Original project @ http://canonical.org/~kragen/bytebeat/
     
    #include "msp430g2553.h" #define MCLK 8000000 #define OUTPUT_SAMPLES_PER_SECOND 8000 #define PIN_SPEAKER BIT2 unsigned long t = 0; unsigned char sample; void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; P1SEL |= PIN_SPEAKER; P1DIR |= PIN_SPEAKER; TA0CTL = TASSEL_2 | MC_1; TA0CCR0 = (0x0001 << 8) - 1; TA0CCTL1 |= OUTMOD_7; TA1CTL = TASSEL_2 | MC_1; TA1CCR0 = MCLK / OUTPUT_SAMPLES_PER_SECOND - 1; TA1CCTL0 |= CCIE; _enable_interrupts(); while(1){ sample = ((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7; // sample = t*(((t>>12)|(t>>8))&(63&(t>>4))); // sample = (t*(t>>5|t>>8))>>(t>>16); // sample = t*(((t>>9)|(t>>13))&(25&(t>>6))); // sample = t*(((t>>11)&(t>>8))&(123&(t>>3))); // sample = (t*5&t>>7)|(t*3&t>>10); // sample = (t&t%255)-(t*3&t>>13&t>>6); // sample = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15))); // sample = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; // sample = ((t*(t>>12)&(201*t/100)&(199*t/100))&(t*(t>>14)&(t*301/100)&(t*399/100)))+((t*(t>>16)&(t*202/100)&(t*198/100))-(t*(t>>17)&(t*302/100)&(t*298/100))); // sample = t*(t^t+(t>>15|1)^(t-1280^t)>>10); // sample = t&t>>8; //44 khz // sample = ((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24); // sample = ((t*("36364689"[t>>13&7]&15))/12&128)+(((((t>>12)^(t>>12)-2)%11*t)/4|t>>13)&127); t++; LPM0; } } #pragma vector = TIMER1_A0_VECTOR __interrupt void T1A0_ISR(void) { TA0CCR1 = sample; LPM0_EXIT; }  
    Switch which "sample =" line is uncommented to change the "song."  The songs are from http://pelulamu.net/countercomplex/music_formula_collection.txt and there are more in that file.
     
     
  23. Like
    NatureTM got a reaction from abecedarian in Music with only a speaker, a LP, and 30 lines of code   
    Hi everyone, it's been awhile.
     
    I just wanted to share something interesting lasershark mentioned on my blog awhile ago.  It's basically a way to make music with one line of code.  It took about 15 minutes to implement on a g2553 and only requires a launchpad and a speaker.  I found it pretty entertaining.  Just connect one terminal of the speaker to Port 1.2 and the other to ground, or drive it with a transistor.
     
    Original project @ http://canonical.org/~kragen/bytebeat/
     
    #include "msp430g2553.h" #define MCLK 8000000 #define OUTPUT_SAMPLES_PER_SECOND 8000 #define PIN_SPEAKER BIT2 unsigned long t = 0; unsigned char sample; void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; P1SEL |= PIN_SPEAKER; P1DIR |= PIN_SPEAKER; TA0CTL = TASSEL_2 | MC_1; TA0CCR0 = (0x0001 << 8) - 1; TA0CCTL1 |= OUTMOD_7; TA1CTL = TASSEL_2 | MC_1; TA1CCR0 = MCLK / OUTPUT_SAMPLES_PER_SECOND - 1; TA1CCTL0 |= CCIE; _enable_interrupts(); while(1){ sample = ((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7; // sample = t*(((t>>12)|(t>>8))&(63&(t>>4))); // sample = (t*(t>>5|t>>8))>>(t>>16); // sample = t*(((t>>9)|(t>>13))&(25&(t>>6))); // sample = t*(((t>>11)&(t>>8))&(123&(t>>3))); // sample = (t*5&t>>7)|(t*3&t>>10); // sample = (t&t%255)-(t*3&t>>13&t>>6); // sample = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15))); // sample = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; // sample = ((t*(t>>12)&(201*t/100)&(199*t/100))&(t*(t>>14)&(t*301/100)&(t*399/100)))+((t*(t>>16)&(t*202/100)&(t*198/100))-(t*(t>>17)&(t*302/100)&(t*298/100))); // sample = t*(t^t+(t>>15|1)^(t-1280^t)>>10); // sample = t&t>>8; //44 khz // sample = ((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24); // sample = ((t*("36364689"[t>>13&7]&15))/12&128)+(((((t>>12)^(t>>12)-2)%11*t)/4|t>>13)&127); t++; LPM0; } } #pragma vector = TIMER1_A0_VECTOR __interrupt void T1A0_ISR(void) { TA0CCR1 = sample; LPM0_EXIT; }  
    Switch which "sample =" line is uncommented to change the "song."  The songs are from http://pelulamu.net/countercomplex/music_formula_collection.txt and there are more in that file.
     
     
  24. Like
    NatureTM got a reaction from bluehash in Music with only a speaker, a LP, and 30 lines of code   
    Hi everyone, it's been awhile.
     
    I just wanted to share something interesting lasershark mentioned on my blog awhile ago.  It's basically a way to make music with one line of code.  It took about 15 minutes to implement on a g2553 and only requires a launchpad and a speaker.  I found it pretty entertaining.  Just connect one terminal of the speaker to Port 1.2 and the other to ground, or drive it with a transistor.
     
    Original project @ http://canonical.org/~kragen/bytebeat/
     
    #include "msp430g2553.h" #define MCLK 8000000 #define OUTPUT_SAMPLES_PER_SECOND 8000 #define PIN_SPEAKER BIT2 unsigned long t = 0; unsigned char sample; void main(void) { WDTCTL = WDTPW + WDTHOLD; DCOCTL = CALDCO_8MHZ; BCSCTL1 = CALBC1_8MHZ; P1SEL |= PIN_SPEAKER; P1DIR |= PIN_SPEAKER; TA0CTL = TASSEL_2 | MC_1; TA0CCR0 = (0x0001 << 8) - 1; TA0CCTL1 |= OUTMOD_7; TA1CTL = TASSEL_2 | MC_1; TA1CCR0 = MCLK / OUTPUT_SAMPLES_PER_SECOND - 1; TA1CCTL0 |= CCIE; _enable_interrupts(); while(1){ sample = ((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7; // sample = t*(((t>>12)|(t>>8))&(63&(t>>4))); // sample = (t*(t>>5|t>>8))>>(t>>16); // sample = t*(((t>>9)|(t>>13))&(25&(t>>6))); // sample = t*(((t>>11)&(t>>8))&(123&(t>>3))); // sample = (t*5&t>>7)|(t*3&t>>10); // sample = (t&t%255)-(t*3&t>>13&t>>6); // sample = t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15))); // sample = (t*9&t>>4|t*5&t>>7|t*3&t/1024)-1; // sample = ((t*(t>>12)&(201*t/100)&(199*t/100))&(t*(t>>14)&(t*301/100)&(t*399/100)))+((t*(t>>16)&(t*202/100)&(t*198/100))-(t*(t>>17)&(t*302/100)&(t*298/100))); // sample = t*(t^t+(t>>15|1)^(t-1280^t)>>10); // sample = t&t>>8; //44 khz // sample = ((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24); // sample = ((t*("36364689"[t>>13&7]&15))/12&128)+(((((t>>12)^(t>>12)-2)%11*t)/4|t>>13)&127); t++; LPM0; } } #pragma vector = TIMER1_A0_VECTOR __interrupt void T1A0_ISR(void) { TA0CCR1 = sample; LPM0_EXIT; }  
    Switch which "sample =" line is uncommented to change the "song."  The songs are from http://pelulamu.net/countercomplex/music_formula_collection.txt and there are more in that file.
     
     
  25. Like
    NatureTM got a reaction from larryfraz in Launchpad Simple MIDI Synth   
    Update:
    This is still a work in progress, but I feel like showing some changes I've made. I wanted to make the synth polyphonic and try using a DAC, while keeping the code small enough to still fit on a G2231.
     
    For the DAC, I chose the MCP4725. I picked this one because it was the cheapest DAC-on-a-breakout from SparkFun. http://www.sparkfun.com/products/8736 I used some TI I2C example code to communicate with it.
     
    Since the I2C communications are using the USI, I needed a different way to get the serial MIDI data from the MIDI controller. I set up TimerA in capture mode, and triggered it on both rising and falling edges in the midi data. From that I could see how long the MIDI line was in a given state and thus how many 0's or 1's I got. (TimerA ticks in state / TimerA ticks per bit = number of bits.) My previous code using the USI was a mess, so I was happy to try something different.
     
    For the synth part, I have a function that takes the frequency and position within the wave, and returns an amplitude. To mix for polyphony, I just average the amplitudes of several of the waves and send the average to the DAC. The synth sounds pretty cruddy. There's several reasons for this, but it mostly boils down to a need for more free CPU time. A faster chip or more efficient code would help. I have ideas for the latter, but I may just take a completely different approach for the synth altogether. I think I'll start concentrating more on the actual sound, and less on seeing what I can squeeze from the chip. There are functions for square, triangle, and sawtooth waves, however all but square are too computationally expensive for polyphony.
     
    Here's the code -- you might want to copy/paste it into a bigger window (I really need to learn how to make a library):

    #include "msp430g2231.h" #include #define MCLK_FREQUENCY 16000000 #define MIDI_FREQUENCY 31250 #define PIN_MIDI_DATA BIT2 #define PIN_SPEAKER BIT4 #define MIDI_CHANNEL 0 // MIDI is 1-indexed so setting this to 0 is midi channel 1 #define MAX_POLYPHONY 7 #define WDT_DIVIDER 64 #define SCALE_SIZE 12 #define number_of_bytes 2 const unsigned long WDT_FREQUENCY = MCLK_FREQUENCY / WDT_DIVIDER; const unsigned int synthNotes[sCALE_SIZE] = {9956, 10548, 11175, 11840, 12544, 13290, 14080, 14917, 7902, 8372, 8869, 9397}; const unsigned int TICKS_PER_BIT = MCLK_FREQUENCY / MIDI_FREQUENCY; unsigned int wdtCounter = 0; char MST_Data[number_of_bytes] = {0x00, 0x00}; char SLV_Addr = 0xC0; char byteCount, Transmit = 0; volatile char I2C_State = 0; char newVelocity; char newNote; int noteIndex = 0; unsigned int wdtPeriodNotes[MAX_POLYPHONY]; unsigned int tNotes[MAX_POLYPHONY] = {0}; char notes[MAX_POLYPHONY] = {0}; char velocities[MAX_POLYPHONY] = {0}; bool midiRXBitState = true; char partialMidiData = 0xFF; bool haveNewMidiByte = false; char newMidiByte; void waitForMidiLoop(); void handleNoteOn(); void handleNoteOff(); void updateWaveTimes(); void synthProcess(); void mixToOutputArray(); void handleNoteEvent(); void shiftLeft(char index); void updateSynth(bool on); char getNoteIndex(char note); void addBits(unsigned int stateCount, bool state); void updateADC(void); void Setup_USI_Master_TX (void); void main(){ DCOCTL = CALDCO_16MHZ; BCSCTL1 = CALBC1_16MHZ; WDTCTL = WDTPW + WDTTMSEL + WDTIS1 + WDTIS0; IE1 |= WDTIE; TACTL |= TASSEL_2; // TACLK = SMCLK P1SEL |= PIN_MIDI_DATA; // Timer A compare input TACCTL1 |= CCIE + CAP + CM_3; // enable capture and interrupt on rising and falling edge TACCTL0 |= CCIE; // enable timeout interrupt TACCR0 = 9 * TICKS_PER_BIT; // should never have more than 8 bits P1OUT |= 0xC0; P1REN |= 0xC0; P1DIR = BIT0 + PIN_SPEAKER + BIT6 + BIT7; I2C_State = 0; // wasn't init'ing properly??? TACTL |= MC_1; // start Timer A _EINT(); // enable interrupts while(1){ waitForMidiLoop(); if(newMidiByte == (0x90 | MIDI_CHANNEL)){ haveNewMidiByte = false; handleNoteOn(); P1OUT ^= BIT0; } else if(newMidiByte == (0x80 | MIDI_CHANNEL)){ haveNewMidiByte = false; handleNoteOff(); } else haveNewMidiByte = false; } } void waitForMidiLoop(){ while(!haveNewMidiByte){ synthProcess(); } } // ################################################## // ################# Synth Stuff #################### void synthProcess(){ if(noteIndex){ updateWaveTimes(); if(haveNewMidiByte) return; mixToOutputArray(); if(haveNewMidiByte) return; // if(!I2C_State){ updateADC(); // } } } // basicly tracks our position within the waveform void updateWaveTimes(){ static unsigned int lastWdt = 0; char iNote; if(lastWdt != wdtCounter){ unsigned int wdtDelta = wdtCounter - lastWdt; lastWdt = wdtCounter; for(iNote = 0; iNote < noteIndex; iNote++){ tNotes[iNote] += wdtDelta; if(tNotes[iNote] >= wdtPeriodNotes[iNote]){ tNotes[iNote] = tNotes[iNote] - wdtPeriodNotes[iNote]; } } } } unsigned int triangle(unsigned int position, unsigned int wavelength){ unsigned int halfWavelength = wavelength / 2; if(position <= halfWavelength) return 0x0FFF * (unsigned long)position / halfWavelength; else return 0x0FFF - (0x0FFF * (unsigned long)(position - halfWavelength) / halfWavelength); } unsigned int sawtooth(unsigned int position, unsigned int wavelength){ return (unsigned long)position * 0x0FFF / wavelength; } // use this one for velocity sensitivity //unsigned int square(unsigned int position, unsigned int wavelength, char velocity){ // if(position > wavelength >> 1) // return 0x001F * velocity; // else // return 0; //} unsigned int square(unsigned int position, unsigned int wavelength){ if(position > wavelength >> 1) return 0x0FFF; else return 0; } // A fast but lo-fi digital mixer. void mixToOutputArray(){ char iSum; unsigned int sum = 0; for(iSum = 0; iSum < noteIndex; iSum++) sum += square(tNotes[iSum], wdtPeriodNotes[iSum]); // using waveforms other than square takes extra cpu time and can lead to unexpected results, // but they're there to mess with. (you may miss midi events and get crudd(y/ier) sound) // sum += triangle(tNotes[iSum], wdtPeriodNotes[iSum]); // sum += sawtooth(tNotes[iSum], wdtPeriodNotes[iSum]); // use this one for velocity sensitivity // sum += square(tNotes[iSum], wdtPeriodNotes[iSum], velocities[iSum]); // Hack to keep the volume more level if(noteIndex == 1) sum /= 2; else sum /= noteIndex; MST_Data[0] = 0x000F & (sum >> 8); MST_Data[1] = 0x00FF & sum; } unsigned int midiNoteToWdtPeriod(char midiNote){ return (WDT_FREQUENCY / (synthNotes[midiNote % 12] >> ((127 - midiNote) / 12))); } #pragma vector=WDT_VECTOR __interrupt void watchdog_timer(void){ wdtCounter++; } // ############################################### // ################# MIDI stuff ################## void handleNoteOn(){ do{ waitForMidiLoop(); if(newMidiByte & 0x80) break; newNote = newMidiByte; haveNewMidiByte = false; waitForMidiLoop(); if(newMidiByte & 0x80) break; newVelocity = newMidiByte; haveNewMidiByte = false; handleNoteEvent(); }while(1); } void handleNoteOff(){ do{ waitForMidiLoop(); if(newMidiByte & 0x80) break; newNote = newMidiByte; haveNewMidiByte = false; newVelocity = 0; handleNoteEvent(); }while(1); } void handleNoteEvent(){ if(newVelocity != 0){ // add a new note when the poly cache is full, replacing the oldest if(MAX_POLYPHONY == noteIndex){ shiftLeft(0); notes[MAX_POLYPHONY - 1] = newNote; velocities[MAX_POLYPHONY - 1] = newVelocity; wdtPeriodNotes[MAX_POLYPHONY - 1] = midiNoteToWdtPeriod(newNote); } // add a new note else{ notes[noteIndex] = newNote; velocities[noteIndex] = newVelocity; wdtPeriodNotes[noteIndex] = midiNoteToWdtPeriod(newNote); noteIndex++; } } else if(getNoteIndex(newNote) < MAX_POLYPHONY){ shiftLeft(getNoteIndex(newNote)); noteIndex -= 2; if(noteIndex >= 0){ noteIndex++; } else{ noteIndex = 0; // set DC offset to zero when no notes MST_Data[0] = 0; MST_Data[1] = 0; updateADC(); } } } // Shift all notes to the right of index left by one, // overwriting index and freeing a spot at the end of // the array void shiftLeft(char index){ int i; for(i = index; i < MAX_POLYPHONY - 1; i++){ notes[i] = notes[i + 1]; velocities[i] = velocities[i + 1]; wdtPeriodNotes[i] = wdtPeriodNotes[i + 1]; } } char getNoteIndex(char note){ int i; for(i = 0; i < MAX_POLYPHONY; i++) if(notes[i] == note) return i; return MAX_POLYPHONY + 1; } // add a new bit to the incomplete midi data variable void shiftInMSB(bool state){ if(state){ partialMidiData >>= 1; partialMidiData |= BIT7; } else{ partialMidiData >>= 1; } } // add the new midi data to the incomplete midi data variable void addBits(unsigned int stateCount, bool state){ while(stateCount > TICKS_PER_BIT / 2){ if(!(partialMidiData & BIT0)){ // are we shifting out the start bit? // if(haveNewMidiByte) // while(1); // catch when we miss midi data (for debugging) shiftInMSB(state); newMidiByte = partialMidiData; // if((newMidiByte != 0xFE) && (newMidiByte != 0xFF)) if(newMidiByte != 0xFE) haveNewMidiByte = true; partialMidiData = 0xFF; } else shiftInMSB(state); stateCount -= TICKS_PER_BIT; } } // CCR1 capture interrupt. Triggers on both edges, adds fresh midi data #pragma vector=TIMERA1_VECTOR __interrupt void CCR1_interrupt(void){ TAR = 0; TACCTL0 &= ~CCIFG; unsigned int tEdge = TACCR1; midiRXBitState = PIN_MIDI_DATA & P1IN; TAIV = 0; addBits(tEdge, !midiRXBitState); } // Like a timeout to add midi data if there's new data, but no edge in too long #pragma vector=TIMERA0_VECTOR __interrupt void CCR0_interrupt(void){ // don't add data if no partial data received, unless the new is all 0's if((partialMidiData != 0xFF) || !midiRXBitState) addBits(9 * TICKS_PER_BIT, midiRXBitState); // in case we captured an edge during this ISR, don't re-add these bits // in the CCR1 ISR TACCR1 = 0; } // ############################################################ // ###################### I2C ADC Stuff ####################### void updateADC(void){ Setup_USI_Master_TX(); USICTL1 |= USIIFG; // Set flag and start communication } void Data_TX (void){ USISRL = MST_Data[byteCount]; // Load data byte USICNT |= 0x08; // Bit counter = 8, start TX I2C_State = 10; // next state: receive data (N)Ack byteCount++; } void Setup_USI_Master_TX (void) { _DINT(); byteCount = 0; Transmit = 1; USICTL0 = USIPE6+USIPE7+USIMST+USISWRST; // Port & USI mode setup USICTL1 = USII2C+USIIE; // Enable I2C mode & USI interrupt USICKCTL = USIDIV_3+USISSEL_2+USICKPL; // USI clk: SCL = SMCLK/128 USICNT |= USIIFGCC; // Disable automatic clear control USICTL0 &= ~USISWRST; // Enable USI USICTL1 &= ~USIIFG; // Clear pending flag _EINT(); } // I2C handler for ADC adapted from TI example code. This runs pretty quick, // so hopefully it doen't clash with Midi input interrupts. #pragma vector = USI_VECTOR __interrupt void USI_TXRX (void) { switch(__even_in_range(I2C_State,14)) { case 0: // Generate Start Condition & send address to slave byteCount = 0; USISRL = 0x00; // Generate Start Condition... USICTL0 |= USIGE+USIOE; USICTL0 &= ~USIGE; USISRL = SLV_Addr; USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address I2C_State = 2; // next state: rcv address (N)Ack break; case 2: // Receive Address Ack/Nack bit USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter=1, receive (N)Ack bit I2C_State = 4; // Go to next state: check (N)Ack break; case 4: // Process Address Ack/Nack & handle data TX USICTL0 |= USIOE; // SDA = output if (USISRL & 0x01) // If Nack received... { // Send stop... USISRL = 0x00; USICNT |= 0x01; // Bit counter=1, SCL high, SDA low I2C_State = 14; // Go to next state: generate Stop } else { // Ack received, TX data to slave... USISRL = MST_Data[byteCount]; // Load data byte USICNT |= 0x08; // Bit counter = 8, start TX I2C_State = 10; // next state: receive data (N)Ack byteCount++; } break; case 10: // Receive Data Ack/Nack bit USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter = 1, receive (N)Ack bit I2C_State = 12; // Go to next state: check (N)Ack break; case 12: // Process Data Ack/Nack & send Stop USICTL0 |= USIOE; if (byteCount == number_of_bytes){// If last byte USISRL = 0x00; I2C_State = 14; // Go to next state: generate Stop USICNT |= 0x01; } // set count=1 to trigger next state else{ Data_TX(); // TX byte } break; case 14:// Generate Stop Condition USISRL = 0x0FF; // USISRL = 1 to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE); // Latch/SDA output disabled I2C_State = 0; // Reset state machine for next xmt break; } USICTL1 &= ~USIIFG; // Clear pending flag }
     


     
    I was planning on entering this in the POTM, but it sounds so bad! My cats start meowing and scratching at the door when I play it! I think I will enter the original version, however.
     

    NOTE: There should be about a 190K resistor between the DAC and the amp in the schematic. Not entirely necessary, but will scale the DAC output down to something more reasonable for the LM386
     

     

     

     

     

×
×
  • Create New...