wilywyrm 13 Posted December 28, 2011 Share Posted December 28, 2011 I'm doing a robot arm project for a competition at my school, and I'm having real trouble with getting the g2553 hardware UART to work. For the life of me, I can't figure out why it's not printing anything at all. I compiled the TI code example that I'm modifying and it works just fine on my Linux box with Cutecom, but when I make transmission not interrupt-based, I get nothing! :x The functions I have in there were what I planned to use with a timer to send strings after a TimerA interrupt, but eventually I stripped it down to just writing to the TX buffer. Still nothing. Shouldn't it give me an 'a"? //****************************************************************************** // MSP430G2xx3 Demo - USCI_A0, Ultra-Low Pwr UART 9600 String, 32kHz ACLK // // Description: This program demonstrates a full-duplex 9600-baud UART using // USCI_A0 and a 32kHz crystal. The program will wait in LPM3, and will // respond to a received 'u' character using 8N1 protocol. The response will // be the string 'Hello World'. // ACLK = BRCLK = LFXT1 = 32768Hz, MCLK = SMCLK = DCO ~1.2MHz // Baud rate divider with 32768Hz XTAL @9600 = 32768Hz/9600 = 3.41 //* An external watch crystal is required on XIN XOUT for ACLK *// //****************************************************************************** #include #include void writeChar(char aChar); void writeWord(char* word); void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop WDT P1DIR = 0xFF; // All P1.x outputs P1OUT = 0; // All P1.x reset P2DIR = 0xFF; // All P2.x outputs P2OUT = 0; // All P2.x reset P1SEL = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD P1SEL2 = BIT1 + BIT2 ; // P1.1 = RXD, P1.2=TXD P3DIR = 0xFF; // All P3.x outputs P3OUT = 0; // All P3.x reset //CCTL0 = CCIE; // CCR0 interrupt enabled //CCR0 = 50000; //TACTL = TASSEL_1 + MC_2 + TAIE; // ACLK, contmode, enable timer interrupt UCA0CTL1 |= UCSWRST; // Put state machine in reset UCA0CTL1 |= UCSSEL_1; // CLK = ACLK UCA0BR0 = 0x03; // 32kHz/9600 = 3.41 UCA0BR1 = 0x00; // UCA0MCTL = UCBRS1 + UCBRS0; // Modulation UCBRSx = 3 //UC0IE |= UCA0RXIE | UCA0TXIE; UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** UCA0TXBUF = 'a'; __bis_SR_register(GIE); } void writeChar(char aChar) { while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready? UCA0TXBUF = aChar; // TX next character //IFG2 &= ~UCA0TXIFG; } void writeWord(char word[]) { while(*word) //for(unsigned int i = 0; i < sizeof word - 1; i++) writeChar(*(word++)); } Here's the makefile, if it helps. I think the only changes are that the mcu flag was set to msp430g2553 to make Uniarch happy and the version of C to c99. CC=msp430-gcc CFLAGS=-Os -Wall -g -mmcu=msp430g2553 -std=c99 OBJS=main.o all: $(OBJS) $(CC) $(CFLAGS) -o main.elf $(OBJS) %.o: %.c $(CC) $(CFLAGS) -c $< clean: rm -fr main.elf $(OBJS) I've been stuck on this for days and would be tremendously grateful to anyone who could help. Quote Link to post Share on other sites
pabigot 355 Posted December 28, 2011 Share Posted December 28, 2011 Try putting the write into a loop: while (1) { if (UC0IFG & UCA0TXIFG) { UCA0TXBUF = 'a'; } } Sometimes the clocks take a while to stabilize and the first character emitted is garbled. Your code with this change spits a stream of "a"s to the output on my launchpad+g2553. Quote Link to post Share on other sites
Rickta59 589 Posted December 28, 2011 Share Posted December 28, 2011 I've been stuck on this for days and would be tremendously grateful to anyone who could help. Did you change the jumpers for TX / RX on J3? Is this on a 1.5 Launchpad with the new style jumpers? The old LP version <= 1.4 board will require you to provide a cross jumper. When you say you were using an interrupt based version and it worked, was that a Timer A based interrupt or USCI based interrupt? If you were using the TimerA based serial it would work with the original TX/RX jumpers. Hardware based USCI would require a change, you would need to swap TX/RX on the J3 jumper. -rick Quote Link to post Share on other sites
Rickta59 589 Posted December 28, 2011 Share Posted December 28, 2011 Try putting the write into a loop: while (1) { if (UC0IFG & UCA0TXIFG) { UCA0TXBUF = 'a'; } } Sometimes the clocks take a while to stabilize and the first character emitted is garbled. Your code with this change spits a stream of "a"s to the output on my launchpad+g2553. If you do that on a linux box, it is going to overflow the serial ACM buffer and the kill the port. It will just be dead. On linux, you should be waiting till you receive something before you start spewing. wilywyrm 1 Quote Link to post Share on other sites
wilywyrm 13 Posted December 29, 2011 Author Share Posted December 29, 2011 Thanks for the help, guys! Still don't have it fixed or really know why, though. Sometimes the clocks take a while to stabilize and the first character emitted is garbled. Your code with this change spits a stream of "a"s to the output on my launchpad+g2553. Does the code without the change output a single 'a'? I'm using a Rev 1.4 launchpad right now, but I do have a 1.5 also. I don't think the problem is with the jumpers because it works with TI's unmodified code example perfectly. Sorry for not being clear, and "interrupt-based" meant hardware USCI (USCIAB0RX_VECTOR, USCIAB0TX_VECTOR, all that good stuff). If hitting the port with a unsolicited continuous stream of 'a's will kill the port, how do I not kill it without sending something first? The goal eventually is to be able to control the arm through a wireless PS2 controller, but I'll need the msp to give me continuous positioning output to my PC before I can hope to even get hardwired servo control right. Quote Link to post Share on other sites
Rickta59 589 Posted December 29, 2011 Share Posted December 29, 2011 If hitting the port with a unsolicited continuous stream of 'a's will kill the port, how do I not kill it without sending something first? The goal eventually is to be able to control the arm through a wireless PS2 controller, but I'll need the msp to give me continuous positioning output to my PC before I can hope to even get hardwired servo control right. Just start by sitting in a loop waiting for a request. Once you receive something you can start sending back your position data. This seems to work: main.cpp /** * main.cpp - swserial/timerserial/usciserial Serial Asynch test driver. * * To test: use putty and connect to /dev/ttyACM0 at 9600 Baud * */ #include #include #include "serial.h" #define MCLK_FREQ 16000000UL /* run @16MHz */ /** * typedef Serial - start of a simple serial wrapper class */ typedef struct { /** * begin() - initialize serial port, sets registers, port direction, and * alternative port features as required by implementing serial routines. * Depending on the implementing method, you may not be able to use * arbitrary rx/tx pins the software only implementation is the only * version capable of using any arbitrary pin. * * baudRate - bits/sec default: 9600 * rxPin - receive pin default: 1 (P1.1) ( g2553 defaults ) * txPin - transmit pin default: 2 (P1.2) */ static void begin(uint32_t baud = 9600, int rxPin = 1, int txPin = 2) { init_serial(1 << txPin, 1 << rxPin, MCLK_FREQ / baud, (MCLK_FREQ / baud) >> 8); } /** * puts - writes the string s to the serial output stream without a trailing * newline character. Note: this differs from the ISO standard puts() * function which appends a newline character. ` */ static void puts(register const char *s) { while(*s) { putchar(*s++); } } } serial_t; static serial_t Serial; static void initMCLK() { #ifdef __MSP430FR5739 CSCTL0_H = 0xA5; // CS_KEY CSCTL1 |= DCOFSEL0 + DCOFSEL1; // Set max. DCO setting CSCTL2 = SELA_3 + SELS_3 + SELM_3; // set ACLK = MCLK = DCO CSCTL3 = DIVA_0 + DIVS_0 + DIVM_0; // set all dividers #else // Use 16MHz DCO factory calibration DCOCTL = 0; BCSCTL1 = CALBC1_16MHZ; DCOCTL = CALDCO_16MHZ; #endif } int main(void) { WDTCTL = WDTPW | WDTHOLD; /* Disable watchdog */ initMCLK(); // configure serial device using defaults Serial.begin(9600); //__eint(); // we enable interrupts after user setup() to give them a chance to customize // if you are using an FTDI or other reasonable USB-serial chip you can output // serial data at reset. If you want to use the Launchpad /dev/ttyACM0 you have // to wait for a request before spewing. #if 0 printf("\rWelcome to msp430 uNix! %s %s %d, %d:%d %s\r\nlogin: ", "Fri", "Oct", 9, 11, 25, "AM"); Serial.puts("\r\n# "); #endif int c; int nCharCnt = 0; for (; { c = getchar(); // do some minimum line control to handle backspace if ( c != 127 ) { putchar(c); nCharCnt++; } else { if (nCharCnt > 0) { putchar(c); --nCharCnt; } } // append newline on CR if ( c == '\r' ) { Serial.puts("\n# "); nCharCnt = 0; } // restart processor on CTRL-D if ( c == 0x04 /*CTRL-D*/ ) { WDTCTL = WDTHOLD; // restart by setting the WDTCTL without a password } } return 0; } usci_serial.c /* * usci_serial.c - USCI implementations of init_serial(), getchar(), putchar() and puts() * * Created on: Oct 30, 2011 * Author: kimballr - rick@kimballsoftware.com */ #include #include #include "serial.h" /** * init_serial(txPinMask, rxPinMask, bitDuration, durMod) * * Really simple use of the hardware UART so that it will be * compatible with the our simple software ones. We setup the * hardware UART using the pins and bit duration provided. * * This code doesn't use any interrupts. Just simple polling * to decide when to receive or transmit. * */ void init_serial(int txPinMask, int rxPinMask, unsigned duration, unsigned durmod) { // Use UART defaults for most things. // LSB // 8-N-1 ( 8 bit, NO parity, 1 Stop bit) UCA0CTL1 |= UCSSEL_2; // use SMCLK as source for USCI clock UCA0BR0 = duration; // bit duration is MCLK/BAUD UCA0BR1 = durmod; // (MCLK/BAUD) >> 8 #ifdef __MSP430FR5739 P2SEL1 |= BIT0 | BIT1; P2SEL0 &= ~(BIT0 | BIT1); #else P1SEL = rxPinMask | txPinMask; // P1.1=RXD, P1.2=TXD P1SEL2 = rxPinMask | txPinMask; // P1.1=RXD, P1.2=TXD #endif UCA0CTL1 &= ~UCSWRST; // Initialize USCI state machine } /** * int getchar() - read next char from serial port rx pin * * */ int getchar(void) { // sit and spin waiting for baudot heh I make myself laugh while (!(UC0IFG & UCA0RXIFG)) { ; // busywait until USCI_A0 RX buffer is ready } return UCA0RXBUF; // return RXed character } /** * putchar(int c) - write char to serial port * */ int putchar(int c) { // make sure previous character has been sent // before trying to send another character while (!(UC0IFG & UCA0TXIFG)) { ; // busywait until USCIA0TX buffer is ready } UCA0TXBUF = (uint8_t) c; // TX character return 0; } serial.h //------------------------------------------------------------------------ // serial.h - function declarations for compact asm serial routines //------------------------------------------------------------------------ #ifdef __cplusplus extern "C" { #endif void init_serial(int txPinMask, int rxPinMask, unsigned duration, unsigned durMod); int getchar(void); int putchar(int); #ifdef __cplusplus } /* extern "C" */ #endif To compile just use: msp430-gcc -O0 -g -mmcu=msp430g2553 main.cpp usci_serial.c -o main.elf wilywyrm 1 Quote Link to post Share on other sites
pabigot 355 Posted December 29, 2011 Share Posted December 29, 2011 Sometimes the clocks take a while to stabilize and the first character emitted is garbled. Your code with this change spits a stream of "a"s to the output on my launchpad+g2553. Does the code without the change output a single 'a'? No. Hence the assumption that the first one (or several) are not being correctly decoded by the PC, possibly due to clock differences. I didn't bother sending a different character on each iteration to see which one got through first, and I've shut down my development machine for the night. The spew shouldn't be an issue at 9600, but it was really just a proof of concept (note that I use the HID interface rather than the tty interface, as noted here, which may be is less susceptible to serial port instabilities). If the possibility bothers you, put a delay in the loop; the point is to see whether you do start getting characters out or not. If not, there's something else wrong: your 32kiHz clock isn't, or your jumpers are wrong, or other things. That you said the demo code from TI worked suggests those aren't an issue. wilywyrm 1 Quote Link to post Share on other sites
wilywyrm 13 Posted December 29, 2011 Author Share Posted December 29, 2011 pabigot, thanks for the suggestion. I need to try that tomorrow. Rick, thank you SO much for the code. I'm actually getting output now! I did have to modify your code a little and take out some C++ stuff since mspgcc doesn't want to compile the cpp file. I'll post it tomorrow for reference, but it does work and I understand it for the most part. The (comparatively small) trouble now is that I don't get this modulation business. Could you explain the UCA0BR1 register? The datasheet goes a little over my head on the first read, and mainly I want to know how to adjust the registers for different clock speeds. The USCI does NOT like me simply redefining MCLK_FREQ to 32768 and choosing ACLK. Again, thanks everyone. I've gotten more done today than the last week because of your help. Quote Link to post Share on other sites
Rickta59 589 Posted December 29, 2011 Share Posted December 29, 2011 The (comparatively small) trouble now is that I don't get this modulation business. Could you explain the UCA0BR1 register? The datasheet goes a little over my head on the first read, and mainly I want to know how to adjust the registers for different clock speeds. The USCI does NOT like me simply redefining MCLK_FREQ to 32768 and choosing ACLK. The easiest way to deal with the baud rate registers is to use this page: http://mspgcc.sourceforge.net/baudrate.html http://mspgcc.sourceforge.net/cgi-bin/msp-uart.pl?clock=32768&baud=9600&submit=calculate Seems to yield: UBR00=0x03; UBR10=0x00; UMCTL0=0x29; /* uart0 32768Hz 9637bps */ I haven't tried the setting above so I can't vouch for it. I'm still trying to understand the proper way to use these registers also. I've looked at the msp-uart.pl perl code and compared it to another site on the net that uses an Excel spreadsheet and they produce different results and use different algorithms. If you can set your MCU_FREQ to be a multiple of the baud rate you want to use you can ignore all the modulation. Using a 1.2288 Mhz clock frequency the cycle count between bits would be an even multiple of 9600 so the UBR00 would be 1228800/9600 = 128. -rick wilywyrm 1 Quote Link to post Share on other sites
pabigot 355 Posted December 29, 2011 Share Posted December 29, 2011 The easiest way to deal with the baud rate registers is to use this page:http://mspgcc.sourceforge.net/baudrate.html While that does work, it was only designed for the 1xx generation of MSP430. As a general practice, I'd recommend instead getting the family user guide for the specific chip (in the case of the MSP430G2553, go to the mcu homepage, and download MSP430x2xx Family User's Guide revision H). In the UART chapter for your chip, there'll usually be a table of "Typical Baud Rates and Errors", which lists the sub-register settings for a specific clock. There are a lot of UART alternatives across the MSP430 line, and looking in the guide rather than using a script will help keep you aware of any inconsistencies between them. (I was surprised to learn that the eUSCI UART on the FRAM parts has a few differences, including recommendation of UCOS16 and a new register field.) There are a variety of UART configuration examples for different UART peripherals in the platform support directories of test430. If you use standard DCO rates (like the pre-calibrated ones), and SMCLK or a derivative thereof as the UART clock, calibration is pretty straightforward. Keep in mind that while "32K" in crystal-speak is really 32 kiHz or 32.768 kHz or 2^15 Hz, 1MHz is really 10^6Hz and never 2^22Hz. This can have a significant impact on error rate. One other thing to keep in mind: in a lot of cases, the crystal doesn't contribute to the DCO unless the XIN/XOUT port pins are correctly configured in addition to the relevant clock register settings. demo/clocks in test430 configures and validates the crystal on a launchpad; see the comments. I seem to recall noticing that if this isn't done, a brought-to-GPIO ACLK doesn't actually tick unless it's reconfigured to use VLOCLK, at least on the value-line devices, but I don't know what impact that might have on internal use. I alway use SMCLK to control a UART; the only reason I can think of to use ACLK is if you're doing interrupt-driven I/O in low-power mode using DMA, and don't want to wait for the DCO to re-stabilize. wilywyrm 1 Quote Link to post Share on other sites
wilywyrm 13 Posted December 30, 2011 Author Share Posted December 30, 2011 Today was... a learning experience. I had wanted to use an external crystal to provide a 16MHz MCLK and spent all day trying to figure how, but it looks like the G2553 doesn't even have the capability to use an oscillator above 50kHz! I hope they provide that in a later G-series generation, but in the meantime, I'll have to use the DCO like in Rick's code. Plan is now a 1MHz MCLK, 500kHz SMCLK for the controller's SPI bus, and 32.768kHz for the UART. All the advice and links you gave will definitely help with fully understanding UART functionality, but for now I'll leave good enough alone and hope I don't break anything. Scheduled deadline's in a week. By the way, what license is the code under? Here's Rick's code modified to conform to boring, old C. /** * main.c - swserial/timerserial/usciserial Serial Asynch test driver. * * To test: use putty and connect to /dev/ttyACM0 at 9600 Baud * */ #include #include #include #include "serial.h" #define MCLK_FREQ 16000000UL /* run @16MHz */ /** * typedef Serial - start of a simple serial wrapper class */ //typedef struct { /** * begin() - initialize serial port, sets registers, port direction, and * alternative port features as required by implementing serial routines. * Depending on the implementing method, you may not be able to use * arbitrary rx/tx pins the software only implementation is the only * version capable of using any arbitrary pin. * * baudRate - bits/sec default: 9600 * rxPin - receive pin default: 1 (P1.1) ( g2553 defaults ) * txPin - transmit pin default: 2 (P1.2) */ /* static void begin(uint32_t baud = 9600, int rxPin = 1, int txPin = 2) { init_serial(1 << txPin, 1 << rxPin, MCLK_FREQ / baud, (MCLK_FREQ / baud) >> 8); } */ /** * puts - writes the string s to the serial output stream without a trailing * newline character. Note: this differs from the ISO standard puts() * function which appends a newline character. ` */ /* static void puts(register const char *s) { while(*s) { putchar(*s++); } } } serial_t; static serial_t Serial; */ //void begin(uint32_t baud = 9600, int rxPin = 1, int txPin = 2) { void begin(uint32_t baud, int rxPin, int txPin); //void puts(register const char *s) { void putS(const char *s); //static void initMCLK() void initMCLK() { #ifdef __MSP430FR5739 CSCTL0_H = 0xA5; // CS_KEY CSCTL1 |= DCOFSEL0 + DCOFSEL1; // Set max. DCO setting CSCTL2 = SELA_3 + SELS_3 + SELM_3; // set ACLK = MCLK = DCO CSCTL3 = DIVA_0 + DIVS_0 + DIVM_0; // set all dividers #else // Use 16MHz DCO factory calibration DCOCTL = 0; BCSCTL1 = CALBC1_16MHZ; DCOCTL = CALDCO_16MHZ; #endif } int main(void) { WDTCTL = WDTPW | WDTHOLD; /* Disable watchdog */ initMCLK(); // configure serial device using defaults //Serial.begin(9600); begin(9600, 1, 2); //__eint(); // we enable interrupts after user setup() to give them a chance to customize // if you are using an FTDI or other reasonable USB-serial chip you can output // serial data at reset. If you want to use the Launchpad /dev/ttyACM0 you have // to wait for a request before spewing. int c; int nCharCnt = 0; while(1){ c = getchar(); // do some minimum line control to handle backspace if ( c != 127 ) { putchar(c); nCharCnt++; } else { if (nCharCnt > 0) { putchar(c); --nCharCnt; } } // append newline on CR if ( c == '\r' ) { //Serial.puts("\n# "); putS("\n# "); nCharCnt = 0; } // restart processor on CTRL-D if ( c == 0x04 /*CTRL-D*/ ) { WDTCTL = WDTHOLD; // restart by setting the WDTCTL without a password } } return 0; } /** * begin() - initialize serial port, sets registers, port direction, and * alternative port features as required by implementing serial routines. * Depending on the implementing method, you may not be able to use * arbitrary rx/tx pins the software only implementation is the only * version capable of using any arbitrary pin. * * baudRate - bits/sec default: 9600 * rxPin - receive pin default: 1 (P1.1) ( g2553 defaults ) * txPin - transmit pin default: 2 (P1.2) */ //static void begin(uint32_t baud = 9600, int rxPin = 1, int txPin = 2) { void begin(uint32_t baud, int rxPin, int txPin) { init_serial(1 << txPin, 1 << rxPin, MCLK_FREQ / baud, (MCLK_FREQ / baud) >> 8); } /** * putS - writes the string s to the serial output stream without a trailing * newline character. Note: this differs from the ISO standard puts() * function which appends a newline character. */ //static void puts(register const char *s) { void putS(const char *s) { while(*s) putchar(*s++); } Makefile: CC=msp430-gcc CFLAGS=-Os -Wall -g -mmcu=msp430g2553 usci_serial.c #no, that's not really the proper way to do it, but it works! OBJS=main.o all: $(OBJS) $(CC) $(CFLAGS) -o main.elf $(OBJS) %.o: %.c $(CC) $(CFLAGS) -c $< clean: rm -fr main.elf $(OBJS) Quote Link to post Share on other sites
Rickta59 589 Posted December 30, 2011 Share Posted December 30, 2011 By the way, what license is the code under? This code snippet is part of a bigger library I'm working on that will be a GPL compatible license if I ever release it. As far as running from a more accurate clock, you might find a post on here from oPossum that details how to steal the 12MHz clock signal from the LP FET and use it as an external clock. -rick Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.