Skip to content

Commit 7dcbaa0

Browse files
committed
Merge pull request energia#54 from energia/issue_53
Added flash controller driver library and example. Fix for issue energia#53 (tone)
2 parents 3b7f944 + 0babe2a commit 7dcbaa0

File tree

7 files changed

+548
-2
lines changed

7 files changed

+548
-2
lines changed

hardware/msp430/cores/msp430/Energia.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ extern const uint8_t digital_pin_to_bit_mask[];
127127
extern const uint16_t port_to_sel[];
128128
extern const uint16_t port_to_sel2[];
129129
extern const uint16_t port_to_input[];
130+
extern const uint16_t port_to_output[];
130131

131132
#define digitalPinToPort(P) ( digital_pin_to_port[P] )
132133
#define digitalPinToBitMask(P) ( digital_pin_to_bit_mask[P] )

hardware/msp430/cores/msp430/Tone.cpp

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/* Tone.cpp
2+
3+
A Tone Generator Library - Modified for Energia
4+
Implements up to 3 (software) PWM outputs using TIMERA0 compare registers and IRQ.
5+
Can use any digital output pin for pulse generation
6+
7+
(c) 2012 - Peter Brier.
8+
9+
Based on code Originally written by Brett Hagman
10+
11+
This library is free software; you can redistribute it and/or
12+
modify it under the terms of the GNU Lesser General Public
13+
License as published by the Free Software Foundation; either
14+
version 2.1 of the License, or (at your option) any later version.
15+
16+
This library is distributed in the hope that it will be useful,
17+
but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19+
Lesser General Public License for more details.
20+
21+
You should have received a copy of the GNU Lesser General Public
22+
License along with this library; if not, write to the Free Software
23+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24+
25+
Version Modified By Date Comments
26+
------- ----------- -------- --------
27+
0001 B Hagman 09/08/02 Initial coding
28+
0002 B Hagman 09/08/18 Multiple pins
29+
0003 B Hagman 09/08/18 Moved initialization from constructor to begin()
30+
0004 B Hagman 09/09/26 Fixed problems with ATmega8
31+
0005 B Hagman 09/11/23 Scanned prescalars for best fit on 8 bit timers
32+
09/11/25 Changed pin toggle method to XOR
33+
09/11/25 Fixed timer0 from being excluded
34+
0006 D Mellis 09/12/29 Replaced objects with functions
35+
0007 M Sproul 10/08/29 Changed #ifdefs from cpu to register
36+
0008 P Brier 12/05/28 Modified for TI MSP430 processor
37+
0009 P Brier 12/05/29 Fixed problem with re-init of expired tone
38+
*************************************************/
39+
40+
#include "wiring_private.h"
41+
#include "pins_energia.h"
42+
#include "Energia.h"
43+
44+
// local funcions
45+
static void initTimers();
46+
static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration);
47+
static void stopTimer(uint8_t n);
48+
49+
// timer clock frequency set to clock/8, at F_CPU = 1MHZ this gives an output freq range of ~[1Hz ..65Khz] and at 16Mhz this is ~[16Hz .. 1MHz]
50+
#define F_TIMER (F_CPU/8L)
51+
52+
#ifdef __MSP430_HAS_TA3__
53+
#define AVAILABLE_TONE_PINS 3
54+
#define SETARRAY(a) a,a,a
55+
#else
56+
#define AVAILABLE_TONE_PINS 2
57+
#define SETARRAY(a) a,a
58+
#endif
59+
60+
61+
// tone_duration:
62+
// > 0 - duration specified
63+
// = 0 - stopped
64+
// < 0 - infinitely (until stop() method called, or new play() called)
65+
66+
static uint8_t tone_state = 0; // 0==not initialized, 1==timer running
67+
static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { SETARRAY(255) };
68+
static uint8_t tone_bit[AVAILABLE_TONE_PINS] = { SETARRAY(255) };
69+
volatile static uint16_t *tone_out[AVAILABLE_TONE_PINS] = { SETARRAY(0) };
70+
static uint16_t tone_interval[AVAILABLE_TONE_PINS] = { SETARRAY(-1) };
71+
static int16_t tone_periods[AVAILABLE_TONE_PINS] = { SETARRAY(0) };
72+
73+
74+
/**
75+
*** tone() -- Output a tone (50% Dutycycle PWM signal) on a pin
76+
*** pin: This pin is selected as output
77+
*** frequency: [Hertz]
78+
** duration: [milliseconds], if duration <=0, then we output tone continously, otherwise tone is stopped after this time (output = 0)
79+
**/
80+
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration)
81+
{
82+
uint8_t port = digitalPinToPort(_pin);
83+
if (port == NOT_A_PORT) return;
84+
85+
// find if we are using it at the moment, if so: update it
86+
for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
87+
{
88+
if (tone_pins[i] == _pin)
89+
{
90+
setTimer(i, frequency, duration);
91+
return; // we are done, timer reprogrammed
92+
}
93+
}
94+
95+
// new tone pin, find empty timer and set it
96+
for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
97+
{
98+
if (tone_pins[i] == 255)
99+
{
100+
tone_pins[i] = _pin;
101+
tone_bit[i] = digitalPinToBitMask(_pin);
102+
tone_out[i] = portOutputRegister(port);
103+
if ( tone_state == 0 )
104+
initTimers();
105+
pinMode(_pin, OUTPUT);
106+
setTimer(i, frequency, duration);
107+
return; // we are done, timer set
108+
}
109+
}
110+
// if we exit here, no unused timer was found, nothing is done
111+
}
112+
113+
114+
/**
115+
*** noTone() - Stop outputting the tone on a pin
116+
**/
117+
void noTone(uint8_t _pin)
118+
{
119+
if ( _pin == 255 ) return; // Should not happen!
120+
for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
121+
{
122+
if (tone_pins[i] == _pin)
123+
{
124+
tone_pins[i] = 255;
125+
stopTimer(i);
126+
}
127+
}
128+
}
129+
130+
131+
// Initialize the timers - Set mode and Enable IRQ
132+
static void inline initTimers()
133+
{
134+
// disable IRQs
135+
TA0CCTL0 = 0;
136+
TA0CCTL1 = 0;
137+
#ifdef __MSP430_HAS_TA3__
138+
TA0CCTL2 = 0;
139+
#endif
140+
TA0CTL = TACLR + TASSEL_2 + ID_3 + MC_2; // clear counter, source=SMCLK/8, mode=continous count up
141+
tone_state = 1; // init is done
142+
}
143+
144+
145+
// Set the timer interval and duration
146+
// frequency in [Hz] and duration in [msec]
147+
// we initialize the timer match value only if the tone was not running already, to prevent glitches when re-programming a running tone
148+
static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration)
149+
{
150+
if ( frequency <= 0 )
151+
{
152+
tone_interval[n] = 0;
153+
tone_periods[n] = 0;
154+
return;
155+
}
156+
tone_interval[n] = F_TIMER / (2L*frequency);
157+
if ( duration > 0 )
158+
tone_periods[n] = (duration * (F_TIMER/2)) / (1000L * tone_interval[n]);
159+
else
160+
tone_periods[n] = -1;
161+
switch( n ) // enable IRQ and set next match time in various timer compare registers (if we where not enabled already)
162+
{
163+
case 0:
164+
if ( ! (TA0CCTL0 & CCIE) ) TA0CCR0 = TAR + tone_interval[0];
165+
TA0CCTL0 = CCIE;
166+
break;
167+
case 1:
168+
if ( !(TA0CCTL1 & CCIE) ) TA0CCR1 = TAR + tone_interval[1];
169+
TA0CCTL1 = CCIE;
170+
break;
171+
#ifdef __MSP430_HAS_TA3__
172+
case 2:
173+
if ( !(TA0CCTL2 & CCIE) ) TA0CCR2 = TAR + tone_interval[2];
174+
TA0CCTL2 = CCIE;
175+
break;
176+
#endif
177+
}
178+
}
179+
180+
/* stopTimer() - Disable timer IRQ */
181+
static void inline stopTimer(uint8_t n)
182+
{
183+
switch( n )
184+
{
185+
case 0: TA0CCTL0 = 0; break;
186+
case 1: TA0CCTL1 = 0; break;
187+
#ifdef __MSP430_HAS_TA3__
188+
case 2: TA0CCTL2 = 0; break;
189+
#endif
190+
}
191+
*tone_out[n] &= ~tone_bit[n];
192+
}
193+
194+
195+
// Peform the isr magic, toggle output, decrease duation if > 0, and stop if duration == 0, continous if duration < 0
196+
// set new interval - defined as macro to limit ISR overhead (at the expense of some code size)
197+
#define isrTimer(n,ccr) do { \
198+
*tone_out[n] ^= tone_bit[n]; \
199+
if ( tone_periods[n] == 0 ) stopTimer(n);\
200+
else if ( tone_periods[n] > 0) tone_periods[n]--; \
201+
ccr += tone_interval[n]; \
202+
} while(0)
203+
204+
205+
// TIMERA vector (CCR0)
206+
__attribute__((interrupt(TIMER0_A0_VECTOR)))
207+
void TIMER0_A0_ISR(void)
208+
{
209+
isrTimer(0, TA0CCR0);
210+
}
211+
212+
// TAIV vector (CCR1/CCR2)
213+
__attribute__((interrupt(TIMER0_A1_VECTOR)))
214+
void TIMER0_A1_ISR(void)
215+
{
216+
switch ( TAIV )
217+
{
218+
case 0x2: isrTimer(1, TA0CCR1); break; // CCR1
219+
#ifdef __MSP430_HAS_TA3__
220+
case 0x4: isrTimer(2, TA0CCR2); break; // CCR2
221+
#endif
222+
}
223+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
MspFlash.h - Read/Write flash memory library for MSP430 Energia
3+
Copyright (c) 2012 Peter Brier. All right reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
*/
19+
#include "MspFlash.h"
20+
#include <msp430.h>
21+
22+
// The Flash clock must be between 200 and 400 kHz to operate correctly
23+
// TODO: calculate correct devider (0..64) depending on clock frequenct (F_CPU)
24+
// Now we set F_CPU/64 is 250khz at F_CPU = 16MHz
25+
26+
#define FLASHCLOCK FSSEL1+((F_CPU/400000L) & 63); // SCLK
27+
28+
// erase flash, make sure pointer is in the segment you wish to erase, otherwise you may erase you program or some data
29+
void MspFlashClass::erase(unsigned char *flash)
30+
{
31+
WDTCTL = WDTPW+WDTHOLD; // Disable WDT
32+
FCTL2 = FWKEY+FLASHCLOCK; // SMCLK/2
33+
FCTL3 = FWKEY; // Clear LOCK
34+
FCTL1 = FWKEY+ERASE; //Enable segment erase
35+
*flash = 0; // Dummy write, erase Segment
36+
FCTL3 = FWKEY+LOCK; // Done, set LOCK
37+
}
38+
39+
// load from memory, at segment boundary
40+
void MspFlashClass::read(unsigned char *flash, unsigned char *dest, int len)
41+
{
42+
while(len--)
43+
*(dest++) = *(flash++);
44+
}
45+
46+
// save in to flash (at segment boundary)
47+
void MspFlashClass::write(unsigned char *flash, unsigned char *src, int len)
48+
{
49+
WDTCTL = WDTPW+WDTHOLD; // Disable WDT
50+
FCTL2 = FWKEY+FLASHCLOCK; // SMCLK/2
51+
FCTL3 = FWKEY; // Clear LOCK
52+
FCTL1 = FWKEY+WRT; // Enable write
53+
while(len--) // Copy data
54+
*(flash++) = *(src++);
55+
FCTL1 = FWKEY; //Done. Clear WRT
56+
FCTL3 = FWKEY+LOCK; // Set LOCK
57+
}
58+
59+
MspFlashClass Flash;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
MspFlash.h - Read/Write flash memory library for MSP430 Energia
3+
Copyright (c) 2012 Peter Brier. All right reserved.
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18+
19+
Provide access to the MSP430 flash memory controller.
20+
All flash memory can be read, erased and written (except SEGMENT_A, the LOCK bits are not in the code, for a good reason).
21+
Flash can only be erased per 512 byte segments (except the 4 special information segments, they are 64 bytes in size)
22+
23+
The same flash locations can be written multiple times with new values, but flash bits can only be reset (from 1 to 0) and cannot
24+
change to a 1 (you need to flash erase the whole segment)
25+
26+
functions:
27+
~~~~~~~~~~
28+
erase(): Erase a flash segment, all bytes in the segment will read 0xFF after an erase
29+
30+
read(): Read flash locations (actually just a proxy for memcpy)
31+
32+
write(): Write flash locations (actually just a proxy for memcpy), the same location can be written multiple times,
33+
but once a bit is reset, you cannot set it with a subsequent write, you need to flash the complete segment to do so.
34+
35+
NOTE: you are flashing the program memory, you can modify EVERYTHING (program and data) this is usually not what you want.
36+
Be carefull when flashing data. You may use SEG_B to SEG_D for normal use, they should not be filled by the compiler with code
37+
or static data. If you wish to use main memory without wasting any space, you need to inform the linker NOT to use the required
38+
segments. This is done in the linker script (this is not for the faint of heart).
39+
40+
An alternative approach is to allocate a static variable with a size of AT LEAST 2 SEGMENTS in your program.
41+
This makes sure there is at least ONE COMPLETE SEGMENT in this static variable, so there is no colleteral damage when you flash this
42+
area. You need to find the pointer to the start of the next segment. There is a macro define to do this: SEGPTR(x)
43+
A example that makes 2 segments available for flashing by allocating 3 segments of constant data:
44+
45+
static const unsigned char flash[3*512] = {0};
46+
47+
Flash segments: |...SEGMENT-0...|...SEGMENT-1...|...SEGMENT-2...|...SEGMENT-3...|...SEGMENT-4...|...SEGMENT-5...|
48+
Program output: |Program code.......|flash[3*512***********************************]|Other data.................|
49+
Available segments: |-------------------------------|+++++++++++++++|+++++++++++++++|-------------------------------|
50+
51+
52+
*/
53+
54+
#ifndef MSP_FLASH_h
55+
#define MSP_FLASH_h
56+
57+
#define SEGMENT_SIZE 512 // main segment size (smallest flasheable area)
58+
#define INFO_SIZE 64 // information flah sizes (SEG_A to SEG_D)
59+
60+
// segment addresses of 64 byte segments
61+
#define SEGMENT_D ((unsigned char*)0x1000)
62+
#define SEGMENT_C ((unsigned char*)0x1000+64)
63+
#define SEGMENT_B ((unsigned char*)0x1000+128)
64+
// #define SEGMENT_A ((unsigned char*)0x1000+192) // NOTE: Contains chip calibarion data, do not change or erase (the protection bit should prevent this happening)
65+
66+
// Segment start addresses of 512 byte segments 0..63 (depending on device flash size)
67+
#define SEGMENT(x) ( (unsigned char*) 0xFFFF - (x)*SEG_SIZE )
68+
69+
// return segment pointer inside variable. Note: return start of NEXT segment
70+
#define SEGPTR(x) ( (unsigned char *) (((unsigned short)(&x)+512) & 0xFE00) )
71+
72+
class MspFlashClass
73+
{
74+
public:
75+
void erase(unsigned char *flash);
76+
void read(unsigned char *flash, unsigned char *dest, int len);
77+
void write(unsigned char *flash, unsigned char *src, int len);
78+
};
79+
80+
extern MspFlashClass Flash;
81+
82+
#endif // MSP_FLASH_h
83+

0 commit comments

Comments
 (0)