
RogueGeek
-
Content Count
7 -
Joined
-
Last visited
-
Days Won
2
Posts posted by RogueGeek
-
-
I'm working on building a speed controller for a belt grinder and I'm using a G2553 for the controller. Since the speed of the motor will need to vary between 1000-8000 RPM, the 32.768Khz clock was too slow and the DCO was drifting too much and causing the RPM to vary by several hundred counts even when the motor was running at a constant stable speed. This in turn caused the PID loop to act really weird and I could not hold a stable speed. What I needed was a stable reference oscillator to use for sensing the RPM of the drive motor.
Being stubborn, I wanted to do this with only a single G2553 cpu and since the 2553 isn't spec'd to use a high freq crystal I went looking for an alternate way to provide a reference for the timers. I was quite happy to discover that P1.0 can be configured as an input for an external timer reference. Thus the "Launchpad Timer Reference Oscillator" was born.
In my application I'm using a 16MHz oscillator, but any speed could be used to fit the given application.
It is a small 0.3" x 1" pcb with an Abracon oscillator, a 0.1uF cap., and connectors.
It only took a minor code change to have the Timer use the external reference and so far it seems to have resolved my timing issues.
While this is without a doubt the most simple PCB I think I have ever designed, it just may turn out to be one of the most useful.
I will definitely be including an external reference oscillator in any of my future projects that need very accurate time references.
Here it is in action.
thanks for looking
Brian
-
Thanks for the suggestions.
Since I have to update the board design to fix a few mistakes I was already thinking of adding some of the functionality you mentioned and making it support I2C or SPI.
If there is interest from the 430 community I would be happy to make the Booster either as a completed unit or just the boards.
As for the current status...I haven't had too much time this week to work on the code. Hopefully this weekend will give me a few hours to work on the receive and interrupt functions.
-Brian
-
Quick update,
I finished assembling one of the prototype boards last night and wrote a bit of test code today.
The uart is very easy to work with using I2C on the G2553. I was able to get it sending data at 230,400 baud after about 20 minutes of hacking/coding.
I have attached a pic of the completed board. I'll upload some source code after I get it cleaned up a bit.
-
The bare boards are due in today. I hopefully will be able to get one or two assembled tonight if my neck and back don't decide to cramp up again and derail my evening plans.
SPI was mentioned above. The same chip supports both SPI and I2C. I decided on I2C and didn't make my boards to support both. I might add that in Rev 2.
The price from Mouser is quite high but the chips can be found for around $2.40 each if you by five or more. So it basically works out to getting 5 chips for the price of buying two through Mouser.
-
I thought I would write a quick post on a booster pack that I am working on.
First a bit of background.
I am working on a High Altitude Amateur Radio Balloon. I have some sensors and peripherals that use standard async serial for IO (GPS, speech module, radio modem), and I have run into a shortage of serial ports on the G2553. I could bit bang the serial out over GPIO but that just seems a bit inelegant to me. So I went looking for a better way. What I found is the SC16IS7xx line from NXP.
I chose the SC16IS752 since it offers dual serial ports with a single connection to the I2C bus, 64 byte FIFO, and 8 programmable GPIO lines.
From the datasheet it looks like the chip has 16 possible I2C slave addresses, so in theory you could stack 16 booster packs for a total of 32 additional serial ports and 128 additional I/O lines.
http://www.nxp.com/documents/data_sheet/SC16IS752_SC16IS762.pdf
So far I have designed the board and sent it out for production. I'm hoping to receive the bare boards near the end of September. After that I'll start doing some testing and working on driver code for the board.
I designed the board to use as few I/O pins as possible and to pass through all pins so other booster packs can stack on top if it.
J1-J4 and I2C address lines
J5/J6 are the serial ports and GPIO
J7-J9 are the boosterpack connections.
So as I'm writing this and just about to hit the POST button, I look at the rendering on the board and realize that I forgot something!
I noticed that I forgot to include the I2C pull-up resistors. DOH!! I also noticed that I mislabeled the J2 & J4 address pin. The pins closest to J7 should be labeled SDA not SCL. At least I only ordered 3 boards for prototyping. I'll fix it on Rev 2.
Thoughts, comments and questions are welcome
thanks
Brian
-
Hello,
I've been lurking here for well over a year and finally figured I should post something.
I've been learning to use the USCI I2C interface on the on MSP430G2553 and came up with this code for talking to the 24LC256 chip.
The code implements single/multibyte read/write routines for the 24LC256 EEPROM.
It should work on the 24LC512 without modification and on other 24LCxx family chips with minimal modification to the addressing.
I have probably broken numerous coding rules and best practices, there is no error checking to speak of, and nothing has timeouts. So use at your own risk.
I also have screen captures from the logic analyzer showing the SCL/SDA timings. If anyone is interested, please let me know and I'll post them.
Comments are welcome and appreciated.
Thanks
Brian
//************************************************************************************* // MSP430G2553 24LC256/512 I2C Interface // Brian McClure NZ8D // first.lastname@gmail.com // July 2014 // /********************************************************************************************/ /* Include */ /********************************************************************************************/ #include "msp430g2553.h" /********************************************************************************************/ /* I2C Definitions */ /********************************************************************************************/ //I2C Port Definitions #define i2cAddress 0x50 //Address GPIO Address //USCI I2C PIN Definition #define SDA_PIN BIT7 //Bit 7 USCI Port 1(SDA) #define SCL_PIN BIT6 //Bit 6 USCI Port 1(SCL) /********************************************************************************************/ /* UART Function Definitions */ /********************************************************************************************/ void UART_puts(char * s); void UART_outdec(long data, unsigned char ndigits); /********************************************************************************************/ /* I2C Function Definitions */ /********************************************************************************************/ void I2C_Write_EEProm(unsigned char slave_address, unsigned int register_address, char * data, unsigned char DataLength ); void I2C_Read_EEProm(unsigned char slave_address, unsigned int memory_address, char * data, unsigned char DataLength ); /********************************************************************************************/ /* Misc Function Definitions */ /********************************************************************************************/ void delay_ms(unsigned int delay); //Delay /********************************************************************************************/ /* Main */ /********************************************************************************************/ void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT BCSCTL1 = CALBC1_16MHZ; //set DCO to 16Mhz DCOCTL = CALDCO_16MHZ; // make sure to update the Delay_ms function if the DCO is changed //UART Initial P1SEL = BIT1 + BIT2; // P1.1 = RX pin, P1.2=TX pin P1SEL2 = BIT1 + BIT2 ; // P1SEL and P1SEL2 = 11--- Secondary peripheral module function is selected. UCA0CTL1 |= UCSSEL_2; // SMCLK UCA0BR0 = 69; // 16MHz 230400 UCA0BR1 = 0; // 16MHz 230400 UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt //USCI_I2C_Init P1SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0 P1SEL2|= SDA_PIN + SCL_PIN; UCB0CTL1 = UCSWRST; // Enable SW reset, HOLD USCB in a reset state UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, MODE 3 = I2C, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB0BR0 = 72; // Set I2C master speed 72 gives approx 200Khz clock at 16Mhz UCB0BR1 = 0; // Set I2C master speed UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation UART_puts("Starting...\r\n"); //output to UART to indicate the program is running __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enable while (1){ } } void UART_puts(char * s) { while (*s) { while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready? UCA0TXBUF = *s++; } } void UART_outdec(long data, unsigned char ndigits){ //UART_outdec modified/hacked to properly handle negative numbers. unsigned char sign, s[15]; //I copied this from a TI example or the 40oh forum, but I'm not sure of the original author. unsigned int i; sign = 0x00; if(data < 0) { sign='-'; data = -data; } i = 0; do { s[i++] = data % 10 + '0'; //adds the value of data least sig digit to ascii value of '0' if(i == ndigits) { s[i++]='.'; } } while( (data /= 10) > 0); if (i < ndigits) //fixes loss of leading 0 in fractional portion when number of digits is less than length of data { do { s[i++]='0'; } while (ndigits > i) ; s[i++]='.'; } if (sign == '-') //if value is negative then include the '-' sign { s[i] = sign; } else { //if value is positive then reduce 'i' counter by 1 to prevent the do loop from trying to output a sign character. i--; } do { while (!(IFG2 & UCA0TXIFG)); UCA0TXBUF = s[i]; } while(i--); } void delay_ms(unsigned int delay) { while (delay--) { __delay_cycles(16000); //1ms = 1000 cycles per 1Mhz clock freq. } } void I2C_Read_EEProm(unsigned char slave_address, unsigned int memory_address, char * data, unsigned char DataLength ) { //Reading from a 24LCxxx series is much easier then writing. Reading doesn't have to be done in 64 byte pages. /* * Todo: * 1. add checks to make sure write does not exceed maximum length of EEprom * 2. check for valid memory_address * */ int rLoop = 0; //loop counter while (UCB0STAT & UCBBUSY); //wait for USCI B0 bus to be inactive UCB0I2CSA = slave_address; //set SLAVE address UCB0CTL1 |= UCTR + UCTXSTT; //set USCI to be I2C TX, send start condition UCB0TXBUF = (memory_address & 0x7F00) >> 8; //transfer memory_address MSB while (UCB0CTL1 & UCTXSTT); // waiting for slave address to transfer while ((IFG2 & UCB0TXIFG) != UCB0TXIFG); //wait for TX IFG to clear UCB0TXBUF = (memory_address & 0x00FF); //transfer memory_address LSB while ((IFG2 & UCB0TXIFG) != UCB0TXIFG); //wait for TX IFG to clear UCB0CTL1 &= ~UCTR; //set USCI to be RECEIVER UCB0CTL1 |= UCTXSTT; //send restart while (UCB0CTL1 & UCTXSTT); // wait until I2C STT is sent for (rLoop=0; rLoop<DataLength; rLoop++) //receive loop { while ((IFG2 & UCB0RXIFG) != UCB0RXIFG); //wait for RX buffer to have data data[rLoop] = UCB0RXBUF; //Move rvcd data of or USCI buffer. This also clears the UCB0RXIFG flag if (rLoop == DataLength-2) //NACK and STOP must be send before the last byte is read from the buffer. //if not the CPU will read an extra byte. { UCB0CTL1 |= UCTXNACK; //generate a NACK UCB0CTL1 |= UCTXSTP; //generate a stop condition } } } void I2C_Write_EEProm(unsigned char slave_address, unsigned int memory_address, char * data, unsigned char DataLength ) { /* * Todo: * 1. add checks to make sure write does not exceed maximum length of EEprom * 2. check for valid memory_address * */ int NumPages = (DataLength)/64 ; //Count of full 64 byte pages, 0 means the data is less than a full 64 byte page int PartialPageBytes = DataLength % 64; //this is the number of bytes remaining that do not make up a full page int address = 0; //EEprom memory starting address, this is different from the I2C slave address int NP =1; //loop counter to iterate though pages of memory int offset = 0; int offsetlimit = 0; if (PartialPageBytes > 0) { NumPages++; //if we have left over bytes that do not make up a full page, this will add a page to handle those bytes. } __disable_interrupt(); //prevent interrupts from messing with the I2C functions while (UCB0STAT & UCBBUSY); //wait for USCI B0 bus to be inactive UCB0I2CSA = slave_address; //set SLAVE address for (NP=1; NP<=NumPages; NP++) { address = ((NP-1) * 64) + memory_address; //this is the full page start address UCB0CTL1 |= UCTR + UCTXSTT; //set USCI to be I2C TX, send start condition UCB0TXBUF = (address & 0x7F00) >> 8; //transferring memory_address MSB while (UCB0CTL1 & UCTXSTT); // waiting for slave address was transferred while ((IFG2 & UCB0TXIFG) != UCB0TXIFG); //wait for TX IFG to clear UCB0TXBUF = (address & 0x00FF); //transferring memory_address LSB while ((IFG2 & UCB0TXIFG) != UCB0TXIFG); //wait for TX IFG to clear offsetlimit = 63; //set the offset limit to 63 if ((NP == NumPages) && (PartialPageBytes > 0)) //if we are preparing to send the last/partial page { offsetlimit = PartialPageBytes-1; //set the offset limit to the number of partial page bytes } for (offset=0; offset <=offsetlimit; offset++) { UCB0TXBUF = data[((NP-1)*64)+offset]; //send data. //UART_outdec(offset,0); while ((IFG2 & UCB0TXIFG) != UCB0TXIFG); //wait for TX IFG to clear } UCB0CTL1 |= UCTXSTP; // set I2C stop condition while ((UCB0STAT & UCSTPIFG) == UCSTPIFG); //wait for Stop condition to be set //delay while the EEPROM completed its write cycle. //It would be better to use NACK polling here as described in the datasheet. delay_ms(6); //24LC256 has a max page write time of 5ms, so we will wait 6ms to be sure } UART_puts("Done...\r\n"); __enable_interrupt(); } // ----------------------------------------------------------------------------- // Interrupt Handlers // ----------------------------------------------------------------------------- #pragma vector=USCIAB0RX_VECTOR __interrupt void USCI0RX_ISR(void) { __bic_SR_register_on_exit(CPUOFF); // Clear CPUOFF bit from 0(SR) char RxChar = 0x00; //char used to receive serial data RxChar = UCA0RXBUF; // copy the RX buffer into RxChar char buf[4] = {0}; //i2c received data buffer //void I2C_Write_EEProm(unsigned char slave_address, unsigned int memory_address, char * data, unsigned char DataLength ) I2C_Write_EEProm(i2cAddress,0x00,"ABCDEFGHIJKLMNOP",15); //void I2C_Read_EEProm(unsigned char slave_address, unsigned int memory_address, char * data, unsigned char DataLength ) I2C_Read_EEProm(i2cAddress,0x05,buf,5); //reads three bytes starting at memory location 0x05 and returns the data into 'buf' UART_puts("READING: \r\n"); UART_puts(buf); UART_puts("\r\n"); __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enable } //end UART RCV ISR
- BlackAngel, zeke and munny
-
2
-
1
Sine Wave Generator using PWM like a class D amplifier.
in Code vault
Posted
About a year or so ago, I quit working on a project which would generate the 1200 baud audio tones for an AFSK Amateur Radio system.
Due to real life getting in the way, I never finished it. I got it to generate the audio tones but never fully implemented the ascii data input handling.
This general design isn't anything new, it has been done before on other platforms and it is quite likely someone has done it on the MSP.
It was my first real development effort using Timers and was a good learning experience.
I figured it was time to post the code in case all or part of it might be useful to someone. USE AT YOUR OWN RISK.
Essentially this code implements a software driven class D amplifier using an MSP430G2553 to generate either a 1200Hz or 2200 Hz sine wave using PWM.
The PWM output is run through a simple low pass filter to get the clean sine wave audio tones.
This uses all of the available timers on the G2553.
TA1 is the PWM generator running at 500Khz
TA0 is the modulator.
WDT is used as an interval timer to clock the ascii data into the modulator at 1200 buad (this is where I stopped working)
The TA0 modulator steps through the wave table and updates the TA1 PWM duty cycle setting it to the value in the wave table. TA1CCR1 = SineTable[sinePosition];
This runs in a continuous loop. The rate at which TA0 steps through the table is the MARK and SPACE values. This rotating PWM duty cycle is what generates the 1200Hz or 2200Hz sine wave.
The 500Khz PWM frequency is filtered out by the low pass filter.
The NOT WORKING parts.
Basically I quit shortly after starting on the ASCII input handling. The idea was that the WDT loop would set TA0 to MARK or SPACE depending on the ASCII character to be sent.
I was in the process of trying to implement a FIFO buffer when I stopped developing the code.
I hope someone finds this useful.
-Brian
WARNING: The code you are about to view most likely breaks every known rule for good software design.
It may use magic numbers, variables named X or Y, have little of no care for memory management, or have numerous other or unknown issues.
This was R&D code and I never really meant for it to be seen by anyone other than myself.
SineGenPWM_main.c