Hey guys, I've got an older analog oscilloscope I'd like to get rid of.
Hitachi V-202F 20MHz
2 Channel
Includes 2 Tektronix Probes with Screw On Tips #013-0071-00
Includes short ground lead
Some of the knobs need a bit of cleaning but everything is functional. Again, it's completely analog so there is no RS-232, USB or SD Card storage. Probably not much use anymore as a digital tool but could be used as a toy. This past x-mas I had a LP draw x-mas trees scrolling across the screen.
Here's the kicker... It's FREE! You just have to pay the shipping which will most certainly be not Free.
The MSP430x2xx Family User's Guide has several forumulas and tables for UART bit rate calculation. Makes it look more complicated than it is. Here is simple code to setup the bit rate divisor and modulator (for oversampling mode).
First you need to know the SMCLK frequency and the desired bit rate.
const unsigned long smclk_freq = 16000000; // SMCLK frequency in hertz
const unsigned long bps = 19200; // Async serial bit rate
Then a 20 bit divisor value can be calculated.
const unsigned long brd = (smclk_freq + (bps >> 1)) / bps; // Bit rate divisor
Shift and mask to setup the registers.
UCA0BR1 = (brd >> 12) & 0xFF; // High byte of whole divisor
UCA0BR0 = (brd >> 4) & 0xFF; // Low byte of whole divisor
UCA0MCTL = ((brd << 4) & 0xF0) | UCOS16; // Fractional divisor, oversampling mode
That's it!
A function for UART setup at a specified bit rate...
const unsigned long smclk_freq = 16000000; // SMCLK frequency in hertz
void setup_UART(const unsigned long bps) // Async serial bit rate
{
const unsigned long brd = (smclk_freq + (bps >> 1)) / bps; // Bit rate divisor
UCA0CTL1 = UCSWRST; // Hold USCI in reset to allow configuration
UCA0CTL0 = 0; // No parity, LSB first, 8 bits, one stop bit, UART (async)
UCA0BR1 = (brd >> 12) & 0xFF; // High byte of whole divisor
UCA0BR0 = (brd >> 4) & 0xFF; // Low byte of whole divisor
UCA0MCTL = ((brd << 4) & 0xF0) | UCOS16; // Fractional divisor, oversampling mode
UCA0CTL1 = UCSSEL_2; // Use SMCLK for bit rate generator, release reset
}
Noisy switches can cause port interrupts to rapid fire and hog the CPU. This can cause unexpected behavior of other ISRs and/or mainline code. Best practice is to debounce in a timer interrupt.
This is a simple debounce that operates similar to a cap in parallel with the switch and a pullup resistor.
The variable debounce will be zero when the switch is open and non-zero when it is closed. Note that it is not boolean.
The expression returns the value of debounce.
volatile unsigned debounce = 0;
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer0_A0 (void)
{
// Reset debounce timeout if switch is closed, else decrement timeout if not zero
((P1IN & BIT3) ? (debounce ? --debounce : 0) : (debounce = 25));
}
This code allows action to be taken on transitions and also provides a bool variable with switch state.
It is necessary to have two variables so that current debounce state can be compared to previous state.
volatile unsigned debounce = 0;
volatile unsigned switch_on = 0;
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer0_A0 (void)
{
// Debounce and check if switch state has changed
if(switch_on != (0 != ((P1IN & BIT3) ? (debounce ? --debounce : 0) : (debounce = 25)))) {
// Update switch state and test direction of transition
if(switch_on = !switch_on) {
// Switch has closed
} else {
// Switch has opened
}
}
}
I'll be sending Bluehash ~80 of them or so for the store. I'm thinking he should limit it to 2 a person... considering the amount of people here and the limited stock (plus it's a loss item, lol, ya'll'r gettin a steal :ugeek: :thumbup: )
The G series MSP430 have 1 or 4 factory calibrations for the DCO stored in info segment A. This firmware will store 96 DCO calibrations in info segments B, C, and D. After these calibrations have been stored it is possible to set the DCO to any frequency from 125 kHz to 16 MHz (128:1 range) with a simple function call...
set_dco.c
#include
static const unsigned * const InfoSegA = (unsigned *)0x10C0;
static const unsigned * const InfoSegD = (unsigned *)0x1000;
int set_dco(const unsigned f) // Set DCO to specified frequency in kHz
{ //
unsigned i; //
unsigned rsel = 0; // Init rsel/dco
unsigned dco = 0x20; //
const unsigned *cal = InfoSegD; // Begin at info segment D
do { //
if(f >= cal[0] && f < cal[1]) { // Frequency in range?
i = cal[1] - cal[0]; // Interpolate for MODx
dco += ((((f - cal[0]) * 0x20) + (i >> 1)) / i);
DCOCTL = dco; // Set the DCO/MOD
BCSCTL1 = rsel; // and RSEL
return 0; // Return success
} //
if((dco += 0x20) == 0xC0) // Next DCO/RSEL
dco = 0x20, ++rsel, ++cal; //
} while(++cal < InfoSegA); // While in info seg D, C, or B
return -1; // Return failure
}
Make sure that CCS is configured to write only to main memory so the calibration constants are not erased. This is not the default configuration.
The following code writes the DCO calibrations to the info segments. This only has to be done once for a specific chip. A 32 kHz crystal must be installed while this code is running, but is no longer needed once the calibration has been written.
Serial output while running calibration...
Info Segments - before calibration
D
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
C
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
B
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,65535,65535,65535,
A
35969,5886,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,4112,32786,0,
32209,742,0,32332,437,0,2302,65535,
65535,65535,65535,2049,36757,36504,36234,34506,
Calibrating...
99,106,114,123,134,146,122,131,
141,152,165,181,170,182,196,212,
229,251,241,258,278,299,323,354,
338,361,388,418,450,493,484,517,
555,596,642,703,673,718,770,827,
891,973,949,1012,1084,1163,1252,1368,
1348,1437,1538,1649,1776,1937,1924,2048,
2189,2346,2526,2754,2738,2911,3109,3329,
3586,3906,3520,3746,4007,4299,4635,5057,
4731,5032,5374,5761,6213,6770,6427,6824,
7278,7786,8385,9123,9350,9928,10587,11331,
12209,13283,12672,13414,14248,15200,16325,17715,
Info Segments - after calibration
D
99,106,114,123,134,146,122,131,
141,152,165,181,170,182,196,212,
229,251,241,258,278,299,323,354,
338,361,388,418,450,493,484,517,
C
555,596,642,703,673,718,770,827,
891,973,949,1012,1084,1163,1252,1368,
1348,1437,1538,1649,1776,1937,1924,2048,
2189,2346,2526,2754,2738,2911,3109,3329,
B
3586,3906,3520,3746,4007,4299,4635,5057,
4731,5032,5374,5761,6213,6770,6427,6824,
7278,7786,8385,9123,9350,9928,10587,11331,
12209,13283,12672,13414,14248,15200,16325,17715,
A
35969,5886,65535,65535,65535,65535,65535,65535,
65535,65535,65535,65535,65535,4112,32786,0,
32209,742,0,32332,437,0,2302,65535,
65535,65535,65535,2049,36757,36504,36234,34506,
Done
Calibration code...
main.c
#include
void putc(unsigned); // serial_tx.asm
void puts(char *); // serial_tx.asm
void utoa(unsigned, char *); // utoa.asm
static char s[8]; // buffer for printing unsigned int
// Address of info segments
static unsigned * const InfoSegA = (unsigned *)0x10C0;
static unsigned * const InfoSegB = (unsigned *)0x1080;
static unsigned * const InfoSegC = (unsigned *)0x1040;
static unsigned * const InfoSegD = (unsigned *)0x1000;
/*
Freq = Count / Time
Freq = Count / (100 / 32768) <-- 100 cycles of 32.768 kHz clock
Freq (kHz) = Count / ((100 / 32768) * 1000)
Freq (kHz) = Count / 3.0517578125
Freq (kHz) = Count * (1 / 3.0517578125)
Freq (kHz) = Count * 0.32768
Freq (kHz) ~= (Count * 21475) / 65536
Freq (kHz) ~= (Count * 21475) >> 16
Freq (kHz) ~= ((Count * 21475) + 32768) >> 16 <-- Better rounding
*/
void cal_block(unsigned *d, unsigned rsel, unsigned dco)
{
unsigned n = 32; // Do 32 cals beginning at rsel/dco with
// dco range of 0x20 to 0xA0 using 0x20 step
// - no modulation - 6 values per rsel
DCOCTL = 0; //
DCOCTL = dco & 0xE0; // Set initial DCOx, MODx == 0
BCSCTL1 = rsel; // Set initial RSELx
do { //
__delay_cycles(3277); // Wait for DCO to settle for 100 ms
TACTL |= TACLR; // Clear TimerA
TACTL |= MC_2; // Start counting - TimerA continuous mode
__delay_cycles(100 - 5); // 100 cycles (5 overhead from previous and next instructions)
TACTL &= ~MC_2; // Stop counting - TimerA stop mode
//
*d++ = ((21475L * TAR) + 32768) >> 16; // Scale to kHz
//
if((DCOCTL += 0x20) == 0xE0) { // Increment DCOx, check if limit reached
DCOCTL = 0x20; // Reset DCOx
++BCSCTL1; // Next RSELx
} //
} while(--n); //
}
void cal_write(const unsigned *c, unsigned *f)
{ // Write one full info segment - 64 bytes / 32 ints
unsigned n; //
DCOCTL = 0; // Run DCO at 1 MHz
BCSCTL1 = CALBC1_1MHZ; //
DCOCTL = CALDCO_1MHZ; //
FCTL2 = FWKEY | FSSEL_2 | 3 - 1; // SMCLK / 3 for flash timing generator
FCTL3 = FWKEY; // Clear lock bit
FCTL1 = FWKEY | ERASE; // Set erase bit
*f = 0; // Dummy write to erase segment
FCTL1 = FWKEY | WRT; // Set wrt bit for write operation
n = 32; do *f++ = *c++; while(--n); // Write flash
FCTL1 = FWKEY; // Clear wrt bit
FCTL3 = FWKEY | LOCK; // Set lock bit
}
void show_block(const unsigned *c)
{ // Show 32 unsigned ints
unsigned a, b; //
a = 4; // 4 lines
do { //
b = 8; // 8 columns
do { //
utoa(*c++, s); // Unsigned to ASCII
puts(s); // Print it
putc(','); //
} while(--; // Next column
puts("\r\n"); // Next line
} while(--a); //
}
void show_all_info_segs(void)
{
puts("D\r\n"); //
show_block(InfoSegD); // D
puts("C\r\n"); //
show_block(InfoSegC); // C
puts("B\r\n"); //
show_block(InfoSegB); // B
puts("A\r\n"); //
show_block(InfoSegA); // A
puts("\r\n"); //
}
void main(void) //
{ //
unsigned n; //
unsigned cal[32];
//
WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset
//
TACTL = TASSEL_2; // SMCLK (DCO)
//
P1DIR = BIT1; //
P1OUT = BIT1; //
//
BCSCTL3 = XCAP_2; // 10 pF
n = 0; //
do { // - Wait for MSP430 to detect clock is stable
IFG1 &= ~OFIFG; // Clear OFIFG
while(--n); // Wait a while
} while(IFG1 & OFIFG); // Loop until OFIFG remains cleared
BCSCTL2 = SELM1 | SELM0; // - Use LFXT1CLK (32 kHz xtal) as clock source
//
// Show all the info segments before calibration
puts("Info Segments - before calibration\r\n");
show_all_info_segs(); //
//
puts("Calibrating...\r\n"); // Do calibration and write to flash info segments D to B
//
cal_block(cal, 0, 0x20); // Calibrate
cal_write(cal, InfoSegD); // Write
show_block(cal); // Show on terminal
//
cal_block(cal, 5, 0x60); //
cal_write(cal, InfoSegC); //
show_block(cal); //
//
cal_block(cal, 10, 0xA0); //
cal_write(cal, InfoSegB); //
show_block(cal); //
//
puts("\r\n"); //
// Show all the info segments after calibration has been written
puts("Info Segments - after calibration\r\n");
show_all_info_segs(); //
//
puts("Done\r\n"); //
//
for(;; //
}
serial_tx.asm
.cdecls C, LIST, "msp430g2211.h"
.text
.def putc, puts
; 32768 Hz / 2400 bps = 13.6533 cycles per bit ~= 41 cycles per 3 bits (14 / 13 / 14)
; Char to tx in R12
putc: or #0x0100, R12 ; Stop bit
mov #0x02, R15 ; Serial out bitmask
jmp bit_start ;
;
bit0: jmp $ + 2 ;
jmp $ + 2 ;
nop ;
rra R12 ; Bit 0/3/6
jc bit0h ;
bic.b R15, &P1OUT ; 14/55/96
jmp bit1 ;
bit0h: bis.b R15, &P1OUT ; 14/55/96
jmp bit1 ;
;
bit1: jmp $ + 2 ;
jmp $ + 2 ;
rra R12 ; Bit 1/4/7
jc bit1h ;
bic.b R15, &P1OUT ; 27/68/109
jmp bit2 ;
bit1h: bis.b R15, &P1OUT ; 27/68/109
jmp bit2 ;
;
bit2: jmp $ + 2 ;
jmp $ + 2 ;
nop ;
rra R12 ; Bit Start/2/5/Stop
jc bit2h ;
bit_start: bic.b R15, &P1OUT ; 0/41/82/-
jmp bit0 ;
bit2h: bis.b R15, &P1OUT ; -/41/82/123
jne bit0 ;
ret_ins: ret
puts: ; Tx string using putc
mov R12, R14 ; String pointer in R12, copy to R14
putsloop: ;
mov.b @R14+, R12 ; Get a byte, inc pointer
tst.b R12 ; Test if end of string
jz ret_ins ; Yes, exit...
call #putc ; Call putc
jmp putsloop ;
;
.end ;
utoa.asm
.cdecls C, LIST, "msp430g2211.h"
.text
.def utoa
utoa: ; --- Unsigned to ASCII ---
; - Range 0 to 65535
; - Leading zeros supressed
push R10 ;
clr R14 ; Clear packed BCD
.loop 13 ; Do 13 bits
rla R12 ; Get bit 15 to 3 of binary
dadd R14, R14 ; Multiply BCD by 2 and add binary bit
.endloop ;
clr R15 ; Clear digit 1 of packed BCD
.loop 3 ; Do 3 bits
rla R12 ; Get bit 2 to 0 of binary
dadd R14, R14 ; Multiply BCD by 2 and add binary bit
dadd R15, R15 ;
.endloop ;
swpb R14 ; Swap digit order
mov R14, R12 ; Copy packed BCD digits
and #0x0F0F, R12 ; Mask digits 5 & 3
rra R14 ; Shift digits 4 & 2 to lower nibble
rra R14 ;
rra R14 ;
rra R14 ;
and #0x0F0F, R14 ; Mask digits 4 & 2
mov #('0' << 8) | '0', R10 ; Make ASCII
add R10, R12 ;
add R10, R14 ;
add R10, R15 ;
cmp.b R10, R15 ; Is first digit a 0?
jne dig5 ; No...
cmp.b R10, R14 ; Is second digit a 0?
jne dig4 ; No, only the first...
cmp.b R10, R12 ; Is third digit a 0?
jne dig3 ; No, only the first two...
cmp R10, R14 ; Is fourth digit a 0? (second is zero)
jne dig2 ; No, only the first three...
dig1: ; First four digits are all 0
swpb R12 ; Fifth digit to string
mov.b R12, 0(R13) ;
inc R13 ;
clr.b 0(R13) ; NULL terminate string
pop R10 ;
ret ; Return
;
dig5: ;
mov.b R15, 0(R13) ; First digit to string
inc R13 ;
dig4: ;
mov.b R14, 0(R13) ; Second digit to string
inc R13 ;
dig3: ;
mov.b R12, 0(R13) ; Third digit to string
inc R13 ;
dig2: ;
swpb R14 ; Fourth digit to string
mov.b R14, 0(R13) ;
inc R13 ;
jmp dig1 ;
Check out USCI I2C documentation, you have to shift device ID >>1, it takes only 7 low bits unlike what you see on the bus. That's the reason why your slave doesn't send ACK.
This firmware will measure the frequency of all 4,096 possible DCO settings and send that information to the host computer as CSV (comma separated values) data at 2400 bps. Just capture the serial data to a text file and view it in your favorite spreadsheet program. The 32 kHz watch crystal is required for this firmware, it is used as reference timebase for serial tx and frequency measurement. I think this will run on all G series chips, and may also work on F2000 series.
The vertical axis is frequency on a log scale.
The horizontal axis DCOCTL (DCOx, MODx)
The 16 plots are RSELx (lower 4 bits of BCSCTL1)
If I can code an i2c master, so could you. With the usi/usci it's pretty straight forward for master, even bitbanging a master is simple.
In your case, I'm assuming both the original master and the sensor cannot be changed? How much do you know about the master?
I'm also assuming that the sensor is read like an eeprom.
Start
Master writes slave bus address + w bit.
Slave acks.
Master writes data address.
Slave acks
(Re)start
Master writes slave bus address + r bit
Slave acks
Slave transmits 1st bit
Master acks
Slave transmits 2nd bit
Master nacks
Stop
If so, bit bang the sensor, and use the usi/usci eeprom slave example from TI to read back to the master.
Biggest concern is simply how often the master reads the slave, and ensuring your code is up to the timing of the master. Is the master i2c running at 100khz, 400khz or faster? 100 and 400 is easy enough.
The other concern is how you code the interrupts and how the master handles missed replies. If you are in the middle of reading the sensor, and the interrupt from the master triggers, is the sensor going to like a hung session and can you reset it? Did you ensure that saving the data from the slave did not get interrupted, corrupting what you would send to the master?
Or how does the master react to ignored sessions? Can you stretch the clock on the master to be able to read and translate the sensor readings?
Not sure how math intensive the translating you need is, but the g2553 is good enough to do two i2c and I'm sure more than enough for what you have in mind.
My lcd project on github has som bitbang and hardware i2c code, if its helpful. May not bereadable. Used hw i2c first, then moved to bitbang. Only one way though.
So, now you got a job doing hardware design and you want to use the MSP430 in your design.
But you've discovered that your boss has insisted on using this "wicked cool awesome" and ancient 5V sensor because they have a bazillion in stock.
"What's the problem with that?" you ask.
The problem is this: the MSP430 is a 3.6V device and it doesn't have 5V tolerant I/O's. This means that you'll probably hurt the part if you put 5V into an input. Go ahead and google it. You'll find our very own OCY schooling someone on this topic back in 2008. I'll let you find the article. OCY is shy. And he's probably gonna rip me open for giving away his secret identity.
So, what's the answer? SLAA148 is the answer.
It will take you through the various input and output scenarios that you may face trying to interface to higher voltage systems - not just 5V.
Using this information, you should be able to measure the status of a 12V power supply with the A/D inputs or figure out how to drive a 12V relay.
If you are new to the MSP430 then you're probably drowning in information right now.
It's true that there are a zillion configurations to make before the 430 will do what you want it do do.
So I'm betting that you are asking yourself "Where do I start?"
I humbly suggest the following TI application notes and books that will get you going in the right direction:
1. slaa294: MSP430 Software Coding Techniques
This application report covers software techniques and topics of interest to all MSP430
programmers. The first part of the document discusses the MSP430 standard
interrupt-based code flow model, recommended for the vast majority of applications.
The next part discusses a handful of techniques that should be considered by any
developer that sets out to develop an MSP430 application. Using these methods can
greatly reduce debug time and/or provide additional robustness in the field. They
include initialization procedures, validation of supply rails before performing
voltage-sensitive operations, and use of special functions. Code examples are
provided.
2. : MSP430 32-kHz Crystal OscillatorsSelection of the right crystal, correct load circuit, and proper board layout are importantfor a stable crystal oscillator. This application report summarizes crystal oscillatorfunction and explains the parameters to select the correct crystal for MSP430ultralow-power operation. In addition, hints and examples for correct board layout aregiven. The document also contains detailed information on the possible oscillator teststo ensure stable oscillator operation in mass production.
3. MSP430 Microcontroller Basics by John H. Davies
The best thing I can say about this book at this time is that it describes well how to make use of the clocking system of the MSP430. This book should be in your personal library or at least on your wishlist.
Once you digest the information above then you will be in good shape for success in working with the msp430.
Have something to add?
Then post up your valuable sources of knowledge.