
kff2
-
Content Count
63 -
Joined
-
Last visited
-
Days Won
3
Reputation Activity
-
kff2 got a reaction from bluehash in MTK3339 GPS
The old version of MTK3339 has built-in datalogging also (a total of 128K of data, about 8000 positions if I remember correctly). I tried it a few weeks ago, worked fine. See
http://www.auroramobile.ru/content/files/pdf/gtop/locus/gtop_locus_library_user_manualv12.pdf
-
kff2 got a reaction from ike in TIDeals : April 17th - Chronos Watch.
For those of you considering getting a Chronos watch, please keep in mind that
1) This watch is not waterproof. Apparently, the "30m waterproof" classification, as advertised in TI's literature, really means "can get an occasional splash". The watch will leak if you swim or take a shower in it.
2) This being an experimental device, there is absolutely no warranty provided by either TI or DigiKey.
See this post for more details:
http://forum.43oh.com/topic/2142-bad-experience-with-a-ti-deals-purchase/
-
kff2 got a reaction from bluehash in cc112x RF chips
Has anyone else noticed the relatively new cc112x RF chips? They are similar to cc1101, but have much better specs: 129dbm receiver sensitivity for cc1125, and 16dbm output power.
http://www.ti.com/product/cc1125
TI claims > 25 km line of sight range at 1.2kbps, all while using under 50mA of TX power and with non-directional antennas. The post below hints that the range could be improved to over 100 km by adding cc1190 (a PA / LNA).
http://e2e.ti.com/support/low_power_rf/f/155/t/206584.aspx
The chips are reasonably priced ($2 for 1120 and $5 for 1125). Unfortunately, I do not see modules based on these newer chips on either ebay or aliexpress. TI sells a kit for $299 that includes a pair of 1125 modules, but those work @ 869mhz only, which I believe disqualifies their use in the US. Modules based on 1120 do come in the 420-470mhz variety though.
http://www.ti.com/tool/cc1125dk
http://www.ti.com/tool/cc1120emk-420-470
Finally, there is another chip launching later this year: cc1200.
http://www.ti.com/product/cc1200
It appears to be similar to cc1125, but has higher maximum throughput (1000kbps vs 200kbps).
-
kff2 got a reaction from bluehash in CC2500 Breakout/BoosterPack
I finally figured out why the board could not be programmed with the radio attached to it. I previously removed all jumpers between the two halves of the Launchpad, assuming that the 0.05 mm header was connected directly to the programmer. This was wrong. It turns out that the VCC jumper needs to stay on. Surprisingly the board could still be programmed with VCC disconnected -- I am guessing there is enough current leakage from other pins. With a radio module attached, and with the additional current that it needed, programming no longer worked.
To summarize -- when using the 0.05 mm programming header, keep the VCC jumper on. The other jumpers can be removed.
-
kff2 got a reaction from tripwire in Using the internal temperature sensor
One gram of water is actually A LOT of matter: one cubic centimeter to be precise. By comparison, the entire MSP430 silicone layer is probably on the order of 2 mm x 2 mm x a few microns, which could be 1 / 100,000th of a gram. The power dissipates through the plastic of course, but you could still have significant temperature gradients as you get closer to the die (consider our planet -- molten rock inside, but relatively cold on the outside).
The reason I think heat is responsible for inaccurate temperature readings is that I tried oPossum's code and noticed that temperatures are fairly accurate when I first turn on the launchpad, but then go up 10-20 degrees over time. It's hard to imagine anything else causing this shift.
-
kff2 reacted to RobG in TI MSP430 Family Eagle library
I was little annoyed by the existing MSP430 libraries so I decided to modify them and create my own.
Here's my first version of it. Let me know what you think.
Updated May 7 2012
Added 2210 & 2230
Updated May 16 2012
Added FR5739 (TSSOP 38)
Updated June 20 2012
Fixed two 14 pin devices. Pin 8 was assigned to pin 10 and vice versa.
Affected devices: MSP430G2X31-14PW14 and MSP430G2X21-14PW14
Updated March 26 2013
Added:
G2x55 TSSOP 38 DA
G2x44 TSSOP 38 DA
G2x44 PDIP 40 N
ti_msp430.lbr
-
kff2 got a reaction from cubeberg in [Group Buy-5][D]MTK3339 GPS + BoosterPack - $14.26
There is a newer version of MTK3339 available from the same seller on AliExpress:
http://www.aliexpress.com/item/FGPMMOPA6H-utilizes-the-MediaTek-new-generation-GPS-Chipset-MT3339/623742154.html
The main differences are size (new unit is 20% thinner: 4.7mm vs 6.2mm), pinout, and presence of an input for an external antenna. The module automatically switches between the built-in and external antennas. As far as I can tell, everything else is the same.
This is what Adafruit now sells as their "Ultimate GPS module":
http://www.adafruit.com/products/790
Datasheet:
http://www.adafruit.com/datasheets/GlobalTop-FGPMMOPA6H-Datasheet-V0A.pdf
-
kff2 got a reaction from bluehash in [Group Buy-5][D]MTK3339 GPS + BoosterPack - $14.26
There is a newer version of MTK3339 available from the same seller on AliExpress:
http://www.aliexpress.com/item/FGPMMOPA6H-utilizes-the-MediaTek-new-generation-GPS-Chipset-MT3339/623742154.html
The main differences are size (new unit is 20% thinner: 4.7mm vs 6.2mm), pinout, and presence of an input for an external antenna. The module automatically switches between the built-in and external antennas. As far as I can tell, everything else is the same.
This is what Adafruit now sells as their "Ultimate GPS module":
http://www.adafruit.com/products/790
Datasheet:
http://www.adafruit.com/datasheets/GlobalTop-FGPMMOPA6H-Datasheet-V0A.pdf
-
kff2 got a reaction from roadrunner84 in CC2500 Breakout/BoosterPack
I finally figured out why the board could not be programmed with the radio attached to it. I previously removed all jumpers between the two halves of the Launchpad, assuming that the 0.05 mm header was connected directly to the programmer. This was wrong. It turns out that the VCC jumper needs to stay on. Surprisingly the board could still be programmed with VCC disconnected -- I am guessing there is enough current leakage from other pins. With a radio module attached, and with the additional current that it needed, programming no longer worked.
To summarize -- when using the 0.05 mm programming header, keep the VCC jumper on. The other jumpers can be removed.
-
kff2 got a reaction from flying4fun in Time & Temperature on Nokia 7110
Some notes on compiling this with msp430-gcc.
1) To handle C++ templates in nokia7110tl.h, you need to a version of msp430-gcc with C++ support. If building from sources, configure with --enable_languages=c,c++. The packaged msp430-gcc in recent versions of Ubuntu should already be C++ enabled.
To compile C++ code, use msp430-g++ instead of msp430-gcc. You will likely get the error
ld: cannot find -lstdc++
This is because msp430-g++ links against the C++ standard library by default, which is not included with msp430-gcc. To work around, run msp430-g++ as follows:
msp430-g++ -mmcu=msp430g2452 -o time.elf main.cpp -nodefaultlibs -fno-rtti -fno-exceptions -lc -lgcc
This tells msp430-g++ not to use default libraries. You then need to explicitly list the default libraries that you need (-lc and -lgcc). -fno-rtti -fno-exceptions eliminates some C++ bloat and reduces code size.
2) time.h is not included with msp430-gcc. I used this version which I found somewhere on the internet:
typedef long time_t; /* for representing times in seconds */ struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; const unsigned char moninit[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static unsigned dylen(unsigned yr) { if(yr%4 || (yr % 100) == 0 && ((yr+300)%400) != 0) return(365); return(366); } struct tm * gmtime(time_t * tp) { time_t t; static struct tm tim; t = *tp; tim.tm_sec = t % 60L; t /= 60L; tim.tm_min = t % 60L; t /= 60L; tim.tm_hour = t % 24L; tim.tm_mday = t / 24L; tim.tm_wday = (tim.tm_mday + 4) % 7; tim.tm_year = 70; while(tim.tm_mday >= dylen(tim.tm_year)) { tim.tm_mday -= dylen(tim.tm_year); tim.tm_year++; } tim.tm_yday = tim.tm_mday; if(tim.tm_mday >= 59 && dylen(tim.tm_year) == 366) tim.tm_mday--; tim.tm_mon = 0; while(tim.tm_mday >= moninit[tim.tm_mon]) { tim.tm_mday -= moninit[tim.tm_mon]; tim.tm_mon++; } tim.tm_mday++; return &tim; } struct tm * localtime(time_t * tp) { time_t t; t = *tp - 7 * 3600L; return gmtime(&t); }
Notice that there is no mktime function here. Instead, in main.cpp, I set tt to the current unix timestamp (obtained with "date +%s" in Linux). Also, the localtime function here hardcodes my timezone. Change accordingly.
3) This original code causes the LCD to go berserk when compiled under msp430-gcc:
for(c = 32; c < 128; ++c) { lcd.print(c); if(++x >= 16) x = 0, lcd.pos(x, ++y); }
That's because c is a signed byte value, and the compiler treats 128 as -1, causing an infinite loop. What you want instead is
for(c = 32; c < 128U; ++c) { lcd.print(c); if(++x >= 16) x = 0, lcd.pos(x, ++y); }
Hope this will be useful to somebody.
-
kff2 got a reaction from spirilis in msp430-gcc 4.6.3 signed overflow questions
The results that you are seeing are not necessarily wrong. Signed integer overflow is not well defined in C or C++. It looks like gcc wraps around overflown integers in some cases and saturates them in others. Inconsistent, but not really a bug either.
See
http://thiemonagel.de/2010/01/signed-integer-overflow/
You can try the -fwrapv option mentioned in the article.
-
kff2 got a reaction from RobG in CC2500 Breakout/BoosterPack
So I think I have figured it out. When resting the board on the launchpad header, the radio comes into close contact (~1") with the 12mhz crystal on the launchpad used for USB. The radio somehow causes interference and USB communication goes haywire. My workaround is to to solder a female 0.05 header to the board and a male 0.05 header to the radio, and to remove the radio whenever I need to program the board. I guess I could also make a cable to connect the board to the launchpad.
-
kff2 got a reaction from bluehash in [S]IV-18 VFD Clock Booster Pack
I assembled my kit today. It works beautifully. Many thanks to Cubeberg for designing the board, sourcing the components, and making the booster pack available to all of us.
I managed to compile the source code for the clock with mspgcc. If anyone is interested, I only had to make a few changes to the original code:
1) Change isr declarations:
//#pragma vector=TIMER0_A0_VECTOR //__interrupt void TIMER0_A0_ISR(void) ISR(TIMER0_A0, ISR1) //#pragma vector=USCIAB0RX_VECTOR //__interrupt void USCI0RX_ISR(void) ISR(USCIAB0RX, ISR2) //#pragma vector=TIMER1_A0_VECTOR //__interrupt void TIMER1_A0_ISR(void) ISR(TIMER1_A0, ISR3)
You will need to include for the ISR macro.
2) Replace the two occurrences of _delay_cycles with __delay_cycles (note the extra underscore).
3) Since there is no _bcd_add_short() intrinsic in mspgcc, I added one manually to time.h:
static inline unsigned short _bcd_add_short(register unsigned short bcd_a, register unsigned short bcd_ { __asm__( " clrc \n" " dadd %[bcd_b],%[bcd_a] \n" : [bcd_a] "+ro" (bcd_a) : [bcd_b] "g" (bcd_); return bcd_a; } 4) In write(), send1 and send2 variables need to be unsigned char instead of char. Otherwise the line
send1 = 1<<(7 -digit);
causes an overflow for the first digit (PM dot always stays on).
To compile:
msp430-gcc -mmcu=msp430g2553 -oS -o clock.elf main.c
You can then upload the binary with
mspdebug rf2500 "prog clock.elf"
Hope this helps somebody.
-
kff2 got a reaction from cubeberg in [S]IV-18 VFD Clock Booster Pack
I assembled my kit today. It works beautifully. Many thanks to Cubeberg for designing the board, sourcing the components, and making the booster pack available to all of us.
I managed to compile the source code for the clock with mspgcc. If anyone is interested, I only had to make a few changes to the original code:
1) Change isr declarations:
//#pragma vector=TIMER0_A0_VECTOR //__interrupt void TIMER0_A0_ISR(void) ISR(TIMER0_A0, ISR1) //#pragma vector=USCIAB0RX_VECTOR //__interrupt void USCI0RX_ISR(void) ISR(USCIAB0RX, ISR2) //#pragma vector=TIMER1_A0_VECTOR //__interrupt void TIMER1_A0_ISR(void) ISR(TIMER1_A0, ISR3)
You will need to include for the ISR macro.
2) Replace the two occurrences of _delay_cycles with __delay_cycles (note the extra underscore).
3) Since there is no _bcd_add_short() intrinsic in mspgcc, I added one manually to time.h:
static inline unsigned short _bcd_add_short(register unsigned short bcd_a, register unsigned short bcd_ { __asm__( " clrc \n" " dadd %[bcd_b],%[bcd_a] \n" : [bcd_a] "+ro" (bcd_a) : [bcd_b] "g" (bcd_); return bcd_a; } 4) In write(), send1 and send2 variables need to be unsigned char instead of char. Otherwise the line
send1 = 1<<(7 -digit);
causes an overflow for the first digit (PM dot always stays on).
To compile:
msp430-gcc -mmcu=msp430g2553 -oS -o clock.elf main.c
You can then upload the binary with
mspdebug rf2500 "prog clock.elf"
Hope this helps somebody.
-
kff2 got a reaction from bluehash in Uni-T UT61E for ~$50
I got this multimeter today and I must say I am impressed. Very decent build quality, and the accuracy is excellent. 2.499V on Maxim 6225 precision reference (2.5V) and 5.000 on 6250 (5.0V). 0.1% tolerance resistors all read spot on. Capacitance was about 2% lower compared to my Fluke 87V.
I did have to tweak calibration a little bit to get these values. There is an easily accessible trimpot inside for adjusting voltage. Originally I was getting 2.497 for 2.5V (which is still within spec).
There is a serial port that spits out about 4 values per second. I used this open source program https://github.com/stv0g/dmm_ut61e to read the data.
Overall, it's really not bad for $50. I still prefer my Fluke, of course, but it also costs 6 times as much. This multimeter is definitely better than Extech 330, which I bought from Amazon about a month ago and promptly sent back.
If anyone has a question about the multimeter, please let me know.
-
kff2 reacted to PedroDaGr8 in Uni-T UT61E for ~$50
I posted this over at fatwallet. Link
Depending on if you use fatwallet or not the price is either $51.29 or $48.73 (after Fatwallet CashBack).
As for the devices capabilities:
[*:3uwr7ymh]Auto-Ranging! (No more setting the range)
[*:3uwr7ymh]TrueRMS! (I think this is one of the better prices I have seen for a TrueRMS meter, let alone with auto-ranging)
[*:3uwr7ymh]Voltage: The lowest range is 220mV highest is 750V
[*:3uwr7ymh]DC Voltage: The lowest range is 220mV highest is 1000V
[*:3uwr7ymh] AC Current: The lowest range is 220uA (thats micro amp I am suspicious of this) highest is 10A.
[*:3uwr7ymh]DC Current: The lowest range is 220uA (thats micro amp I am suspicious of this) highest is 10A.
[*:3uwr7ymh]Resistance: The lowest range appears to be 22ohm highest is 220megaohm
[*:3uwr7ymh]Capacitance: The lowest range is 22nF and the highest usable range is 220uF/2.2mF (there is a 22mF to 220mF range but it is listed as reference only)
[*:3uwr7ymh]Frequency: 10Hz-220Mhz
[*:3uwr7ymh]Duty Cycle: 0.1%-99.9%
[*:3uwr7ymh]Data Hold: 61 (points?)
[*:3uwr7ymh]Diode Test
[*:3uwr7ymh]Continuity Buzzer
[*:3uwr7ymh]RS232 Interface (the description talks a lot about a USB interface but the image clearly shows an RS232 cable).
[*:3uwr7ymh]A stand for sitting it up on a table top.
[*:3uwr7ymh]Plus some other stuff.
My impressions of this meter:
Having owned this meter for 6months now, I absolutely love it still. It is one of the best (if not the best) meter at this price point. The quality and feel are outright outstanding (excluding the test leads). The case has a nice slightly rubberized feel but more importantly the case is THICK and rigid. I couldn't get the case to flex one bit even with all of the screws removed. The rotary dial on the front has a nice solid feel as clicks solidly into the different spots, especially the off slot which has a nice Ka-Chunk. The meter is large, I'd say its about as long as a DVD case give or take. So similar if not a bit larger than meters like Fluke, Amprobe or Extech. My meter came with both an English and a chinese manual (two seperate manuals). The leads as mentioned aren't the greatest. Mine show ~120mOhms resistance. They feel kind of cheap, though they do have a REALLY sharp tip. I can't find much at all to complain about it. It really is an amazing meter for a hobbyist.
I did a test on this meters specs using some high quality KOASpeer resistors and UCC caps. The meter meets/beats all of its rated specs (which are actually quite good).
EEVBlog forum like this meter as well as great for the price. Link
Here is a link to the meter for $56.99 Then enter coupon code cheaper10off to knock 10% off the price. If you use the link I posted above at FW, you can use FW Cashback to get 5% back bringing the price down to the under $49.
EDIT: OK now with 3 posts under my belt I can add links Hopefully, someone else finds this meter as great as I have.
-
kff2 got a reaction from RobG in FM radio
Thanks for sharing this. A good place for buying TEA5767 is suntekstore.com -- about $10 for 10 pcs (after a $2 off coupon), or one for $2.16, with free shipping. Everything on ebay is more expensive. I ordered a couple to play with.
Does anyone know of a similar chip that can also do AM?
-
kff2 reacted to RobG in MTK3339 GPS
Sample code to display GPS fix data from the MTK3339 GPS module (group buy #5.)
Raw:
Parsed:
updated 6/30
GPS-630.zip
-
kff2 reacted to oPossum in Pronto IR code transmission
Pronto CIR codes have become a de facto standard for CIR code exchange. They where originally used by the Philips Pronto series of universal remotes, but are now supported by many universal remotes and PC CIR software. There is a database of codes at Remote Central.
This MSP430 firmware will send most type 0 pronto codes - that is the most common type. It can run at 1 MHz, so it can be used with the G2231 and G2211 chips that came with the rev 1.4 and earlier Launchpad.
The pronto codes have the timing in units of the IR carrier frequency. This characteristic allows the firmware to work with only one Timer A unit. It is used for both carrier generation with hardware PWM and for timing with an ISR.
/* Copyright (C) 2012 Kevin Timmerman This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Pronto code format http://www.remotecentral.com/features/irdisp2.htm Pronto code database http://www.remotecentral.com/cgi-bin/codes/ */ #include static const unsigned pentax[] = { // - Pentax camera IR code in Pronto format // http://img811.imageshack.us/img811/6074/pentaxprotocol.jpg 0, // Modulated code 109, // Carrier period - 4,145,152 / 38 kHz = 109 8, // Number of one time pairs 0, // Number of repeat pairs 500, 115, // 13 ms on, 3 ms off 38, 39, // 1 ms on, 1 ms off 38, 39, // 1 ms on, 1 ms off 38, 39, // 1 ms on, 1 ms off 38, 39, // 1 ms on, 1 ms off 38, 39, // 1 ms on, 1 ms off 38, 39, // 1 ms on, 1 ms off 38, 39 // 1 ms on, 1 ms off }; // - Canon camera IR code in Pronto format // http://www.doc-diy.net/photo/rc-1_hacked/index.php static const unsigned canon_immed[] = { // Immediate shutter release 0, // Modulated code 127, // 32.768 kHz 16, // Number of one time pairs 0, // Number of repeat pairs 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240, // 488 us on, 7332 us off 16, 240 // 488 us on, 7332 us off }; // static const unsigned canon_delay[] = { // Delayed shutter release 0, // Modulated code 127, // 32.768 kHz 16, // Number of one time pairs 0, // Number of repeat pairs 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176, // 488 us on, 5372 us off 16, 176 // 488 us on, 5372 us off }; static volatile int cycle_count; // IR carrier cycle count #pragma vector = TIMERA0_VECTOR // Timer A Capture/Compare 0 interrupt __interrupt void timera0_isr(void) // This ISR must be very simple because there may // be as few as 16 cycles available for it to complete { // if(--cycle_count == 0) // Decrement cycle count __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code when count reaches 0 } // inline void send_chunk(const unsigned *code, unsigned len) { do { // Do all pairs __bis_SR_register(LPM0_bits + GIE); // Sleep until cycle count is zero TACCTL1 = OUTMOD_7; // Turn on IR cycle_count += *code++; // Set on cycle count __bis_SR_register(LPM0_bits + GIE); // Sleep until cycle count is zero TACCTL1 = OUTMOD_0; // Turn off IR cycle_count += *code++; // Set off cycle count } while(--len); // Decrement pair count, do next pair if not zero } void send_code(const unsigned *code, unsigned repeat) { if(*code++) return; // First word must be 0 (0 = Modulated code) // Pronto carrier freq == 4,145,152 / N // ( 4,145,152 == 32,768 * 506 / 4 ) // 1,000,000 / 4,145,152 == 0.241246 // 0.241246 << 16 == 15,810 const unsigned period = (((unsigned long)*code++ * 15810) + 7905) >> 16; // For 1 MHz MPU clock //const unsigned period = *code++; // For 4.145152 MHz MPU clock const unsigned one_len = *code++; // One time length const unsigned repeat_len = *code++; // Repeat length // TACCR0 = period - 1 ; // Set timer period TACCR1 = TACCR0 >> 1; // Set timer on duration - 50% duty cycle cycle_count = 16; // Setup some initial IR off lead in TACCTL0 = CCIE; // Enable Timer A CC0 interrupt // if(one_len) send_chunk(code, one_len); // Send one time chunk // if(repeat_len) { // Send repeat chunk code += (one_len << 1); // Move pointer to repeat chunk while(repeat) { // Do it repeat times send_chunk(code, repeat_len); // Send it --repeat; // Decrement repeat count } // } // // __bis_SR_register(LPM0_bits + GIE); // Sleep until cycle count is zero TACCTL0 = 0; // Turn off Timer A CC0 interrupt } void main(void) { WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset DCOCTL = 0; // Run at 1 MHz BCSCTL1 = CALBC1_1MHZ; // Note: The ideal clock freq for this code DCOCTL = CALDCO_1MHZ; // is 4.145152 MHz - calibrate the DCO for // that freq for optimal timing - the second // word of the pronto code can then be used // without scaling P1OUT = 0; // P1DIR = BIT6; // Enable PWM output on P1.6 P1SEL = BIT6; // // TACTL = TASSEL_2 | MC_1; // Timer A config: SMCLK, count up TACCTL1 = OUTMOD_0; // Make sure IR is off // _enable_interrupts(); // Enable global interrupts // send_code(pentax, 1); // Send Pentax IR code // send_code(canon_immed, 1); // // send_code(canon_delay, 1); // }
-
-
kff2 got a reaction from GeekDoc in Generating image bitmaps for Nokia 7110 LCD
In oPossum's Time and Temperature project, there is an embedded TI logo that rolls across the screen when the program starts. Here is how you can generate bitmaps from your own images. You will need ImageMagick and python.
1) Resize your image to fit on the LCD. The maximum height is 96 pixels. The height should be a multiple of 8. Also, you will want to stretch your image horizontally a little bit, as pixels on 7110 aren't square.
I did something along the lines of
convert -resize 43x48! logo.jpg logo2.png
The exclamation point here tells ImageMagick to go ahead and change the aspect ratio of the image; otherwise it will set width (or height) automatically to maintain the original aspect ratio.
2) Convert the image to 1-bit black and white and save it in RGB format
convert -monochrome -depth 1 -type Bilevel +dither logo2.png logo3.rgb
The resulting file contains 3 bits for each pixel (red, green, and blue channels). The 3 bits are the same. 0 corresponds to black and 1 to white.
I wrote a short python script to convert RGB files to the format expected by nokia7110tl.h's bitmap() function:
import sys import string x = int(sys.argv[2]) y = int(sys.argv[3]) img = open(sys.argv[1]).read() b = string.join([str(int(ord(img[i*3/8]) & (1<<(7-(i*3%8))) == 0)) for i in xrange(x*y)], '') # b is a string that contains a 0 or 1 for each pixel of the image. If you now set # the screen size to 'width' and print b, you should see your image in ascii. # The next step is to rearrange the bits in b to match the # addressing expected by 7110's controller. b2 = '' for i in reversed(xrange(y/8)): for j in xrange(x): b2 += string.join([b[(y-1-i*8-k)*x+j] for k in xrange(8)], '') print string.join(['0x%02x' % eval('0b'+b2[i*8:i*8+8]) for i in xrange(x*y/8)], ',')
You run it like this:
python convert_7110.py logo3.rgb 43 48
where 43 is the width and 48 is the height of the image. The script spits out an array of ascii bytes that you can copy and paste directly into your C program and pass to the bitmap() function.
-
kff2 reacted to oPossum in Time & Temperature on Nokia 7110
This uses code from Software RTC, Using the internal temperature sensor and Nokia 5110 template class. The code has been modified and refined a bit for this project.
main.cpp
#include #include #include #include #include "nokia7110tl.h" using namespace nokia7110; static const unsigned char ti[] = { 48, 48 / 8, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00,0x00,0xF0, 0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFC,0xFC,0x3C,0x80,0xFC,0xFD, 0xFD,0xFD,0x3D,0x81,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00, 0x00,0x1C,0x7C,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xF7,0xF0,0xF0,0x00,0xF0,0xFF,0xFF,0xFF, 0xFF,0x0F,0xF0,0xF0,0xF0,0xF0,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xF0,0x00,0x00, 0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x3F,0x7F,0xFF, 0xFF,0xFF,0xFF,0xFF,0x7F,0x3F,0x3F,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0x1F,0x00,0x1E,0x1F,0x1F,0x1F,0x1F, 0x01,0x1E,0x1F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x7F,0x7F,0x3F,0x3F,0x3F,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x03, 0x0F,0x3F,0xFF,0xFE,0xFE,0xFE,0xFC,0xFC,0xFC,0xFC, 0xFC,0xFC,0xFC,0x7F,0x1F,0x07,0x03,0x03,0x01,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0x07,0x0F,0x0F,0x1F,0x1F,0x3F, 0x3F,0x3F,0x3F,0x1C,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; /* P1.0 Reset P1.3 Temp Sensor (Not used) P2.0 Serial Data P2.1 Backlight P2.2 Chip Select P2.3 Data/Command P2.4 Serial Clock P2.5 Pushbutton */ // P1 static const unsigned LCD_RESET = BIT0; static const unsigned RXD = BIT2; static const unsigned SWITCH = BIT3; // P2 static const unsigned LCD_DATA = BIT0; static const unsigned LCD_BACKLIGHT = BIT1; static const unsigned LCD_CE = BIT2; static const unsigned LCD_DC = BIT3; static const unsigned LCD_CLK = BIT4; static const unsigned LCD_BTN = BIT5; Nokia7110 lcd; void show_time(const struct tm *t) // Show time on LCD { static const char *dow[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; int x, w; const char *s; div_t d; if(t->tm_hour < 10) { x = -2; lcd.fill(0, 0, 9, 2, 0); d.rem = t->tm_hour; } else { x = 3; lcd.fill(0, 0, 3, 2, 0); d = div(t->tm_hour, 10); lcd.pd12(d.quot, x, 0); } lcd.pd12(d.rem, x + 11, 0); lcd.pd12(11, x + 22, 0); d = div(t->tm_min, 10); lcd.pd12(d.quot, x + 27, 0); lcd.pd12(d.rem, x + 38, 0); lcd.pd12(11, x + 49, 0); d = div(t->tm_sec, 10); lcd.pd12(d.quot, x + 54, 0); lcd.pd12(d.rem, x + 65, 0); lcd.fill(x + 76, 0, 7 - x, 2, 0); if(t->tm_mon < 9) { x = -5; lcd.fill(0, 2, 6, 2, 0); d.rem = t->tm_mon + 1; } else { x = 0; d = div(t->tm_mon + 1, 10); lcd.pd12(d.quot, x, 2); } lcd.pd12(d.rem, x + 11, 2); lcd.pd12(13, x + 22, 2); d = div(t->tm_mday, 10); lcd.pd12(d.quot, x + 30, 2); lcd.pd12(d.rem, x + 41, 2); lcd.pd12(13, x + 52, 2); d = div(t->tm_year % 100, 10); lcd.pd12(d.quot, x + 60, 2); lcd.pd12(d.rem, x + 71, 2); if(x) lcd.fill(x + 82, 2, 1 - x, 2, 0); s = dow[t->tm_wday]; w = strlen(s) * 6; x = (83 - w) >> 1; lcd.fill(0, 4, x, 1, 0); lcd.print((unsigned char)x, (unsigned char)4, s); x += w; lcd.fill(x, 4, 83 - x, 1, 0); } // Print integer from -999 to 9999 using 12 x 16 font void print_int(int i, const unsigned y) { if(i < -999 || i > 9999) return; const unsigned neg = i < 0; if(neg) i = -i; div_t d; d.quot = i; unsigned x = 48; do { d = div(d.quot, 10); lcd.pd12(d.rem, x -= 12, y); } while(d.quot); if(neg) lcd.pd12(14, x -= 12, y); while(x) lcd.pd12(10, x -= 12, y); } // Print integer from -999 to 9999 using 6 x 8 font void print_int(int i, unsigned x, const unsigned y) { if(i < -999 || i > 9999) return; const unsigned e = x; x += 24; const unsigned neg = i < 0; if(neg) i = -i; div_t d; d.quot = i; do { d = div(d.quot, 10); lcd.print(x -= 6, y, '0' + d.rem); } while(d.quot); if(neg) lcd.print(x -= 6, y, '-'); while(x > e) lcd.print(x -= 6, y, ' '); } void draw_bargraph(int f) { int x, y, bg; char c; unsigned char bgc[9] = { 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF, 0 }; char *bgl[8] = { "90", "80", "70", "60", "50", "40", "30", "20" }; x = 20; // bg = (f - (x - 5)) * 4 / 5; // for(y = 7; y >= 0; --y) { // lcd.pos(83, y); // if(bg < 0) { c = bgc[8]; } else if (bg > 7) { c = bgc[7]; } else { c = bgc[bg]; } lcd.write((unsigned char *)&c, 1, lcd_data); lcd.print(bgl[y], c); bg -= 8; } // } time_t tt; // Time in seconds since epoch unsigned adc; // ADC value #pragma vector = WDT_VECTOR // - Watchdog timer interrupt vector __interrupt void wdt_isr(void) // This interrupt will occur once per second { // ++tt; // Increment time_t __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } // // #pragma vector = ADC10_VECTOR // ADC conversion complete interrupt __interrupt void ADC10_ISR(void) // { // adc = ADC10MEM; // Read ADC __bic_SR_register_on_exit(LPM0_bits); // Wakeup main code } // int main(void) { int x, y; char c; struct tm ts; int dc, dk, df; // Temperature in degrees C, K, and F WDTCTL = WDTPW | WDTHOLD; P1REN = RXD | SWITCH; P1DIR = LCD_RESET; P1OUT = RXD | SWITCH | LCD_RESET; P2DIR = LCD_DC | LCD_CE | LCD_CLK | LCD_BACKLIGHT | LCD_DATA; P2REN = LCD_BTN; P2OUT = LCD_CLK | LCD_DC | LCD_CE | LCD_BACKLIGHT | LCD_BTN; ADC10CTL0 = 0; // Configure ADC ADC10CTL1 = INCH_10 | ADC10DIV_3; // ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE; ADC10CTL0 |= ADC10IE; // Enable ADC conversion complete interrupt // // 32 kHz xtal loading //BCSCTL3 = XCAP_1; // 6 pF (default) BCSCTL3 = XCAP_2; // 10 pF //BCSCTL3 = XCAP_3; // 12.5 pF // WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL; // Use WDT as interval timer IE1 |= WDTIE; // Enable WDT interrupt _EINT(); // Enable interrupts // // Set initial time - there is no UI for this ts.tm_hour = 13; // Hour ts.tm_min = 37; // Minute ts.tm_sec = 42; // Second ts.tm_mon = 3; // Month (0 based!) ts.tm_mday = 20; // Day of Month ts.tm_year = 2012 - 1900; // Year ts.tm_wday = 5; // Day of Week - Not used by mktime() ts.tm_yday = 0; // Not used by mktime() ts.tm_isdst = 0; // DST flag - Not used by rtc_tick() // tt = mktime(&ts); // Convert tm struct to time_t // lcd.reset(); lcd.init(); lcd.clear(); lcd.print(30, 6, "MSP430"); lcd.print(18, 7, "Nokia 7110"); for(x = -47; x < 24; ++x) { lcd.bitmap(ti, x, 0); __delay_cycles(20000); } __delay_cycles(2000000); for(; x < 96; ++x) { lcd.bitmap(ti, x, 0); __delay_cycles(20000); } lcd.clear(); lcd.print(9, 7, "Character Set"); x = y = 0; lcd.pos(x, y); for(c = 32; c < 128; ++c) { lcd.print(c); if(++x >= 16) x = 0, lcd.pos(x, ++y); } __delay_cycles(3000000); lcd.clear(); ts.tm_hour = ts.tm_min = ts.tm_sec = 88; ts.tm_mon = 87; ts.tm_mday = ts.tm_year = 88; ts.tm_wday = 5; show_time(&ts); lcd.pd12(15, 48, 5); lcd.pd12(17, 59, 5); lcd.print(24, 7, "\x7F""C"); lcd.print(66, 7, "\x7F""K"); print_int(8888, 5); print_int(8888, 0, 7); print_int(8888, 42, 7); for(x = 15; x < 96; ++x) draw_bargraph(x); __delay_cycles(3000000); lcd.clear(); // lcd.pd12(15, 48, 5); // Degrees lcd.pd12(17, 59, 5); // F lcd.print(24, 7, "\x7F""C"); // C lcd.print(66, 7, "\x7F""K"); // K // for(; { // for-ever show_time(localtime(&tt)); // Convert time_t to tm struct and show on LCD __bis_SR_register(LPM0_bits + GIE); // Sleep until WDT interrupt ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion __bis_SR_register(LPM0_bits + GIE); // Sleep until conversion complete // // Convert to temperature dc = ((27069L * adc) - 18169625L) >> 16; // Vref = 1.5V dk = ((27069L * adc) - 268467L) >> 16; // Vref = 1.5V df = ((48724L * adc) - 30634388L) >> 16; // Vref = 1.5V // // Display on LCD print_int(df, 5); // Degrees F print_int(dc, 0, 7); // Degrees C print_int(dk, 42, 7); // Degrees K draw_bargraph(df); // Deg F Bargraph } // return 0; }
nokia7110tl.h - C++ template class for Nokia 7110 (SED1565)
namespace nokia7110 { unsigned char PNONE; typedef enum { lcd_command = 0, // Array of one or more commands lcd_data = 1, // Array of one or more bytes of data lcd_data_repeat = 2 // One byte of data repeated } lcd_cmd_type; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> struct Nokia7110 { void write(const unsigned char *cmd, unsigned len, const lcd_cmd_type type = lcd_data); void reset(void); void init(void); void home(void); void pos(unsigned char x, unsigned char y); void clear(unsigned char x = 0); void fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z); void bitmap(const unsigned char *bmp, signed char x, signed char y, unsigned char w, unsigned char h); inline void bitmap(const unsigned char *bmp, signed char x, signed char y) { bitmap(bmp + 2, x, y, bmp[0], bmp[1]); }; void print(char c); inline void print(unsigned char x, unsigned char y, char c) { pos(x, y); print(c); }; void print(const char *s); inline void print(unsigned char x, unsigned char y, const char *s) { pos(x, y); print(s); }; void print(const char *s, unsigned char m); void printv(unsigned char x, unsigned char y, char *s); void pd12(unsigned n, unsigned x, unsigned y); }; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::write(const unsigned char *cmd, unsigned len, const lcd_cmd_type type) { register unsigned mask; if(&_EP != &PNONE) _EP &= ~_CE; do { mask = 0x0080; do { if(*cmd & mask) { _SP |= _DATA; _SP &= ~_CLK; } else { _SP &= ~(_CLK | _DATA); } _SP |= _CLK; mask >>= 1; } while(!(mask & 1)); if(&_CP == &PNONE) { __delay_cycles(_DC); } else { if(!type) _CP &= ~_DC; } if(*cmd & mask) { _SP |= _DATA; _SP &= ~_CLK; } else { _SP &= ~(_CLK | _DATA); } _SP |= _CLK; if(&_CP != &PNONE) _CP |= _DC; if(!(type & 2)) ++cmd; } while(--len); if(&_EP != &PNONE) _EP |= _CE; } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::reset(void) { if(&_RP == &PNONE) { // --- Set initial state of CLK, DC and CE as needed // * = output used for reset if(&_CP == &PNONE) { if(&_EP != &PNONE) { // CLK*, DATA, CE _EP |= _CE; } // else // CLK*, DATA } else { if(&_EP != &PNONE) { // CLK, DATA, DC*, CE if(&_SP == &_EP) { _SP |= (_CLK | _CE); } else { _SP |= _CLK; _EP |= _CE; } } else { // CLK, DATA, DC* _SP |= _CLK; } } // --- Reset pulse on CLK or DC as needed if(&_CP == &PNONE) { // No DC port, use CLK to reset _SP &= ~_CLK; __delay_cycles(_RD); _SP |= _CLK; } else { // Use DC to reset _CP &= ~_DC; __delay_cycles(_RD); _CP |= _DC; } } else { _RP &= ~_RST; if(&_EP != &PNONE) { if(&_SP == &_EP) { _SP |= (_CLK | _CE); } else { _SP |= _CLK; _EP |= _CE; } } else { _SP |= _CLK; } __delay_cycles(_RD); _RP |= _RST; } __delay_cycles(_RD); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::init(void) { static const unsigned char init[] = { 0xA6, //Display: Normal 0xA3, //LCD Bias Settings: 1/7 0xA1, //ADC Selection: Reverse 0xC0, //Common Output: Normal Direction //0xC8, //Common Output: Upside Down 0x22, //Set the V5 output Voltage 0x81, //Set Electronic Volume Register 0x2E, //Power Controller Set // Booster circuit: ON // Voltage regulator circuit: ON // Voltage follower circuit: OFF 0x2F, //Power Controller Set // Voltage follower circuit: ON 0xE3, //Non-OPeration Command 0x40, //Set the start line 0xAF, //LCD On //0xA5, //Display All Points: ON 0xA4, //Display All Points: NORMAL }; write(init, sizeof(init), lcd_command); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::home(void) { static const unsigned char home[] = { 0xB0, 0x11, 0x02 }; write(home, sizeof(home), lcd_command); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::pos(unsigned char x, unsigned char y) { unsigned char c[3]; x += 18; c[0] = 0xB0 | y; c[1] = 0x10 | (x >> 4); c[2] = x & 0x0F; write(c, sizeof(c), lcd_command); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::clear(unsigned char x) { for(unsigned y = 0; y < 9; ++y) { pos(0, y); write(&x, 96, lcd_data_repeat); } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::fill(unsigned x, unsigned y, unsigned w, unsigned h, unsigned char z) { unsigned yy = y + h; unsigned char c[3]; x += 18; c[1] = 0x10 | (x >> 4); c[2] = (x & 0x0F); for(;y < yy; ++y) { c[0] = 0xB0 | y; write(c, sizeof(c), lcd_command); write(&z, w, lcd_data_repeat); } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::bitmap(const unsigned char *bmp, signed char x, signed char y, unsigned char w, unsigned char h) { unsigned char c[3]; unsigned char ww; if(x < 0) { ww = w + x; bmp -= x; x = 0; } else if(x + w >= 96) { ww = (96 - x); } else { ww = w; } x += 18; c[0] = 0xB0 | y; c[1] = 0x10 | (x >> 4); c[2] = x & 0x0F; while(h--) { write(c, sizeof(c), lcd_command); write(bmp, ww); bmp += w; ++c[0]; } } static const unsigned char font[96][5] = { 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x5F, 0x00, 0x00, // ! 0x00, 0x07, 0x00, 0x07, 0x00, // " 0x14, 0x7F, 0x14, 0x7F, 0x14, // # 0x24, 0x2A, 0x7F, 0x2A, 0x12, // $ 0x23, 0x13, 0x08, 0x64, 0x62, // % 0x36, 0x49, 0x56, 0x20, 0x50, // & 0x00, 0x08, 0x07, 0x03, 0x00, // ' 0x00, 0x1C, 0x22, 0x41, 0x00, // ( 0x00, 0x41, 0x22, 0x1C, 0x00, // ) 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // * 0x08, 0x08, 0x3E, 0x08, 0x08, // + 0x00, 0x40, 0x38, 0x18, 0x00, // , 0x08, 0x08, 0x08, 0x08, 0x08, // - 0x00, 0x00, 0x60, 0x60, 0x00, // . 0x20, 0x10, 0x08, 0x04, 0x02, // / 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 0x42, 0x61, 0x51, 0x49, 0x46, // 2 0x21, 0x41, 0x49, 0x4D, 0x33, // 3 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 0x27, 0x45, 0x45, 0x45, 0x39, // 5 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 0x41, 0x21, 0x11, 0x09, 0x07, // 7 0x36, 0x49, 0x49, 0x49, 0x36, // 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 0x00, 0x00, 0x14, 0x00, 0x00, // : 0x00, 0x00, 0x40, 0x34, 0x00, // ; 0x00, 0x08, 0x14, 0x22, 0x41, // < 0x14, 0x14, 0x14, 0x14, 0x14, // = 0x00, 0x41, 0x22, 0x14, 0x08, // > 0x02, 0x01, 0x51, 0x09, 0x06, // ? 0x3E, 0x41, 0x5D, 0x59, 0x4E, // @ 0x7C, 0x12, 0x11, 0x12, 0x7C, // A 0x7F, 0x49, 0x49, 0x49, 0x36, // B 0x3E, 0x41, 0x41, 0x41, 0x22, // C 0x7F, 0x41, 0x41, 0x41, 0x3E, // D 0x7F, 0x49, 0x49, 0x49, 0x41, // E 0x7F, 0x09, 0x09, 0x09, 0x01, // F 0x3E, 0x41, 0x49, 0x49, 0x7A, // G 0x7F, 0x08, 0x08, 0x08, 0x7F, // H 0x00, 0x41, 0x7F, 0x41, 0x00, // I 0x20, 0x40, 0x41, 0x3F, 0x01, // J 0x7F, 0x08, 0x14, 0x22, 0x41, // K 0x7F, 0x40, 0x40, 0x40, 0x40, // L 0x7F, 0x02, 0x1C, 0x02, 0x7F, // M 0x7F, 0x04, 0x08, 0x10, 0x7F, // N 0x3E, 0x41, 0x41, 0x41, 0x3E, // O 0x7F, 0x09, 0x09, 0x09, 0x06, // P 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q 0x7F, 0x09, 0x19, 0x29, 0x46, // R 0x26, 0x49, 0x49, 0x49, 0x32, // S 0x01, 0x01, 0x7F, 0x01, 0x01, // T 0x3F, 0x40, 0x40, 0x40, 0x3F, // U 0x1F, 0x20, 0x40, 0x20, 0x1F, // V 0x3F, 0x40, 0x38, 0x40, 0x3F, // W 0x63, 0x14, 0x08, 0x14, 0x63, // X 0x03, 0x04, 0x78, 0x04, 0x03, // Y 0x61, 0x51, 0x49, 0x45, 0x43, // Z 0x00, 0x7F, 0x41, 0x41, 0x41, // [ 0x02, 0x04, 0x08, 0x10, 0x20, // '\' 0x00, 0x41, 0x41, 0x41, 0x7F, // ] 0x04, 0x02, 0x01, 0x02, 0x04, // ^ 0x80, 0x80, 0x80, 0x80, 0x80, // _ 0x00, 0x03, 0x07, 0x08, 0x00, // ' 0x20, 0x54, 0x54, 0x54, 0x78, // a 0x7F, 0x28, 0x44, 0x44, 0x38, // b 0x38, 0x44, 0x44, 0x44, 0x28, // c 0x38, 0x44, 0x44, 0x28, 0x7F, // d 0x38, 0x54, 0x54, 0x54, 0x18, // e 0x00, 0x08, 0x7E, 0x09, 0x02, // f 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g 0x7F, 0x08, 0x04, 0x04, 0x78, // h 0x00, 0x44, 0x7D, 0x40, 0x00, // i 0x00, 0x20, 0x40, 0x40, 0x3D, // j 0x00, 0x7F, 0x10, 0x28, 0x44, // k 0x00, 0x41, 0x7F, 0x40, 0x00, // l 0x7C, 0x04, 0x78, 0x04, 0x78, // m 0x7C, 0x08, 0x04, 0x04, 0x78, // n 0x38, 0x44, 0x44, 0x44, 0x38, // o 0xFC, 0x18, 0x24, 0x24, 0x18, // p 0x18, 0x24, 0x24, 0x18, 0xFC, // q 0x7C, 0x08, 0x04, 0x04, 0x08, // r 0x48, 0x54, 0x54, 0x54, 0x24, // s 0x04, 0x04, 0x3F, 0x44, 0x24, // t 0x3C, 0x40, 0x40, 0x20, 0x7C, // u 0x1C, 0x20, 0x40, 0x20, 0x1C, // v 0x3C, 0x40, 0x30, 0x40, 0x3C, // w 0x44, 0x28, 0x10, 0x28, 0x44, // x 0x4C, 0x90, 0x90, 0x90, 0x7C, // y 0x44, 0x64, 0x54, 0x4C, 0x44, // z 0x00, 0x08, 0x36, 0x41, 0x00, // { 0x00, 0x00, 0x77, 0x00, 0x00, // | 0x00, 0x41, 0x36, 0x08, 0x00, // } 0x02, 0x01, 0x02, 0x04, 0x02, // ~ 0x00, 0x06, 0x09, 0x09, 0x06, // degrees }; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::print(char c) { write(&font[c - 32][0], 5); write(&font[0][0], 1); } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::print(const char *s) { while(*s) { write(&font[*s - 32][0], 5); write(&font[0][0], 1); ++s; } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::print(const char *s, unsigned char m) { unsigned char c; while(*s) { c = font[*s - 32][0] ^ m; write(&c, 1); c = font[*s - 32][1] ^ m; write(&c, 1); c = font[*s - 32][2] ^ m; write(&c, 1); c = font[*s - 32][3] ^ m; write(&c, 1); c = font[*s - 32][4] ^ m; write(&c, 1); write(&m, 1); ++s; } } template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::printv(unsigned char x, unsigned char y, char *s) { while(*s) { pos(x, y); ++y; write(&font[*s - 32][0], 5); write(&font[0][0], 1); ++s; } } static const unsigned char num11x16[19][11 * 2] = { 0x00,0xF0,0xFC,0xFE,0x06,0x02,0x06,0xFE,0xFC,0xF0,0x00, // 0 0x00,0x07,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x07,0x00, 0x00,0x00,0x08,0x0C,0xFC,0xFE,0xFE,0x00,0x00,0x00,0x00, // 1 0x00,0x20,0x20,0x20,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x00, 0x00,0x0C,0x0E,0x06,0x02,0x02,0x86,0xFE,0x7C,0x38,0x00, // 2 0x00,0x30,0x38,0x3C,0x36,0x33,0x31,0x30,0x30,0x38,0x00, 0x00,0x0C,0x0E,0x86,0x82,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 3 0x00,0x18,0x38,0x30,0x20,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x00,0xC0,0x20,0x18,0x04,0xFE,0xFE,0xFE,0x00,0x00, // 4 0x00,0x03,0x02,0x02,0x02,0x22,0x3F,0x3F,0x3F,0x22,0x02, 0x00,0x00,0x7E,0x7E,0x46,0x46,0xC6,0xC6,0x86,0x00,0x00, // 5 0x00,0x18,0x38,0x30,0x20,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0xC0,0xF0,0xF8,0xFC,0x4C,0xC6,0xC2,0x82,0x00,0x00, // 6 0x00,0x0F,0x1F,0x3F,0x30,0x20,0x30,0x3F,0x1F,0x0F,0x00, 0x00,0x06,0x06,0x06,0x06,0x06,0xC6,0xF6,0x3E,0x0E,0x00, // 7 0x00,0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00,0x00, 0x00,0x38,0x7C,0xFE,0xC6,0x82,0xC6,0xFE,0x7C,0x38,0x00, // 8 0x00,0x0E,0x1F,0x3F,0x31,0x20,0x31,0x3F,0x1F,0x0E,0x00, 0x00,0x78,0xFC,0xFE,0x86,0x02,0x86,0xFE,0xFC,0xF8,0x00, // 9 0x00,0x00,0x00,0x21,0x21,0x31,0x1D,0x1F,0x0F,0x03,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x70,0x70,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // : 0x00,0x0E,0x0E,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // . 0x00,0x38,0x38,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xC0,0x30,0x0C,0x00,0x00,0x00,0x00, // / 0x00,0x30,0x0C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // - 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x00,0x18,0x3C,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00, // 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xF0,0xF8,0x0C,0x06,0x02,0x02,0x02,0x02,0x0E,0x0C,0x00, // C 0x03,0x07,0x0C,0x18,0x10,0x10,0x10,0x10,0x1C,0x0C,0x00, 0xFE,0xFE,0x42,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00, // F 0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xFE,0xFE,0x40,0xE0,0xB0,0x18,0x0C,0x06,0x02,0x00,0x00, // K 0x1F,0x1F,0x00,0x00,0x01,0x03,0x06,0x0C,0x18,0x10,0x00 }; template volatile unsigned char &_EP, unsigned char _CE, volatile unsigned char &_RP, unsigned char _RST, unsigned _RD> void Nokia7110<_SP, _CLK, _DATA, _CP, _DC, _EP, _CE, _RP, _RST, _RD>::pd12(unsigned n, unsigned x, unsigned y) { pos(x, y); write(num11x16[n], 11, lcd_data); pos(x, ++y); write(num11x16[n] + 11, 11, lcd_data); } } // namespace
-
kff2 got a reaction from oPossum in Time & Temperature on Nokia 7110
Some notes on compiling this with msp430-gcc.
1) To handle C++ templates in nokia7110tl.h, you need to a version of msp430-gcc with C++ support. If building from sources, configure with --enable_languages=c,c++. The packaged msp430-gcc in recent versions of Ubuntu should already be C++ enabled.
To compile C++ code, use msp430-g++ instead of msp430-gcc. You will likely get the error
ld: cannot find -lstdc++
This is because msp430-g++ links against the C++ standard library by default, which is not included with msp430-gcc. To work around, run msp430-g++ as follows:
msp430-g++ -mmcu=msp430g2452 -o time.elf main.cpp -nodefaultlibs -fno-rtti -fno-exceptions -lc -lgcc
This tells msp430-g++ not to use default libraries. You then need to explicitly list the default libraries that you need (-lc and -lgcc). -fno-rtti -fno-exceptions eliminates some C++ bloat and reduces code size.
2) time.h is not included with msp430-gcc. I used this version which I found somewhere on the internet:
typedef long time_t; /* for representing times in seconds */ struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; const unsigned char moninit[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; static unsigned dylen(unsigned yr) { if(yr%4 || (yr % 100) == 0 && ((yr+300)%400) != 0) return(365); return(366); } struct tm * gmtime(time_t * tp) { time_t t; static struct tm tim; t = *tp; tim.tm_sec = t % 60L; t /= 60L; tim.tm_min = t % 60L; t /= 60L; tim.tm_hour = t % 24L; tim.tm_mday = t / 24L; tim.tm_wday = (tim.tm_mday + 4) % 7; tim.tm_year = 70; while(tim.tm_mday >= dylen(tim.tm_year)) { tim.tm_mday -= dylen(tim.tm_year); tim.tm_year++; } tim.tm_yday = tim.tm_mday; if(tim.tm_mday >= 59 && dylen(tim.tm_year) == 366) tim.tm_mday--; tim.tm_mon = 0; while(tim.tm_mday >= moninit[tim.tm_mon]) { tim.tm_mday -= moninit[tim.tm_mon]; tim.tm_mon++; } tim.tm_mday++; return &tim; } struct tm * localtime(time_t * tp) { time_t t; t = *tp - 7 * 3600L; return gmtime(&t); }
Notice that there is no mktime function here. Instead, in main.cpp, I set tt to the current unix timestamp (obtained with "date +%s" in Linux). Also, the localtime function here hardcodes my timezone. Change accordingly.
3) This original code causes the LCD to go berserk when compiled under msp430-gcc:
for(c = 32; c < 128; ++c) { lcd.print(c); if(++x >= 16) x = 0, lcd.pos(x, ++y); }
That's because c is a signed byte value, and the compiler treats 128 as -1, causing an infinite loop. What you want instead is
for(c = 32; c < 128U; ++c) { lcd.print(c); if(++x >= 16) x = 0, lcd.pos(x, ++y); }
Hope this will be useful to somebody.
-
kff2 got a reaction from turd in Generating image bitmaps for Nokia 7110 LCD
In oPossum's Time and Temperature project, there is an embedded TI logo that rolls across the screen when the program starts. Here is how you can generate bitmaps from your own images. You will need ImageMagick and python.
1) Resize your image to fit on the LCD. The maximum height is 96 pixels. The height should be a multiple of 8. Also, you will want to stretch your image horizontally a little bit, as pixels on 7110 aren't square.
I did something along the lines of
convert -resize 43x48! logo.jpg logo2.png
The exclamation point here tells ImageMagick to go ahead and change the aspect ratio of the image; otherwise it will set width (or height) automatically to maintain the original aspect ratio.
2) Convert the image to 1-bit black and white and save it in RGB format
convert -monochrome -depth 1 -type Bilevel +dither logo2.png logo3.rgb
The resulting file contains 3 bits for each pixel (red, green, and blue channels). The 3 bits are the same. 0 corresponds to black and 1 to white.
I wrote a short python script to convert RGB files to the format expected by nokia7110tl.h's bitmap() function:
import sys import string x = int(sys.argv[2]) y = int(sys.argv[3]) img = open(sys.argv[1]).read() b = string.join([str(int(ord(img[i*3/8]) & (1<<(7-(i*3%8))) == 0)) for i in xrange(x*y)], '') # b is a string that contains a 0 or 1 for each pixel of the image. If you now set # the screen size to 'width' and print b, you should see your image in ascii. # The next step is to rearrange the bits in b to match the # addressing expected by 7110's controller. b2 = '' for i in reversed(xrange(y/8)): for j in xrange(x): b2 += string.join([b[(y-1-i*8-k)*x+j] for k in xrange(8)], '') print string.join(['0x%02x' % eval('0b'+b2[i*8:i*8+8]) for i in xrange(x*y/8)], ',')
You run it like this:
python convert_7110.py logo3.rgb 43 48
where 43 is the width and 48 is the height of the image. The script spits out an array of ascii bytes that you can copy and paste directly into your C program and pass to the bitmap() function.
-
kff2 got a reaction from bluehash in Generating image bitmaps for Nokia 7110 LCD
In oPossum's Time and Temperature project, there is an embedded TI logo that rolls across the screen when the program starts. Here is how you can generate bitmaps from your own images. You will need ImageMagick and python.
1) Resize your image to fit on the LCD. The maximum height is 96 pixels. The height should be a multiple of 8. Also, you will want to stretch your image horizontally a little bit, as pixels on 7110 aren't square.
I did something along the lines of
convert -resize 43x48! logo.jpg logo2.png
The exclamation point here tells ImageMagick to go ahead and change the aspect ratio of the image; otherwise it will set width (or height) automatically to maintain the original aspect ratio.
2) Convert the image to 1-bit black and white and save it in RGB format
convert -monochrome -depth 1 -type Bilevel +dither logo2.png logo3.rgb
The resulting file contains 3 bits for each pixel (red, green, and blue channels). The 3 bits are the same. 0 corresponds to black and 1 to white.
I wrote a short python script to convert RGB files to the format expected by nokia7110tl.h's bitmap() function:
import sys import string x = int(sys.argv[2]) y = int(sys.argv[3]) img = open(sys.argv[1]).read() b = string.join([str(int(ord(img[i*3/8]) & (1<<(7-(i*3%8))) == 0)) for i in xrange(x*y)], '') # b is a string that contains a 0 or 1 for each pixel of the image. If you now set # the screen size to 'width' and print b, you should see your image in ascii. # The next step is to rearrange the bits in b to match the # addressing expected by 7110's controller. b2 = '' for i in reversed(xrange(y/8)): for j in xrange(x): b2 += string.join([b[(y-1-i*8-k)*x+j] for k in xrange(8)], '') print string.join(['0x%02x' % eval('0b'+b2[i*8:i*8+8]) for i in xrange(x*y/8)], ',')
You run it like this:
python convert_7110.py logo3.rgb 43 48
where 43 is the width and 48 is the height of the image. The script spits out an array of ascii bytes that you can copy and paste directly into your C program and pass to the bitmap() function.