yyrkoon 250 Posted January 20, 2013 Share Posted January 20, 2013 main.cpp /** A very basic implementation of hardware UART using oPossum's Tiny printf(). Link: http://forum.43oh.com/topic/1289-tiny-printf-c-version/ Code written by yyrkoon of forum.430h.com. To demonstrate UART, and printf() as a mechanism for Serial output used for debugging purposes. Following source is very trivial, common knowlege, and therefore open to public domain. **/ #include <msp430.h> #include "uart.h" void printf(char *, ...); // printf() output pointed to UART through putc() int main(void) { WDTCTL = WDTPW + WDTHOLD; // Disable watchdog. BCSCTL1 = CALBC1_1MHZ; // Load calibrated constants for 1Mhz operation. DCOCTL = CALDCO_1MHZ; // ... uart_initialize(); // Initialize UART printf("Hello Launchpad v1.5 world \n\r"); return 0; } uart.h #ifndef __UART_H #define __UART_H void uart_initialize(void); #endif uart.cpp #include <msp430.h> #define TXD BIT2 void uart_initialize(void) { P1SEL = TXD; P1SEL2 = TXD; UCA0CTL1 |= UCSSEL_2; // SMCLK UCA0BR0 = 104; // 1MHz 9600 UCA0BR1 = 0; // 1MHz 9600 UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1 UCA0CTL1 &= ~UCSWRST; // Initialize USCI state machine } void putc(unsigned byte) { while (!(IFG2 & UCA0TXIFG)); UCA0TXBUF = byte; } void puts(char *str) { while(*str) putc(*str++); } The source for oPossum's Tiny printf() must be downloaded separately on a user by user basis. Link provided in main.cpp. If you use / like his code, click the thanks button on his first post. Once Tiny printf() is obtained, simply add it and the source above ( uart.h, and uart.cpp ) into your project. #include uart.h in main cpp. Then finally make sure the printf() prototype is declared just above main. As shown in main.cpp. This source was compiled using Energia, with the Energia framework bypassed( gcc-msp430.). It works as intended. RX /TX jumpers on the launchpad should probably be set into hardware configuration. Binary sketch size: 711 bytes (of a 16,384 byte maximum). I do have plans on reducing code size on target in the future, but for now this is how the code stands. I just needed a simple method to debug code, since Energia's Serial.print() methods require too much memory for my own taste. Anyway, maybe this will help others as well. Quote Link to post Share on other sites
jazz 209 Posted January 20, 2013 Share Posted January 20, 2013 Just note that (if there is no 9600 bps Launchpad limitation) emulated uart can go over 115200 bps, and real uart til 921600 bps. 9600 bps for real emulation is too slow. My debugging way (on lower level) is explained here: http://forum.43oh.com/topic/2646-software-debugging-without-hardware-tools/ Quote Link to post Share on other sites
yyrkoon 250 Posted January 20, 2013 Author Share Posted January 20, 2013 Yes, I was looking at your post, and oPossum's for same related material. It bothers me to hard code things like this, *but* this keeps it simple, and short. However, what does your ASM debugger have to do with being simple ? oPossum already wrote assembly source for printf() to use uart output mentioned on the Tiny printf() page. What I have here, is meant more as beginner learning code, that just so happens to provide some useful functionality. jsolarski 1 Quote Link to post Share on other sites
yyrkoon 250 Posted February 12, 2013 Author Share Posted February 12, 2013 Here is yet another text only implementation. However, you can use itoa() to convert integers to string format, which in return can then be printed. This does however add some overhead, but is the smallest I have yet tested. Using oPossums Tiny printf() may even yield smaller results. With the above said, with the G2553 MSP430's "tiny" is not necessarily needed, but is a good exercise in learning how to reduce code size as much as possible ( for when it *is* needed ). Inspired by Rickta59's Serial / UART code in Energia. In fact, the Initialization code is nearly all his. This is a C++ template class implementation however. Code size on target. Writing 186 bytes at c000... Writing 32 bytes at ffe0... Done, 218 bytes total hw_serial.h namespace uart { template <uint32_t BAUD, uint32_t MCLK_HZ> struct UART { void Initialize(void); void Write(char ch); void Write(char *str); void Read(void); }; template <uint32_t BAUD, uint32_t MCLK_HZ> void UART<BAUD, MCLK_HZ>::Initialize(void) { const uint32_t baud_rate_20_bit = (MCLK_HZ + (BAUD >> 1)) / BAUD; P1SEL |= BIT1 | BIT2; P1SEL2 |= BIT1 | BIT2; UCA0CTL1 = UCSWRST; UCA0CTL0 = 0; UCA0BR1 = (baud_rate_20_bit >> 12) & 0xFF; UCA0BR0 = (baud_rate_20_bit >> 4) & 0xFF; UCA0MCTL = ((baud_rate_20_bit << 4) & 0xF0) | UCOS16; UCA0CTL1 = UCSSEL_2; } template <uint32_t BAUD, uint32_t MCLK_HZ> void UART<BAUD, MCLK_HZ>::Write(char ch) { while(!(IFG2 & UCA0TXIFG)); UCA0TXBUF = ch; } template <uint32_t BAUD, uint32_t MCLK_HZ> void UART<BAUD, MCLK_HZ>::Write(char *str) { while(*str) { Write(*str++); } } } // end namespace #include <msp430.h> #include <stdint.h> #include "hw_serial.h" using namespace uart; UART<9600, 1000000> Serial; int main(void) { WDTCTL = WDTPW | WDTHOLD; DCOCTL = 0; DCOCTL = CALDCO_1MHZ; BCSCTL1 = CALBC1_1MHZ; BCSCTL2 = DIVS_0; Serial.Initialize(); Serial.Write("Hello World \r\n"); return 0; } chicken 1 Quote Link to post Share on other sites
yyrkoon 250 Posted February 14, 2013 Author Share Posted February 14, 2013 So the last example had no number printing capabilities, and was text only. I did some more testing last night, but with numbers. After fooling around with several different approaches ( including embedding opossums printf() code into my template class ). I settled for just adding a simple Write() overload which takes a value , and base parameter. Yes, that means with this code below, you can print any numerical value, with any base type that itoa() can take. Granted, there are a few caveats here. such as there is no built in type checking really ( other than that which is provided by the compiler ), and there is no variable length checking / testing. This code as such implies that you the user of the code knows what you're doing. If you really want to break it, then it should not be too hard. If you use it correctly though, it so far is the smallest / most efficient way I have tested up to this point. Granted with C, or ASM you may be able to write code that is slightly smaller, but I am pretty sure at this point I am bumping up against diminishing returns. Considering an empty main file with just the main method, and including msp430.h is 107 bytes in size. . . this is truly very tiny. Next I plan on implementing a way to "format" output without writing multiple blocks of code for a few separate variables( on the same line of code ). I have a few ideas, but have no idea where this may lead for optimal code size / efficiency. Size on target: Writing 426 bytes at c000... Writing 32 bytes at ffe0... Done, 458 bytes total hw_serial.h namespace uart { template <uint32_t BAUD, uint32_t MCLK_HZ> struct UART { void Initialize(void); void Write(char ch); void Write(char *str); void Write(uint16_t value, uint8_t base); }; template <uint32_t BAUD, uint32_t MCLK_HZ> void UART<BAUD, MCLK_HZ>::Initialize(void) { const uint32_t baud_rate_20_bit = (MCLK_HZ + (BAUD >> 1)) / BAUD; P1SEL |= BIT1 | BIT2; P1SEL2 |= BIT1 | BIT2; UCA0CTL1 = UCSWRST; UCA0CTL0 = 0; UCA0BR1 = (baud_rate_20_bit >> 12) & 0xFF; UCA0BR0 = (baud_rate_20_bit >> 4) & 0xFF; UCA0MCTL = ((baud_rate_20_bit << 4) & 0xF0) | UCOS16; UCA0CTL1 = UCSSEL_2; } template <uint32_t BAUD, uint32_t MCLK_HZ> void UART<BAUD, MCLK_HZ>::Write(char ch) { while(!(IFG2 & UCA0TXIFG)); UCA0TXBUF = ch; } template <uint32_t BAUD, uint32_t MCLK_HZ> void UART<BAUD, MCLK_HZ>::Write(char *str) { while(*str) { Write(*str++); } } template <uint32_t BAUD, uint32_t MCLK_HZ> void UART<BAUD, MCLK_HZ>::Write(uint16_t value, uint8_t base) { char buffer[16]; itoa(value, buffer, base); Write(buffer); } } // end namespace main.cpp #include <msp430.h> #include <stdlib.h> #include "hw_serial.h" using namespace uart; UART<9600, 1000000> Serial; int main(void) { WDTCTL = WDTPW | WDTHOLD; DCOCTL = 0; DCOCTL = CALDCO_1MHZ; BCSCTL1 = CALBC1_1MHZ; BCSCTL2 = DIVS_0; Serial.Initialize(); Serial.Write("Hello world\r\n"); Serial.Write(1234, 10); return 0; } Quote Link to post Share on other sites
yyrkoon 250 Posted February 15, 2013 Author Share Posted February 15, 2013 So, I have been shown code that is at least 160 bytes smaller. So apparently I was not quite near diminishing returns. Ah, well I will keep on experimenting and will let this person name them self, and show their own code when they are ready. A bit of a teaser though, which I must say is very encouraging. Writing 244 bytes at c000... Writing 32 bytes at ffe0... Done, 276 bytes total output: Hello world 10011010010 // 1234 in base 2 . . . 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.