Edit: Probably misunderstood your question. You would like to put two transceivers on a single Launchpad? Then read below
There are probably two options. Having both transceivers on the same SPI bus and assigning different CSN, CE and IRQ pins to them. To do this, you need to create two radio objects, for example:
Enrf24 radio1(CE_1,CSN_1, IRQ_1);
Enrf24 radio2(CE_2,CSN_2, IRQ_2);
Then you repeat the begin and configuration code for both. I havent tested this yet myseld, but should work in theory.
The second option is to use two different SPI buses, for that the Enrf24 library needs to be adapted, to support SPI argument as well. Again, writing this without testing:
Enrf24.h must have the constructor fixed and an include added.
#include <SPI.h>
Enrf24(SPIClass *mySPI, uint8_t cePin, uint8_t csnPin, uint8_t irqPin);
SPIClass *_mySPI;
Enrf24.cpp must have the constructor fixed and all instances of "SPI." changed by "_mySPI->"
/* Constructor */
Enrf24::Enrf24(SPIClass *mySPI,uint8_t cePin, uint8_t csnPin, uint8_t irqPin)
{
_mySPI = mySPI;
_cePin = cePin;
_csnPin = csnPin;
_irqPin = irqPin;
rf_status = 0;
rf_addr_width = 5;
txbuf_len = 0;
readpending = 0;
}
Then in your main code you call this fixed library by:
//radio
Enrf24 radio(&SPI, ENRF24L_CE, ENRF24L_CSN, ENRF24L_IRQ);
where SPI can be SPI0 for channel 0, SPI1 for channel 1, SPI for channel 2 (default), SPI3 for channel 3.
This should work in theory, but needs to be tested. There could be a neater solution though.
oPossum, thank you for all the great and useful code you are posting.
I have "forked" frequency counter and created a Frequency Counter using Launchpad & UART that can more readily be integrated in programs. It works on G2553 and sends measured frequency via UART. Code is available on github, fully working, however there is still a lot room for improvement.
This is a simple frequency counter with a range of 1 Hz to 16 MHz. It uses the 32 kHz watch crystal so the accuracy is good enough for many applications. Gate time is automatically set to 250 ms or 1 second.
There is no signal conditioning (front end), so it is limited to 3V logic levels signals only! It is intended to be used with digital circuits like another MSP430 or 74HCxx circuits. It is not a substitute for a proper commercial frequency counter.
How a frequency counter works: Fundamentals of Electronic Counters by Agilent
Wiring
main.c
#include "msp430g2553.h"
#include "lcd.h"
static const unsigned long dv[] = { // Base 10 digit weights
10000000, // 8 digit maximum count
1000000, //
100000, //
10000, //
1000, //
100, //
10, //
1, //
0 //
};
static void print_freq(unsigned long n)
{
const unsigned long *dp = dv;
unsigned x = 0;
unsigned c;
unsigned long d;
while(n < *dp) { // Skip leading zeros
if(*dp == 100000 || *dp == 100) x += 2; // Space between 3 digit groups
++dp; //
lcd_pd10(10, x, 2); // Print space
x += 10; //
} //
if(n) { // Non-zero
do { //
d = *dp++; // Get digit value
c = 0; //
while(n >= d) ++c, n -= d; // Divide
if(d == 100000 || d == 100) x += 2; // Space between 3 digit groups
lcd_pd10(c, x, 2); // Print digit
x += 10; //
} while(!(d & 1)); // Until all digits done
} else //
lcd_pd10(0, x - 10, 2); // Print zero
}
static unsigned clock_input = 0;
void set_input(void)
{
const unsigned char z = 0;
lcd_pos(0, 4);
lcd_send(&z, 84, lcd_data_repeat);
switch(clock_input) {
default:
clock_input = 0;
case 0:
TACTL = TASSEL_2;
lcd_print("Internal 16MHz", 0, 4);
break;
case 1:
TACTL = TASSEL_0;
lcd_print("Clock In P1.0", 3, 4);
break;
#if 0
case 2: // This should always show 32768
TACTL = TASSEL_1; // Something is very wrong if it doesn't
lcd_print("Internal 32kHz", 0, 4);
break;
#endif
}
}
void set_gate(unsigned long f)
{
if(WDTCTL & WDTIS0) { // 250 ms gate currently in use
if(f < 800000) { // Switch to 1 s gate if frequncy is below 800 kHz
lcd_print("1 Second Gate", 3, 5);
WDTCTL = WDTPW | WDTTMSEL | WDTSSEL;
}
} else { // 1 s gate currently in use
if(f > 900000) { // Switch to 250 ms gate if frequency above 900 kHz
lcd_print(" 250 ms Gate ", 3, 5);
WDTCTL = WDTPW | WDTTMSEL | WDTSSEL | WDTIS0;
}
}
}
void main(void)
{
unsigned long freq = 12345678L; // Measured frequency
//
WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset
//
DCOCTL = 0; // Run at 16 MHz
BCSCTL1 = CALBC1_16MHZ; //
DCOCTL = CALDCO_16MHZ; //
//BCSCTL3 = XCAP_1; // 6 pF (default)
BCSCTL3 = XCAP_2; // 10 pF
//BCSCTL3 = XCAP_3; // 12.5 pF
//
lcd_init(); // Initialize LCD
//
P1SEL |= BIT0; // Use P1.0 as TimerA input
P1SEL2 &= ~BIT0; //
P1DIR &= ~BIT0; //
P1OUT &= ~BIT0; // Enable pull down resistor to reduce stray counts
P1REN |= BIT0; //
//
WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL | WDTSSEL | WDTIS0; // Use WDT as interval timer
// Default to 250 ms gate so that initial call to set_gate()
// will switch to 1 s gate and update the LCD
//
lcd_clear(0); // Clear LCD
lcd_print("Frequency", 16, 0); // What it is
lcd_print("Counter", 22, 1); //
print_freq(freq); // 8 digit frequency
set_input(); // Set input and show on LCD
set_gate(0); // Set gate time and show on LCD
//
for(; { // for-ever
freq = 0; // Clear frequency
TACTL |= TACLR; // Clear TimerA
//
IFG1 &= ~WDTIFG; // Wait for WDT period to begin
while(!(IFG1 & WDTIFG)); //
//
TACTL |= MC_2; // Start counting - TimerA continuous mode
//
IFG1 &= ~WDTIFG; //
while(!(IFG1 & WDTIFG)) { // While WDT period..
if(TACTL & TAIFG) { // Check for TimerA overflow
freq += 0x10000L; // Add 1 to msw of frequency
TACTL &= ~TAIFG; // Clear overflow flag
} //
} //
//
TACTL &= ~MC_2; // Stop counting - TimerA stop mode
if(TACTL & TAIFG) freq += 0x10000L; // Handle TimerA overflow that may have occured between
// last check of overflow and stopping TimerA
freq |= TAR; // Merge TimerA count with overflow
if(WDTCTL & WDTIS0) freq <<= 2; // Multiply by 4 if using 250 ms gate
print_freq(freq); // Show on LCD
//
set_gate(freq); // Adjust gate time if necessary
//
if(!(P1IN & BIT3)) { // Check if pushbutton down
++clock_input; // Switch clock input
set_input(); //
} //
} //
}
lcd.h
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;
void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type);
void lcd_home(void);
void lcd_pos(unsigned char x, unsigned char y);
void lcd_clear(unsigned char x);
void lcd_init(void);
void lcd_print(char *s, unsigned x, unsigned y);
void lcd_pd10(unsigned n, unsigned x, unsigned y);
lcd.c
#include "msp430g2553.h"
#include "lcd.h"
//static const unsigned TXD = BIT1;
static const unsigned RXD = BIT2;
static const unsigned SWITCH = BIT3;
static const unsigned LCD_CLK = BIT5;
static const unsigned LCD_BACKLIGHT = BIT6;
static const unsigned LCD_DATA = BIT7;
static const unsigned LCD_DC = BIT0; // PORT2
static const unsigned LCD_CE = BIT1; // PORT2
void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type)
{
register unsigned mask;
P2OUT &= ~LCD_CE;
do {
mask = 0x0080;
do {
if(*cmd & mask) {
P1OUT &= ~LCD_CLK;
P1OUT |= LCD_DATA;
} else {
P1OUT &= ~(LCD_CLK | LCD_DATA);
}
P1OUT |= LCD_CLK;
mask >>= 1;
} while(!(mask & 1));
if(!type) P2OUT &= ~LCD_DC;
if(*cmd & mask) {
P1OUT &= ~LCD_CLK;
P1OUT |= LCD_DATA;
} else {
P1OUT &= ~(LCD_CLK | LCD_DATA);
}
P1OUT |= LCD_CLK;
P2OUT |= LCD_DC;
if(!(type & 2)) ++cmd;
} while(--len);
P2OUT |= LCD_CE;
}
static const unsigned char home[] = { 0x40, 0x80 };
void lcd_home(void)
{
lcd_send(home, sizeof(home), lcd_command);
}
void lcd_pos(unsigned char x, unsigned char y)
{
unsigned char c[2];
c[0] = 0x80 | x;
c[1] = 0x40 | y;
lcd_send(c, sizeof(c), lcd_command);
}
void lcd_clear(unsigned char x)
{
lcd_home();
lcd_send(&x, 504, lcd_data_repeat);
lcd_home();
}
void lcd_init(void)
{
static const unsigned char init[] = {
0x20 + 0x01, // Function set - extended instructions enabled
//0x80 + 64, // Set vop (contrast) 0 - 127
0x80 + 66, // This is better for fast LCD update
0x04 + 0x02, // Temperature control
0x10 + 0x03, // Set bias system
0x20 + 0x00, // Function set - chip active, horizontal addressing, basic instructions
0x08 + 0x04 // Display control - normal mode
};
P1REN = RXD | SWITCH;
P1DIR = LCD_CLK | LCD_BACKLIGHT | LCD_DATA;
P1OUT = LCD_CLK | RXD | SWITCH | LCD_BACKLIGHT;
P2REN = 0;
P2DIR = LCD_DC | LCD_CE;
P2OUT = LCD_CE;
__delay_cycles(20000);
P2OUT |= LCD_DC;
__delay_cycles(20000);
lcd_send(init, sizeof(init), lcd_command);
}
static const unsigned char font6x8[96][5] = {
0x00, 0x00, 0x00, 0x00, 0x00, // 20 32 <space>
0x00, 0x00, 0x5F, 0x00, 0x00, // 21 33 !
0x00, 0x07, 0x00, 0x07, 0x00, // 22 34 "
0x14, 0x7F, 0x14, 0x7F, 0x14, // 23 35 #
0x24, 0x2A, 0x7F, 0x2A, 0x12, // 24 36 $
0x23, 0x13, 0x08, 0x64, 0x62, // 25 37 %
0x36, 0x49, 0x56, 0x20, 0x50, // 26 38 &
0x00, 0x08, 0x07, 0x03, 0x00, // 27 39 '
0x00, 0x1C, 0x22, 0x41, 0x00, // 28 40 (
0x00, 0x41, 0x22, 0x1C, 0x00, // 29 41 )
0x2A, 0x1C, 0x7F, 0x1C, 0x2A, // 2A 42 *
0x08, 0x08, 0x3E, 0x08, 0x08, // 2B 43 +
0x00, 0x40, 0x38, 0x18, 0x00, // 2C 44 ,
0x08, 0x08, 0x08, 0x08, 0x08, // 2D 45 -
0x00, 0x00, 0x60, 0x60, 0x00, // 2E 46 .
0x20, 0x10, 0x08, 0x04, 0x02, // 2F 47 /
0x3E, 0x51, 0x49, 0x45, 0x3E, // 30 48 0
0x00, 0x42, 0x7F, 0x40, 0x00, // 31 49 1
0x42, 0x61, 0x51, 0x49, 0x46, // 32 50 2
0x21, 0x41, 0x49, 0x4D, 0x33, // 33 51 3
0x18, 0x14, 0x12, 0x7F, 0x10, // 34 52 4
0x27, 0x45, 0x45, 0x45, 0x39, // 35 53 5
0x3C, 0x4A, 0x49, 0x49, 0x30, // 36 54 6
0x41, 0x21, 0x11, 0x09, 0x07, // 37 55 7
0x36, 0x49, 0x49, 0x49, 0x36, // 38 56 8
0x06, 0x49, 0x49, 0x29, 0x1E, // 39 57 9
0x00, 0x00, 0x14, 0x00, 0x00, // 3A 58 :
0x00, 0x00, 0x40, 0x34, 0x00, // 3B 59 ;
0x00, 0x08, 0x14, 0x22, 0x41, // 3C 60 <
0x14, 0x14, 0x14, 0x14, 0x14, // 3D 61 =
0x00, 0x41, 0x22, 0x14, 0x08, // 3E 62 >
0x02, 0x01, 0x51, 0x09, 0x06, // 3F 63 ?
0x3E, 0x41, 0x5D, 0x59, 0x4E, // 40 64 @
0x7C, 0x12, 0x11, 0x12, 0x7C, // 41 65 A
0x7F, 0x49, 0x49, 0x49, 0x36, // 42 66 B
0x3E, 0x41, 0x41, 0x41, 0x22, // 43 67 C
0x7F, 0x41, 0x41, 0x41, 0x3E, // 44 68 D
0x7F, 0x49, 0x49, 0x49, 0x41, // 45 69 E
0x7F, 0x09, 0x09, 0x09, 0x01, // 46 70 F
0x3E, 0x41, 0x49, 0x49, 0x7A, // 47 71 G
0x7F, 0x08, 0x08, 0x08, 0x7F, // 48 72 H
0x00, 0x41, 0x7F, 0x41, 0x00, // 49 73 I
0x20, 0x40, 0x41, 0x3F, 0x01, // 4A 74 J
0x7F, 0x08, 0x14, 0x22, 0x41, // 4B 75 K
0x7F, 0x40, 0x40, 0x40, 0x40, // 4C 76 L
0x7F, 0x02, 0x1C, 0x02, 0x7F, // 4D 77 M
0x7F, 0x04, 0x08, 0x10, 0x7F, // 4E 78 N
0x3E, 0x41, 0x41, 0x41, 0x3E, // 4F 79 O
0x7F, 0x09, 0x09, 0x09, 0x06, // 50 80 P
0x3E, 0x41, 0x51, 0x21, 0x5E, // 51 81 Q
0x7F, 0x09, 0x19, 0x29, 0x46, // 52 82 R
0x26, 0x49, 0x49, 0x49, 0x32, // 53 83 S
0x01, 0x01, 0x7F, 0x01, 0x01, // 54 84 T
0x3F, 0x40, 0x40, 0x40, 0x3F, // 55 85 U
0x1F, 0x20, 0x40, 0x20, 0x1F, // 56 86 V
0x3F, 0x40, 0x38, 0x40, 0x3F, // 57 87 W
0x63, 0x14, 0x08, 0x14, 0x63, // 58 88 X
0x03, 0x04, 0x78, 0x04, 0x03, // 59 89 Y
0x61, 0x51, 0x49, 0x45, 0x43, // 5A 90 Z
0x00, 0x7F, 0x41, 0x41, 0x41, // 5B 91 [
0x02, 0x04, 0x08, 0x10, 0x20, // 5C 92 '\'
0x00, 0x41, 0x41, 0x41, 0x7F, // 5D 93 ]
0x04, 0x02, 0x01, 0x02, 0x04, // 5E 94 ^
0x80, 0x80, 0x80, 0x80, 0x80, // 5F 95 _
0x00, 0x03, 0x07, 0x08, 0x00, // 60 96 '
0x20, 0x54, 0x54, 0x54, 0x78, // 61 97 a
0x7F, 0x28, 0x44, 0x44, 0x38, // 62 98 b
0x38, 0x44, 0x44, 0x44, 0x28, // 63 99 c
0x38, 0x44, 0x44, 0x28, 0x7F, // 64 100 d
0x38, 0x54, 0x54, 0x54, 0x18, // 65 101 e
0x00, 0x08, 0x7E, 0x09, 0x02, // 66 102 f
0x18, 0xA4, 0xA4, 0xA4, 0x7C, // 67 103 g
0x7F, 0x08, 0x04, 0x04, 0x78, // 68 104 h
0x00, 0x44, 0x7D, 0x40, 0x00, // 69 105 i
0x00, 0x20, 0x40, 0x40, 0x3D, // 6A 106 j
0x00, 0x7F, 0x10, 0x28, 0x44, // 6B 107 k
0x00, 0x41, 0x7F, 0x40, 0x00, // 6C 108 l
0x7C, 0x04, 0x78, 0x04, 0x78, // 6D 109 m
0x7C, 0x08, 0x04, 0x04, 0x78, // 6E 110 n
0x38, 0x44, 0x44, 0x44, 0x38, // 6F 111 o
0xFC, 0x18, 0x24, 0x24, 0x18, // 70 112 p
0x18, 0x24, 0x24, 0x18, 0xFC, // 71 113 q
0x7C, 0x08, 0x04, 0x04, 0x08, // 72 114 r
0x48, 0x54, 0x54, 0x54, 0x24, // 73 115 s
0x04, 0x04, 0x3F, 0x44, 0x24, // 74 116 t
0x3C, 0x40, 0x40, 0x20, 0x7C, // 75 117 u
0x1C, 0x20, 0x40, 0x20, 0x1C, // 76 118 v
0x3C, 0x40, 0x30, 0x40, 0x3C, // 77 119 w
0x44, 0x28, 0x10, 0x28, 0x44, // 78 120 x
0x4C, 0x90, 0x90, 0x90, 0x7C, // 79 121 y
0x44, 0x64, 0x54, 0x4C, 0x44, // 7A 122 z
0x00, 0x08, 0x36, 0x41, 0x00, // 7B 123 {
0x00, 0x00, 0x77, 0x00, 0x00, // 7C 124 |
0x00, 0x41, 0x36, 0x08, 0x00, // 7D 125 }
0x02, 0x01, 0x02, 0x04, 0x02, // 7E 126 ~
0x00, 0x06, 0x09, 0x09, 0x06, // 7F 127 degrees
};
void lcd_print(char *s, unsigned x, unsigned y)
{
lcd_pos(x, y);
while(*s) {
lcd_send(&font6x8[*s - 32][0], 5, lcd_data);
lcd_send(&font6x8[0][0], 1, lcd_data);
++s;
}
}
static const unsigned char num10x16[11][9 * 2] = { // Numbers for 10x16 cell
0xF0,0xF8,0x0C,0x04,0x04,0x04,0x0C,0xF8,0xF0, // 0
0x0F,0x1F,0x30,0x20,0x20,0x20,0x30,0x1F,0x0F,
0x00,0x00,0x10,0x10,0xFC,0xFC,0x00,0x00,0x00, // 1
0x00,0x00,0x20,0x20,0x3F,0x3F,0x20,0x20,0x00,
0x18,0x1C,0x04,0x04,0x04,0x04,0x8C,0xF8,0x70, // 2
0x20,0x30,0x38,0x2C,0x26,0x23,0x21,0x20,0x20,
0x18,0x1C,0x04,0x84,0x84,0x84,0xCC,0x78,0x30, // 3
0x18,0x38,0x20,0x20,0x20,0x20,0x31,0x1F,0x0E,
0x00,0x80,0x40,0x20,0x10,0x08,0xFC,0xFC,0x00, // 4
0x03,0x02,0x02,0x02,0x02,0x02,0x3F,0x3F,0x02,
0x00,0x7C,0x7C,0x44,0x44,0x44,0xC4,0x84,0x04, // 5
0x18,0x38,0x20,0x20,0x20,0x20,0x30,0x1F,0x0F,
0xE0,0xF0,0x58,0x4C,0x44,0x44,0xC4,0x84,0x00, // 6
0x0F,0x1F,0x30,0x20,0x20,0x20,0x30,0x1F,0x0F,
0x04,0x04,0x04,0x04,0x04,0xC4,0xF4,0x3C,0x0C, // 7
0x00,0x00,0x30,0x3C,0x0F,0x03,0x00,0x00,0x00,
0x30,0x78,0xCC,0x84,0x84,0x84,0xCC,0x78,0x30, // 8
0x0E,0x1F,0x31,0x20,0x20,0x20,0x31,0x1F,0x0E,
0xF0,0xF8,0x0C,0x04,0x04,0x04,0x0C,0xF8,0xF0, // 9
0x00,0x21,0x23,0x22,0x22,0x32,0x1A,0x0F,0x07,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // <space>
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
void lcd_pd10(unsigned n, unsigned x, unsigned y) // Print 10x16 digit
{
unsigned char c[2];
c[0] = 0x80 | x;
c[1] = 0x40 + y; lcd_send(c, 2, lcd_command); lcd_send(num10x16[n], 9, lcd_data);
c[1] = 0x41 + y; lcd_send(c, 2, lcd_command); lcd_send(num10x16[n] + 9, 9, lcd_data);
}