-
Content Count
24 -
Joined
-
Last visited
Posts posted by davebranton
-
-
I decided to have a crack at building some drum pads, because the keyboard that I'd acquired for the kids to learn piano on had a midi input, and somewhat acceptable drum sounds to boot. After several months of sitting around half-completed under a cupboard, I decided to pull it out over the easter weekend and actually make it work.
In words, here's what I did.
- Six drum pads, made of a square of hardboard with 5mm closed-cell foam glued to the top and standing on 15mm foam pads. Each pad has a piezo electric transducer underneath, attached using hot glue.
- These six piezos are each hooked up to a buffer and biasing and clamping circuit, like below. The op-amp I used was an LMC648, which is great for this application but isn't that cheap.
- These six analog channels are connected to six of an MSP430's analog inputs. This micro samples these six inputs continuously, and uses a simple filter and threshold algorithm to determine when they've been hit.
- Once it detects a hit, it outputs the pad ID (zero to five) and the 'velocity' of the hit over I2C to another MSP430.
- This second micro in turn outputs the MIDI command, through a completely un-necassary additional chip to drive the MIDI output completely to spec. I know it's possible to output midi using 3.3v, but I used a driver chip anyway to run it at 5v - a TC4428 FET driver that I happened to have left over from another project.
- The second micro also translated the pad ID to a MIDI note, and can be reprogrammed via a couple of buttons that are used to change the drum sound that's assigned to the most recently hit pad. The assignments are stored in the MSP430's "information memory" segment, so that it remembers how you set it up when you turn it off. I'd originally planned to put a 16x2 LCD on there, but I ended up using that for something else.
In pictures, here's what it looks like.
Overall:
PCB closeup - I would normally have a full eagle schematic and layout for this, even if I'm building it on stripboard, because I just make fewer mistakes that way. In this case though the layout of the six clamping circuits turned out to be much easier if I stood those components up out of the board (look on the right side of the picture) and wired them up over the top, and you can't really do that so easily in Eagle.
Piezo closeup (under the pads)
And finally, in code, here's the two projects.
The ADC sampling micro : main.c
The MIDI outputting micro : main.c
Probably the most interesting aspect of this for me at least, was that when I was designing the (admittedly still rather crude) algorithm to determine how hard the pad had been hit, I needed to get the ADC samples off the chip so that I could experiment with algorithms offline and tweak their performance. I happen to have one of the rather wonderful saleae USB logic analysers (https://www.saleae.com/logic/), and so if you compile the ADC sampling micro's code with OUTPUT_SAMPLES defined, it will run the I2C at 1Mhz (instead of a much more reasonable 125kHz) and output all the ADC data as it receives it. This is pretty borderline for I2C, but if you keep the track length for the I2C nice and short you should be ok. In my case it's the orange and yellow wires in the PCB photo above - and of course you'll need both micros there because the I2C won't work if nothing's there to ACK.
Anyway, doing this I was able to get the samples back into my PC, and using python and pylot etc, plot handy graphs like this:
Python file, and two text files recorded from the inputs using I2C and the USB logic analyser : Archive.zip
The algorithm just detects when the input rises over some threshold, and while integrates the value before it crosses back over zero again (this is after the ADC samples have had 512 subtracted from them of course, making them signed). This is then considered the 'velocity', and the MIDI micro then divides that down to get a 7-bit velocity value for MIDI. This works 'ok', but very light hits aren't detected, and sometimes hits are quite a bit louder than you'd expect.
It's still fun though.
-
Also, one more pic I forgot, here's everything plugged together showing some text. After reading something about how the text needs to be antialiased to allow for smooth scrolling, I modified the text rendering code to allow the characters to be placed by quarter pixels. This made a huge difference to the readability of text on such a small display, but makes it look a bit weird in photographs.
The idea is to intersperse the timer display with occasional inspirational/subliminal messages
Well, actually probably just stuff to entertain a bit - although I've not really had any good ideas on this. At the moment it just scrolls Robert Frost's 'Fire and Ice', which is a bit over their heads and perhaps a bit apocalyptic for bedtime anyway. I thought perhaps knock-knock jokes would be pretty fun.
-
Having done quite a bit of work in the past with inertial sensors, I can say that it's next to impossible to distinguish one type of motion from another using accelerometers & gyros. Especially with the kind of cheap accelerometers that would fit on the back of a toothbrush. So although it's a pretty nice idea, I think in the end I'll have to trust them. I'll probably be behind them in the bathroom policing the whole thing anyway, and the timer is as much for me as it is for them. AND of course the whole thing is only an excuse to play with electronics and spend some cash of parts and PCB manufacture
But in any case, the components turned up, I soldered them on & it worked first time - so I've attached some photos and a couple of grabs from the scope to show what the power supply looks like. I have another filter before the power gets to the MSP, which I've got on the scope traces separately. More by luck than judgement, this filtered power happened to max out at 3.6v!
So: Populated board front & back:
Scope trace of filtered & unfiltered power:
& The same (except the traces are swapped) after I added a couple of extra thousand microfarads of capacitance to the power supply. This has a different time scale too, but you can see the min & max of the supply voltage at the bottom of the snap.
& Again, just because I only just found out how to get my scope to upload screengrabs to my laptop, here's one of those spikes zoomed in. Presumably, when the power supply load changes as a new set of LEDs are switched on, the regulator reacts with this little short wobbly spike thing that lasts just under a microsecond. Hopefully this won't cause any issues with the serial communications between the shift registers (running off this noisy supply), and the micro (running off comparatively clean power).
-
Fantastic project, and impressive results. I wonder if you would be better off transmitting the co-ordinates in binary rather then text too? That might improve your bandwidth.
I have a few questions if you'll permit me:
1) How much current do the motors draw?
2) How have you implemented the z-axis?
3) What kind of pen have you used? Have you experimented with others? Biro vs. a felt-tip for instance?
-
-
I'm using a python script, and url2lib2:
try: r = urllib2.Request('http://api.xively.com/v2/feeds/100842', json_data, {'Content-Type': 'application/json', 'X-ApiKey':apikey}) r.get_method = lambda: 'PUT' urllib2.urlopen(r) except urllib2.HTTPError, error: contents = error.read() print >> sys.stderr, contents s.write('E') continue except Exception as e: print >> sys.stderr, e s.write('E') continue
where json_data is built up like so:
json_data = '{"version":"1.0.0","datastreams":[{"id":"Humidity", "current_value":"%.1f"},{"id":"InsideTemp","current_value":"%.1f"}]}'%(float(temps[1])/10.0,float(temps[2]) / 10.0)
and apikey is the api key as provided by cosm for write access to your datastream.
Notice the line that set the get method - this is a hack because url2lib2 technically support HTTP PUT. It works though. It's running on a mac mini, if that helps.
r.get_method = lambda: 'PUT'
But I've actually moved away from cosm, although all the data is ending up there. They've been bought by someone else now, and I don't like their graphs so much anymore. Now I'm also logging the data to a local mysql server, and serving the data up to my personal devices using apache. I'm doing the plots using the javascript library flot, which I think makes nicer plots that the ones cosm uses.
I'd hand out the URL here, but I rather think my mac mini won't thank me
-
Two great suggestions, thanks. I have the PCBs on order from seeed as it happens, and I have some other boards in the pipeline so maybe I'll look into that. I couldn't find the ones you mentioned spirilis, but I did find this:
Costs as much as 10 custom PCBs!! We do have a fantastic thrift shop round these parts that I'll hit up first to see what I can find.
Anyway, I wrote the code I mentioned earlier up in assembly, and unrolled the loops, and got the inter-byte time down from 25us to 8us. Then I upped the size of the framebuffer so that it covers all three panels, soldered up the other two boards, and it all works very nicely with no perceptible flicker.
Next step is the control board. This is the one I have on order from seeed, which should be here in a week or so. This is a much more complex board that my first one, as it supports a few different peripherals, and a couple of power supply options. I'll put a picture of it up when I get it, but I mentioned it in this post here:
http://forum.43oh.com/topic/1374-get-your-booster-packs-sponsored-10/?p=36212
For this project, I'll be boosting a couple of AAs to 3.3v, and at this point I've no idea how well the part I've chosen to do this is going to work. I did try this once with a different boost controller, and saw spikes up to 4v as the inductor discharged. I've made provision for a LC filter to help smooth these out, but I'll have to see if it's going to be required.
The idea is that the project is asleep most of the time, waiting for the button to be pressed. While asleep the boost regulator is off, and the micro is powered by the AAs through the inductor and the diode (so that'll be battery voltage minus the drop of the diode, which I think is about 100mv at low currents). When it wakes up, it'll turn on the boost controller and then switch to a higher clock frequency. It remains to be seen what the power supply will look like when the regulator turns on, maybe there'll be horrible spikes that'll kill the micro? Who knows.
-
@@igor , that's not a bad idea, I probably will
Also, I wonder if anyone knows of any nice big smackable buttons that would be suitable for something like this. Kid proof, waterproof, and nice and big and satisfying. The kind you often find on slot machines I guess.
-
What you're attempting may very well be beyond the capabilities of an MSP430. I did a project a while back driving RGB LEDs, and that required multiple FPGAs to actually drive the LEDs fast enough, and used megabit serial (!) to transmit data to the FPGAs. This was generated by an LPC1768 running at a clock speed well out of reach of an MSP430.
Anyway, let's do some calculations:
You have 16x16 display, which means you have 256*3 = 768 bits of data to output if all you were going to do is have each LED either on or off (no PWM).
You are trying to PWM with 255 brightness levels for each LED, which means you must output 768 bits of data 255 times over, and fast enough to not perceive any flickering. I've found 75Hz is about what's required to avoid any perception of flickering, so you'll have to output 768*255*75 bits of data per second. That's about 14 megabits, which means you'd need a clock speed of 30Mhz just to get the data out - leaving no time over to calculate anything or load any data into registers or anything.
Also, if you want to get this data into the MSP over serial, I presume you'll want to store the image data in RAM on the MSP. Your 16x16x3 bytes of memory is 768 bytes, which is more RAM than is available in the MSP.
So my advice is to drop the number of brightness levels you need to (say) four, this is two bits per channel per pixel. Forget about trying to calculate HSV to RGB transformations on the MSP430. Work on just getting the LEDs on or off to start with, to make sure you can get everything running fast enough to get that going. And then try four brightness levels.
My feeling is though that to get enough speed for this project you will either need to get into assembly or use a more powerful micro. The LPC1768 is a nice one, but anything with a bit more grunt is probably what you'll need. Your shift registers go up to 100Mhz input, why not use a micro that can give you that kind of speed?
-
Thanks abecedarian, I'll do that next time.
Ok, so I took a look at the assembly version of the loop I mentioned above, and I'm not all that impressed with the compiler's output. I'm probably expecting too much, because I've seen it do pretty clever stuff in the past.
output = 0; for(bit = 8; bit ; bit--) { output <<= 1; output |= (framebuffer[row][bit] > pwm) ? 0 : 1; // on if over pwm }
And here's the assembly, annotated to help with my own understanding of it. This is a release build using CCS with optimisations at maximum speed.
MOV.B &line+0,r14 ; [] RLA.W r14 ; [] RLA.W r14 ; [] RLA.W r14 ; [] ADD.W #framebuffer+8,r14 ; [] // r14 = (framebuffer + 8) + (line * 8) MOV.W #0,r15 ; [] |81| MOV.W #8,r12 ; [] // r15 = 0, r12 = 8 // r12 is being used as the loop counter MOV.B #0,r13 ; [] |84| // r13 = 0 CMP.B @r14,&pwm+0 ; [] |84| // set flags as if (*r14) - pwm . // Jump if LO to $C$L2, JLO jumps if carry is not set JLO $C$L2 ; [] |84| // r13 = 1 MOV.B #1,r13 ; [] |84| C2L2: RLA.B r15 ; [] |84| OR.B r13,r15 ; [] |84| // r15 <<= 1 // OR r13 into r15 SUB.W #1,r14 ; [] |82| // move pointer // r14-- SUB.W #1,r12 ; [] |82| // loop counter // r13--, loop if nonzero JNE $C$L1 ; [] |82|
So looking at this code reveals that it uses an unnecessary register (r13), which it loads with either zero or one, depending on the value of the carry flag. If carry is not set, it leaves r13 as zero, otherwise it loads it with one. It then OR's r13 into r15, having shifted r15 one bit to the left.
Sound like the instruction RLC.B to you? Sure does to me. All the code between the MOV.W #8,r12 and the SUB.W #1,r14 can be replaced with the following two instructions.
CMP.B @r14,&pwm+0 RLC.B r15
And moreover, since pwm doesn't change inside the loop, it can kept in a register during the loop thus saving an additional memory read during the instruction. I suspect that this will make the whole operation at least twice as fast - although I haven't tried it out yet.
If that's still not enough, I think I might unroll the whole loop - since each iteration will only be three instructions long there will be plenty of program space.
All of the above leaves me with a single question - how do I get my assembly into the code? I've written a single assembly function once, and I placed it in a separate file. I can do that again, but I'll need that file to have access to global variables declared in a C file. If anyone here is able to offer advice on how to achieve that I would be grateful.
Actually I have another question too, in case anyone is still reading. What is the interrupt priority on the MSP430? I presume it doesn't nest interrupts, and so most of the time the priority is whichever happens first, but if two did happen to come along at once, which would be serviced first?
-
Earlier this year I happened to be in Singapore in transit, and took the opportunity to visit Sim Lim Tower - a high-rise mall full of electronic components shops! Amazing place, and amongst other things (including super-cheap kits for the kids to build!) I picked up three 5x7 LED matrix panels for a dollar each (!!). They didn't have any part numbers that I could see, but after experimentation they turned out to be row-anode devices and once I figured out the pinout I decided to build a project out of them.
As any of us who have children know, it's a daily battle to get kids to brush their teeth for long enough, and as any of us who have children also know, time spent on projects goes down better if there's a kid angle to it.
So I decided to make a tooth brush timer, that will display a countdown from two minutes - interspersed with inspirational text and probably amusing patterns and other things to keep the kids entertained during their twice-daily two-minute ordeal. Poor things.
Being given to doing things the hard way, I decided to design two boards - one for the LED panels, and one for the controller. I'll get to the controller board in a later post because it's quite complicated (since the board fab place makes 10 identical boards I decided to try to make it as multi-purpose as possible). The LED boards though are simpler, they just use a 4094 shift register to handle the columns (or rows, I decided to use my three matrices abutted end to end rather than side to side, giving me a display that's five LEDs high and twenty-one across. I found a nice 5x5 bitmap font on dafont.com, and decided that I had enough space for what I needed to display.
Because if all the LEDs are on the 4094 will be over it's specified current limit, it drives the columns through seven PNP transistors. This meant quite a bit of routing, which is what led me to use a board fab house rather than attempt it all on veroboard.
So, onto the code (I have attached the eagle files if anyone is interested). I wanted to use the USI module to drive the 4094's. Looking at the datasheet for a 4094, it clocks in it's data on a rising edge of the clock. So the USI module will need to be configured thusly (I'm running the CPU at 12Mhz).
// Reset USI logic to allow configuration USICTL0 |= USISWRST; // Setup serial output for a 4094 shift register USICTL0 = USIPE6 // SDO enabled + USIPE5 // CLK enabled + USIMST // master mode + USIOE // output enable + USISWRST // leave in reset ; // clock phase = 1, interrupts enabled SICTL1 = USICKPH // phase = 1 + USIIE // interrupt enabled ; USICKCTL = USIDIV_4 // divide by 16, giving 500khz clock + USISSEL_2 // SMCLCK as clock source ; // release SPI for operation USICTL0 &= ~USISWRST;
Now, the tricky part of this project is that I want to have greyscale (redscale!) on my LEDs. To do this I will have to not only row-scan the matrix, but also PWM each LED individually. And I want to do all this in the USI interrupt handler, and have a framebuffer in RAM that I can just write to at any time without having to worry about getting the LEDs to update. So first thing is the framebuffer, I'm just using one LED matrix at the moment so it's only a 5x7 array:
unsigned char framebuffer[5][8]; // greyscale framebuffer
So, for those unfamiliar with how to drive these types of LED matrices, here's a bullet-pointed flow-chart.
- Start at the first row.
- Clock bits into the 4094 shift register, corresponding to which LEDs in the current row are on. Because I'm using PNP transistors as high-side switches, a zero in the 4094 outputs will correspond to an LED being on.
- Once the bits are clocked in, set the 'strobe' input into the 4094 to high. This will latch the outputs of the 4094, causing them all to change at once to whatever you just clocked in.
- Now that the high-side transistors are all either on or off, according to which LED in the current row you want to be on, it's time to turn that row on using the low-side switch for that row. Of course there could be quite a bit of current flowing out through the row pin if all the LEDs in that row are on, so it's not safe to just hook it up to a GPIO pin. A FET is instead used as a low-side switch.
- Now the row is lit up. Move to the next row and goto 2. If we're at the last row, then instead goto 1 (which means start over).
The above method, if run fast enough, will cause all the LEDs to appear illuminated in whatever pattern you desire. But it won't allow brightness control of individual LEDs. To get that we need another variable, that I'll call PWM.
PWM will start at zero, and will be incremented at step 1 of the above bullet-pointed flowchart. When it reaches 16, it will be reset back to zero. So it's a rolling counter that will count up each time a frame is output to the matrix, and will roll over at 16. Using this, and the greyscale framebuffer above, we can modify the flowchart so that instead of just clocking in whichever LEDs are on into the 4094, we clock in whichever LEDs have a brightness value that is greater than the current PWM counter.
Thus, when the framebuffer entry is zero for a particular LED, that LED is never illuminated because it's brightness value (zero) is never greater than the PWM value (zero to fifteen). And when the framebuffer entry is sixteen, it's always greater than the PWM counter, and thus that LED is illuminated every frame. In between values illumiate the LED for varying numbers of frames, which - if you run the whole process fast enough - will cause them to appear dimmer.
The heart of this code is the following loop, which I would love some advice on how to optimise:
output = 0; for(bit = 8; bit ; bit--) { output <<= 1; output |= (framebuffer[row][bit] > pwm) ? 0 : 1; // on if over pwm }
This loop calculates what to output to the 4094 shift register for each row. It's quite alot of work for an interrupt handler, and it needs to be three times more work once I add the additional two LED panels. I'm sure the compiler in CCS does a good job of figuring out the loop and optimising it for me, but I should take a look at the assembly output and determine if I can do any better.
Anyway, to kickstart the process, the main program calls NextRow(), and then sets up a timer and fiddles with the framebuffer at intervals. So, here's NextRow and the USI interrupt handler:
unsigned char pwm = 0; unsigned char row = 0; void NextRow() { unsigned char bit; unsigned char output; // strobe low ready for next thingy P1OUT &= ~BIT3; // next row's data - work out if each pixel should be on output = 0; for(bit = 8; bit ; bit--) { output <<= 1; output |= (framebuffer[row][bit] > pwm) ? 0 : 1; // on if over pwm } USISRL = output; // 8 bits out, we'll come back to _usi once these bits have been clocked out. USICNT = 8; } #pragma vector = USI_VECTOR interrupt void _usi() { USICTL1 &= ~USIIFG; // reset interrupt flag // turn off rows P2OUT = 0; // latch data P1OUT |= BIT3; // Strobe high // turn on rows P2OUT = (1 << row); // next row row++; if (row == 5) { pwm++; pwm &= 0x0F; // loop pwm back to zero when it hits sixteen row= 0; // start at row zero again. } NextRow(); }
I've left alot of stuff out here, setting up the clock module and the GPIO pins etc etc. Also there's code in the project that makes a pretty (ish) pattern, and the displays some numbers. The code and eagles files are attached, and assuming I can use the video feature of BBcode correctly, here's a video!
http://www.youtube.com/watch?v=gvPZ8LyGYGY
--Edit---
Well that youtube embed didn't work. Here's a link. How do you embed videos anyway?
-
Well it looks like the culprit was Grace - which I think loaded the USICNT register with 8, causing the SPI clock to start. So by the time I got to my code the clock had already been running for a bit, and so writing 8 to the register caused me to see about 12 bits on the output.
Once I threw away the grace project and started again doing everything by hand it all worked fine. I'm going to start another thread about this project because it involves a couple of custom boards and I don't want to have my stupid question at the top of the thread
-
So I've finally finished this, and I thoght I might share the code I used to talk to the indoor temperaure and humidity sensor - a DHT-22. These sensor output their data using a 1-wire protocol that's completely different to the 1-wire protocol used by the dallas sensors. Sigh. I somethimes think that although hardware designers talk alot about compatibility, they hate it really because it cramps their style.
Anyway, here's what I did. I've attached all the code to this post, but here's the relevant parts. First, setting up the one pin for the sensor, which I'd connected up to P2.0, and setting the state machine variables used by the GPIO interrupt.
unsigned char State, Bit;
unsigned int Humidity;unsigned int Temperature;unsigned char Checksum;// DHT-22 is P2.0 P2DIR &= ~BIT0; // P2.0 is an input P2OUT = 0 ;
// State machine variables
State = 0; Bit = 0;And then setting up a microsecond timer that I use to both count the intervals between the interrupts, and trigger a timeout if the device doesn't respond in a reasonable period of time (this causes a retry, and happens every so often - probably because my connection to the sensor is over several meters of wire).
// Counter for DTH22 interrupts TA1CTL = TASSEL_2 + MC_2 + ID_3; // SMCLCK, cont. mode. counts up at 1Mhz. // TA1R counts up at 1us intervals TA1R = 0;
I have SMCLCK at 8Mhz, and the ID_3 divides by 8, gving a 1Mhz timer which means it'll increment every microsecond.
I have two #defines to make it clear what I'm doing on the DHT-22's pin. Since the output is set to zero, changing the pin from an input to an output will have the effect of either pulling it down, or allowing it to be pulled up by the external pullup resistor.
#define DHT22_LOW() P2DIR |= BIT0 #define DHT22_FLOAT() P2DIR &= ~BIT0
So, in order to trigger the sensor one has to pull it's line low for at least 500 microseconds. Using the timer then I;
// drive signal low DHT22_LOW(); // Wait 1ms (must be >500us) TA1R = 0; while(TA1R < 1000);
Obviously that while loop causes the compiler to get upset that I'm not entering a low-power mode - but this is the indoor unit hooked up to power over USB so I'm not worried about power usage. Anyway, after that 1ms has elapsed, I set up the GPIO interrupts and then release the data line.
// set up interrupts, we are looking for a falling edge initially, which we'll // see when the sensor announces itself P2IES |= BIT0; // falling edge P2IFG = 0; P2IE |= BIT0; // P2 IE for bit zero DHT22_FLOAT(); // release line.
Also I change the timer mode to continuous mode, set the timeout to 1000, and turn on timer interrupts. Each time I see a transition from the GPIO interrupt, I set the timer counter back to zero. So if I don't see the transition within 1ms the timer interrupt will go off and I enter an error condition. In the actual code, this causes a retry after a couple of seconds.
//now change timer mode TA1CTL = TASSEL_2 + MC_1 + ID_3; // SMCLCK, cont. mode. counts up at 1Mhz. (ID_3 = clock / 8) TA1R = 0; TA1CCR0 = 1000; // 1 millisecond. If we don't see a transition in this time, then we're going to ping // the sensor again TA1CCTL0 |= CCIE; // 'rupts on again
And now the interrupt handler. This isn't pretty, and could be refactored to be much smaller, but it doesn't really matter. The handler first grabs the value of TA1R, which will be the number of microseconds since the last interrupt - and then resets TA1R. Then depending on whether or not P2.0 is high or low, I change P2IES to switch the interrupt handling from rising to falling edges. Then my 'state machine' kicks in, which is hardly worthy of the name, since all we're doing is collecting up all the bits output by the sensor and putting them into three variables. The first 16 go into the humidity variable, the second 16 into the temperature variable, and the last 8 into the checksum.
interrupt(PORT2_VECTOR) Port2_Vector() { unsigned int t = TA1R; // capture counter unsigned char signal = (P2IN & BIT0); TA1R = 0; // and reset P2IFG = 0; if (signal) // what's P2.0 doing { P2IES |= BIT0; // falling edge } else { P2IES &= ~BIT0; // rising edge } switch(State) { case 0: // We should see a falling and then a rising edge when the sensor pulls the line low // to signal it's present, and again to signal the start of a bit // We want to see both of these before we transition to reading data if (!signal) { Bit++; if (Bit == 2) // seen two lows. One was the 'presence pulse' // and the second indicated the start of a bit. { Bit = 0; State = 1; } } break; case 1: // Reading humidity. We'll get two interrupts for each bit, when the // line has gone high the bit is complete if (!signal) { // line is low. t indicates how long it was high for //all_t[Bit] = t; if (t > 50) { // was a '1' Humidity |= 1; } Bit++; if (Bit == 16) { // Onto temp State = 2; Bit = 0; } else { Humidity <<= 1; } } break; case 2: // Reading temp. We'll get two interrupts for each bit, when the // line has gone high the bit is complete if (!signal) { if (t > 50) { // was a '1' Temperature |= 1; } Bit++; if (Bit == 16) { // Onto checksum State = 3; Bit = 0; } else { Temperature <<= 1; } } break; case 3: // Reading checksum. We'll get two interrupts for each bit, when the // line has gone high the bit is complete if (!signal) { if (t > 50) { // was a '1' Checksum |= 1; } Bit++; if (Bit == 8) { // Onto done P2IE = 0; State = 4; Bit = 0; // also turn off timer interrupt, the main loop will output this measurement now TA1CCTL0 &= ~CCIE; } else { Checksum <<= 1; } } } }
Once this is done I turn off the GPIO interrupt (P2IE = 0), and set the state to 4. The main loop then picks up this & outputs the values over serial if the checksums match. The checksum code is.
unsigned char CalcChecksum() { unsigned char c = 0; c += (unsigned char)(Humidity & 0xFF); c += (unsigned char)(Humidity >> 8); c += (unsigned char)(Temperature & 0xFF); c += (unsigned char)(Temperature >> 8); return c; }
And that's it. Well except that it isn't of course, but I've attached all the code in case anyone would like to see it.
-
I have a schematic and board layout for an MSP that supports the following features - it's not a boosterpack though it's a standalone board that breaks out the debug pins so you can connect it to a launchpad for programming. I personally find this a more cost-effective approach because the chips by themselves are a couple of bucks, and the final product is more flexible. This board will be used in several projects. An LED matrix, A robot with ultrasonic sensors, and some audio project or other. Perhaps a digital guitar effects pedal or something.
Two power supply options:
Step up power supply using NCP1450 - can be enabled/disabled by the micro. In the robot project it won't power the micro, but will step up to 5v for the ultrasonic sensors.
Linear regulator.
Two DRV883 H-bridge motor driver footprints.
One MCP4911 DAC for audio output.
And six of the GPIO outputs can be connected up to FETs to control other types of output. I'm using these for the rows of an LED matrix, but they would be useful for anything.
Footprints for either a surface mount or a thru-hole crystal.
Optional LC power supply filtering for the output of the boost regulator (the inductor can just be bridged, which is what makes it optional
)
Eagle files attached.
-
Hi,
I'm building a simple LED matrix display, using 4094's to drive the columns of the matrices, and I would like to use the universal serial module to clock the data into the shift registers.
So I set up a simple test, and had the serial module clock out 7 bits (I have three 7x5 matrix panels, and at first I'm just using one), but I didn't see the results I expected. When I got a scope on the outputs, I saw the serial module clocking out 11 bits! I had to set the bit counter register USICNT to 4 to convince the serial module to output 7 bits. The code looks something like
//set strobe high P1OUT |= BIT3; // load the data (this was a single bit moving through the byte) USISRL = <data>; // clock out 7 bits (!!) USICNT = 4; // wait until they're out while(USICNT); // set strobe low, this will latch the outputs on the shift register P1OUT &= ~BIT3;
I know that USICNT reaches zero at the point I expect, because I can see the strobe go low when the final bit is clocked out. But what is going on here? Does anyone know why seven bits get clocked out when I set USICNT to four?
-
Well I ran the same code through CCS, and it's a damn clever compiler. It even figured out that my sequence of shifts and adds constituted a multiply and jumped off into some subroutine apparently called __mspabi_mpyi. Impressive - assuming that this would be faster than the shifts and adds of course, but I'll give it the benefit of the doubt.
So I'll be sticking with CCS, even though I'll have to run it in a VM it's going to save me a lot of time. Thanks very much for your advice.
-
Thanks for the hint about the multiplier, I decomposed the multiplies into various shifts and adds, and the c version of the code works great!
...So I thought to myself, even though I don't actually need to, I'll have a crack at using the inline assembly to get the same results and free up some cycles.
...But... inline assembly is a strange beast in msp430-gcc land. I honestly can't make head nor tail of the documentation, and very strange things started to happen. But in any case, I did have a question about this code:
mov &fade_shifts_counter, r15 add #llo(-1), r15 mov r15, &fade_shifts_counter mov &fade_shifts_counter, r15 cmp #0, r15 jne .L5 mov.b &fade_shifts, r15 add.b #1, r15 mov.b r15, &fade_shifts mov #2000, &fade_shifts_counter .L5:
This is what msp430-gcc produces in response to the following c code
fade_shifts_counter--; if (fade_shifts_counter == 0) { fade_shifts++; fade_shifts_counter = FADE_SHIFTS_STEP; }
Now, to me the assembly looks pretty strange, and very poorly optimised. My attempt at hand-coding the assembly gives:
dec.w &fade_shifts_counter jnz .CounterNotZero inc.b &fade_shifts mov.w #2000,&fade_shifts_counter .CounterNotZero:"
Which seems like it should perform a bit better and doesn't clobber any registers, but doesn't appear to actually work. Or rather, appears to behave in strange ways that I haven't quite got to the bottom of yet. I rather suspect that this may be related to the strange syntax employed by msp430-gcc for inline assembly. This document http://mspgcc.sourceforge.net/manual/c1308.html describes the inline assembly construct that I'm struggling with.
On the plus side, my synthesiser is working, and sounds pretty cool, and looks like it should run from two AA's (although at the moment it doesn't, which seems to be something to do with the boost circuit - the msp430 is perfectly happy). And soon I'll squeeze it onto some perfboard and stick it into one of the light guns.
-
So, with an eye to the exponential chirp code above, and following some-one else's advice in youtube, I've got a phased linear chirp plus some filtered white noise sounding quite nice in matlab (m-file attached for those who happen to have access to matlab and are interested laser.m.txt).
I'm probably going to need to code this in assembly in order to keep up with the 20kHz ouput frequency, since at 8Mhz I'll have only 400 cycles to calculate each sample.
Actually, that's probably plenty, but assembly is interesting anyway, and presumably there are some great resources on assembly on this site somewhere. I don't need a basic introduction, just a nice table of the assembly instructions and what they do (different kinds of shifts, how the flags are effected, addressing modes, etc etc).
If anyone could point me in that direction, I would be extremely grateful, and will of course post the final code that I come up with.
Thanks very much in advance.
-dave
-
That's a very good point about the supply voltage. I'll have to run some experiments to see how much speed I'll need to generate good sound effects. 6Mhz might well be enough because I'll be sending samples to a DAC at probably only 20kHz or so. At 16 bits per sample (even though it's only a 10 bit dac, it still wants 16 bits before it'll output...) that's about 320kHz, or 0.3Mhz SPI clock speed.
So from that point of view 6Mhz would be plenty to drive the DAC. Then it's just a question of writing some really tight code with a lookup table / algorithmic outputs and seeing what comes out.
-
-
Hi,
Since I have two of the MSP430's left over from the two launchpads I bought, I though I'd breathe new life and fun into the now-obsolete PlayStation one light guns. Using an MS430, an 8-pin DAC in a DIP package (an MCP4911) and an LM386 for amplification.
Thus far, the basic plan is:
- Run the 430 of two AAs directly
- Use a DC converter (TPS61041) to step this up to 5 volts ish for the LM386, Use a FET to turn this 5v side off when in sleep mode
- Use the 430 to generate some incredibly awesome laser gun sound...
- ..and actually a cool reloading (yes I know laser guns don't reload) sound too, because the gun has these cool buttons on the sides too.
Steps one and two are no problem, and talking to the DAC is over SPI so that should be straightforwards too. This should be a fun project, but I've never done any synthesis before. I guess I could put an SD card in there and use digital samples, but I'd like to create the sounds generatively.
Has anyone got any ideas? I've heard of FM synthesis, but it doesn't sound all that flash to me. Also, I've come across this rather amazing thing, which might also be a possibility if I could get the numbers right:
http://countercomplex.blogspot.co.nz/2011/10/algorithmic-symphonies-from-one-line-of.html
And just to add a picture too, here's the surface-mount DC converter soldered onto a bit of perfboard next to the schottky diode.
-
I used two lines for the sensors simply because I couldn't be bothered to figure out the rom search stuff for them. With two lines I just use the skip rom command and then I can read data from them at the same time. I had the spare pins anyway, so it was no problem.
That's a great idea about the 74HC595, but it's all soldered down now so it's too late!
About building circuits on verboard - I certainly used to just jump in, but ended up having to rework half of it. Getting interrupted by the kids didn't exactly help
VeeCAD looks great - I'll give it a try.
-
I've built a wireless temperature monitor with the following features:
- Very low-power (7uA) sleep mode
- Dual 1-wire dallas temperature sensor inputs, to measure two temperature sensors at the same time.
- A 2-line LCD display that displays the current measurement for 15 seconds when a button is pushed.
- 2xAA battery supply, with charge pump to power the LCD, temperature sensors and 433Mhz wireless module.
- Uses VirtualWire to transmit the data to an MSP430-powered receiver module.
This is all currently sitting outside, and the indoor rx unit is uploading the measured temperatures as well as the battery voltage to Cosm.
I've attached to this post the eagle files for the outdoor unit, and the veroboard layout for the same. I'll upload some photos also, and details of the indoor unit and the python script that uploads the data to cosm. The indoor unit is currently sitting on a breadboard, not in a nice project box, but its time will come
I've hacked the VirtualWire library to bits, leaving only the transmitting portion of the code. This was so that I could understand it, and so that I could have it running at a lower timer speed for (very slightly) lower power.
There's a few things in the schematic that might not really be necessary. I've used two fets to switch off the radio and the lcd/sensor separately, because I was under the impression that the radio transmitted the whole time while powered on. I now realise that it only transmits when the data pin is high, so I could have saved myself a component there. Also I'm using a whole hex level shifter IC just for the one input into the radio, because the other shifter's lines are all used up by the LCD. This also isn't necessary, since a singe fet would have done the job if I'd inverted the output of VirtualWire in software.
But in any case, it all works, and seems (so far) fairly reliable. There are some spurious readings that I accidentally uploaded to cosm while working on the receiver end, but other than that I'm pretty happy. What I'm really quite interested in is how the battery voltage is going to change over the coming few years.
Please feel free to use any of the code that I've uploaded, but be aware that in the words of some other forum poster somewhere, it's not supposed to be pretty - it's just supposed to work!
-dave
16 voice wavetable synth/sampler with MSP430G2553
in Projects
Posted
Wonderful.
I'm going to completely steal this, and use the extra space on the left side of my MIDI drum pads and play stadiums:
http://forum.43oh.com/topic/8340-midi-drum-pads/