Hardware components | ||||||
![]() |
| × | 3 | |||
![]() |
| × | 3 | |||
![]() |
| × | 3 | |||
| × | 3 | ||||
| × | 3 | ||||
| × | 3 | ||||
Software apps and online services | ||||||
![]() |
| |||||
Hand tools and fabrication machines | ||||||
| ||||||
![]() |
| |||||
|
Often times in the workplace it is beneficial to have the ability to speak to someone in the same building without having to get up and walk over to them. Phones have been replaced with Skype (and similar programs), but the Shred box is the future.
The Shred Box makes it easy to have a conversation or discuss plans with friends or fellow employees without the hassle of having to log onto a communication program or move all your belongings into the same room. All you have to do is add power and the Shred Box is ready to send and receive voice communication to anyone on the same wifi.
Here is our data that we collected from the Shred Box. We used IFTTT to push real time data into a Google sheet for each time the microphone was on or off for one device, speaking and listening respectively. This graph shows our data collected from one session.
https://docs.google.com/spreadsheets/d/1AN7j4PlPLQgZHO0xPR81TKfKnssPQ5tLvg7NK17dWPI/edit?usp=sharing
For this project we built three ShredBoxes. This system utilizes wifi connection to allow the photons to connect. Using a broadcasting IP address, the photon uses the microphone and sends the audio over the wifi for the other photons to detect, capture, and play through the speaker. For this system to work, all photons must be all connected to the same wifi because this is how the photons communicate with one another. The signal begins to be lost when connected to multi-router/extender systems like the one at UNCC. Additionally, it may not work on other public wifis because of security reasons set up by owner. It is best to utilize a wifi that you have permissions to and does not receive heavy traffic. When it comes to the hardware of the board, soldering all connections greatly enhances the audio quality produced by the speaker.
Shred Box wiring diagram
#include "SparkIntervalTimer.h"
#include "SimpleRingBuffer.h"
//WiFi.selectAntenna(ANT_EXTERNAL);
#define MICROPHONE_PIN DAC1
#define SPEAKER_PIN DAC2
#define BUTTON_PIN A0
#define BROADCAST_PORT 3443
#define UDP_BROADCAST_PORT 3444
#define AUDIO_BUFFER_MAX 8192
//#define SERIAL_DEBUG_ON true
#define AUDIO_TIMING_VAL 125 /* 8,000 hz */
//#define AUDIO_TIMING_VAL 62 /* 16,000 hz */
//#define AUDIO_TIMING_VAL 50 /* 20,000 hz */
UDP Udp;
IPAddress broadcastAddress(255,255,255,255);
int audioStartIdx = 0, audioEndIdx = 0;
int rxBufferLen = 0, rxBufferIdx = 0;
//uint16_t audioBuffer[AUDIO_BUFFER_MAX];
uint8_t txBuffer[AUDIO_BUFFER_MAX];
//uint8_t rxBuffer[AUDIO_BUFFER_MAX];
SimpleRingBuffer audio_buffer;
SimpleRingBuffer recv_buffer;
// IntervalTimer readMicTimer;
// IntervalTimer sendAudioTimer;
// version without timers
unsigned long lastRead = micros();
unsigned long lastSend = millis();
char myIpAddress[24];
TCPClient audioClient;
TCPClient checkClient;
TCPServer audioServer = TCPServer(BROADCAST_PORT);
IntervalTimer readMicTimer;
//int led_state = 0;
float _volumeRatio = 0.25;
int _sendBufferLength = 0;
unsigned int lastPublished = 0;
void setup() {
#if SERIAL_DEBUG_ON
Serial.begin(115200);
#endif
pinMode(MICROPHONE_PIN, INPUT);
pinMode(SPEAKER_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLDOWN);
pinMode(D7, OUTPUT);
Particle.function("setVolume", onSetVolume);
Particle.variable("ipAddress", myIpAddress, STRING);
IPAddress myIp = WiFi.localIP();
sprintf(myIpAddress, "%d.%d.%d.%d", myIp[0], myIp[1], myIp[2], myIp[3]);
recv_buffer.init(AUDIO_BUFFER_MAX);
audio_buffer.init(AUDIO_BUFFER_MAX);
Udp.setBuffer(1024);
Udp.begin(UDP_BROADCAST_PORT);
// Udp.beginPacket(broadcastAddress, UDP_BROADCAST_PORT);
// Udp.write(rxBuffer, 10);
// Udp.endPacket();
// 1/16,000th of a second is ~62 mcsec
//readMicTimer.begin(readMic, 62, uSec);
// // send a chunk of audio every 1/2 second
// sendAudioTimer.begin(sendAudio, 1000, hmSec);
audioServer.begin();
lastRead = micros();
}
bool _isRecording = false;
void startRecording() {
if (!_isRecording) {
// 1/8000th of a second is 125 microseconds
readMicTimer.begin(readMic, AUDIO_TIMING_VAL, uSec);
}
_isRecording = true;
}
void stopRecording() {
if (_isRecording) {
readMicTimer.end();
}
_isRecording = false;
}
void loop() {
// checkClient = audioServer.available();
// if (checkClient.connected()) {
// audioClient = checkClient;
// }
//listen for 100ms, taking a sample every 125us,
//and then send that chunk over the network.
//listenAndSend(100);
if (digitalRead(BUTTON_PIN) == HIGH) {
digitalWrite(D7, HIGH);
startRecording();
Particle.publish("Light" , "Speaking" , PUBLIC);
delay(2000);
System.sleep(40);
sendEvery(100);
}
else {
digitalWrite(D7, LOW);
Particle.publish("Light" , "Listening", PUBLIC);
delay(2000);
System.sleep(40);
stopRecording();
}
readAndPlay();
//led_state = !led_state;
}
int onSetVolume(String cmd) {
_volumeRatio = cmd.toFloat() / 100;
}
void readAndPlay() {
while (Udp.parsePacket() > 0) {
while (Udp.available() > 0) {
recv_buffer.put(Udp.read());
}
if (recv_buffer.getSize() == 0) {
analogWrite(SPEAKER_PIN, 0);
}
}
// #if SERIAL_DEBUG_ON
// Serial.println("received " + String(count));
// #endif
// //read as much as we can off the buffer.
// rxBufferLen = Udp.read(rxBuffer, AUDIO_BUFFER_MAX);
// rxBufferIdx = 0;
playRxAudio();
}
void playRxAudio() {
unsigned long lastWrite = micros();
unsigned long now, diff;
int value;
//toggleLED();
//noInterrupts();
//while (rxBufferIdx < rxBufferLen) {
while (recv_buffer.getSize() > 0) {
// ---
//map it back from 1 byte to 2 bytes
//map(value, fromLow, fromHigh, toLow, toHigh);
//value = map(rxBuffer[rxBufferIdx++], 0, 255, 0, 4095);
//play audio
value = recv_buffer.get();
value = map(value, 0, 255, 0, 4095);
value = value * _volumeRatio;
now = micros();
diff = (now - lastWrite);
if (diff < AUDIO_TIMING_VAL) {
delayMicroseconds(AUDIO_TIMING_VAL - diff);
}
//analogWrite(SPEAKER_PIN, rxBuffer[rxBufferIdx++]);
analogWrite(SPEAKER_PIN, value);
lastWrite = micros();
}
//interrupts();
//toggleLED();
}
void listenAndSend(int delay) {
unsigned long startedListening = millis();
while ((millis() - startedListening) < delay) {
unsigned long time = micros();
if (lastRead > time) {
// time wrapped?
//lets just skip a beat for now, whatever.
lastRead = time;
}
//125 microseconds is 1/8000th of a second
if ((time - lastRead) > 125) {
lastRead = time;
readMic();
}
}
sendAudio();
}
void sendEvery(int delay) {
// #if SERIAL_DEBUG_ON
// Serial.println("sendEvery");
// #endif
// if it's been longer than 100ms since our last broadcast, then broadcast.
if ((millis() - lastSend) >= delay) {
sendAudio();
lastSend = millis();
}
}
// Callback for Timer 1
void readMic(void) {
//read audio
uint16_t value = analogRead(MICROPHONE_PIN);
value = map(value, 0, 4095, 0, 255);
audio_buffer.put(value);
//old
// if (audioEndIdx >= AUDIO_BUFFER_MAX) {
// audioEndIdx = 0;
// }
// audioBuffer[audioEndIdx++] = value;
// //play audio
// value = map(recv_buffer.get(), 0, 255, 0, 4095);
// if (value >= 0) {
// analogWrite(SPEAKER_PIN, value);
// }
// //play audio
// if (rxBufferIdx < rxBufferLen) {
//
//// uint8_t lsb = rxBuffer[rxBufferIdx];
//// uint8_t msb = rxBuffer[rxBufferIdx+1];
//// rxBufferIdx +=2;
//// uint16_t value = ((msb << 8) | (lsb & 0xFF));
//// value = (value / 65536.0) * 4095.0;
//// analogWrite(SPEAKER_PIN, value);
//
// //tcpBuffer[tcpIdx] = map(val, 0, 4095, 0, 255);
// analogWrite(SPEAKER_PIN, rxBuffer[rxBufferIdx++]);
// }
//digitalWrite(D7, (led_state) ? HIGH : LOW);
// if (rxBufferIdx < rxBufferLen) {
// int value = map(rxBuffer[rxBufferIdx++], 0, 255, 0, 4095);
// analogWrite(SPEAKER_PIN, value);
// }
}
void copyAudio(uint8_t *bufferPtr) {
int c = 0;
while ((audio_buffer.getSize() > 0) && (c < AUDIO_BUFFER_MAX)) {
bufferPtr[c++] = audio_buffer.get();
}
_sendBufferLength = c - 1;
// //if end is after start, read from start->end
// //if end is before start, then we wrapped, read from start->max, 0->end
//
// int endSnapshotIdx = audioEndIdx;
// bool wrapped = endSnapshotIdx < audioStartIdx;
// int endIdx = (wrapped) ? AUDIO_BUFFER_MAX : endSnapshotIdx;
//
//
// for(int i=audioStartIdx;i<endIdx;i++) {
// // do a thing
// bufferPtr[c++] = audioBuffer[i];
// }
//
// if (wrapped) {
// //we have extra
// for(int i=0;i<endSnapshotIdx;i++) {
// // do more of a thing.
// bufferPtr[c++] = audioBuffer[i];
// }
// }
//
// //and we're done.
// audioStartIdx = audioEndIdx;
//
// if (c < AUDIO_BUFFER_MAX) {
// bufferPtr[c] = -1;
// }
// _sendBufferLength = c;
}
// Callback for Timer 1
void sendAudio(void) {
// #if SERIAL_DEBUG_ON
// Serial.println("sendAudio");
// #endif
copyAudio(txBuffer);
int i=0;
uint16_t val = 0;
if (audioClient.connected()) {
write_socket(audioClient, txBuffer);
}
else {
write_UDP(txBuffer);
}
// else {
// while( (val = txBuffer[i++]) < 65535 ) {
// Serial.print(val);
// Serial.print(',');
// }
// Serial.println("DONE");
// }
}
void write_socket(TCPClient socket, uint8_t *buffer) {
int i=0;
uint16_t val = 0;
int tcpIdx = 0;
uint8_t tcpBuffer[1024];
while( (val = buffer[i++]) < 65535 ) {
if ((tcpIdx+1) >= 1024) {
socket.write(tcpBuffer, tcpIdx);
tcpIdx = 0;
}
tcpBuffer[tcpIdx] = val & 0xff;
tcpBuffer[tcpIdx+1] = (val >> 8);
tcpIdx += 2;
}
// any leftovers?
if (tcpIdx > 0) {
socket.write(tcpBuffer, tcpIdx);
}
}
void write_UDP(uint8_t *buffer) {
int stopIndex=_sendBufferLength;
// uint16_t val = 0;
// int tcpIdx = 0;
// uint8_t tcpBuffer[1024];
// while (( buffer[stopIndex++] < 4096 ) && (stopIndex < AUDIO_BUFFER_MAX)) {
// ;
// }
#if SERIAL_DEBUG_ON
Serial.println("SENDING " + String(stopIndex));
#endif
Udp.sendPacket(buffer, stopIndex, broadcastAddress, UDP_BROADCAST_PORT);
//Udp.beginPacket(broadcastAddress, UDP_BROADCAST_PORT);
// while( (val = buffer[i++]) < 4096 ) {
// if ((tcpIdx+1) >= 1024) {
//
// //works
//// Udp.beginPacket(broadcastAddress, UDP_BROADCAST_PORT);
//// Udp.write(tcpBuffer, tcpIdx);
//// Udp.endPacket();
//
// //Doesn't work
// Udp.sendPacket(tcpBuffer, tcpIdx, broadcastAddress, UDP_BROADCAST_PORT);
//
//
// #if SERIAL_DEBUG_ON
// Serial.println("SENT " + String(tcpIdx));
// #endif
// //delay(5);
//
//
// tcpIdx = 0;
// toggleLED();
// }
//
// //map(value, fromLow, fromHigh, toLow, toHigh);
// tcpBuffer[tcpIdx] = val; //map(val, 0, 4095, 0, 255);
// tcpIdx++;
//
//// tcpBuffer[tcpIdx] = val & 0xff;
//// tcpBuffer[tcpIdx+1] = (val >> 8);
//// tcpIdx += 2;
// }
//
// // any leftovers?
// if (tcpIdx > 0) {
// //works
//// Udp.beginPacket(broadcastAddress, UDP_BROADCAST_PORT);
//// Udp.write(tcpBuffer, tcpIdx);
//// Udp.endPacket();
//
// //doesn't work
// Udp.sendPacket(tcpBuffer, tcpIdx, broadcastAddress, UDP_BROADCAST_PORT);
//
// #if SERIAL_DEBUG_ON
// Serial.println("SENT " + String(tcpIdx));
// #endif
// }
//toggleLED();
}
bool ledState = false;
void toggleLED() {
ledState = !ledState;
digitalWrite(D7, (ledState) ? HIGH : LOW);
}
/* Copyright (c) 2014 Paul Kourany, based on work by Dianel Gilbert
UPDATED Sept 3, 2015 - Added support for Particle Photon
Copyright (c) 2013 Daniel Gilbert, loglow@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in the
Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include "application.h"
#include "SimpleRingBuffer.h"
SimpleRingBuffer::SimpleRingBuffer() {
}
void SimpleRingBuffer::init(unsigned int size) {
cur_idx = 0; //pos
cur_len = 0; //len
_data = (uint8_t*)malloc(size * sizeof(uint8_t));
data_size = size; //cap
}
bool SimpleRingBuffer::put(uint8_t value) {
if (cur_len < data_size) {
// get our index, wrap on buffer size
_data[(cur_idx + cur_len)%data_size] = value;
cur_len++;
return true;
}
return false;
}
uint8_t SimpleRingBuffer::get() {
// lets return 0 if we don't have anything
uint8_t val = 0;
if (cur_len > 0) {
// grab the value from the current index
val = _data[cur_idx];
// move to next, wrap as necessary, shrink length
cur_idx = (cur_idx + 1) % data_size;
cur_len--;
}
return val;
}
unsigned int SimpleRingBuffer::getSize() {
return cur_len;
}
unsigned int SimpleRingBuffer::getCapacity() {
return data_size;
}
void SimpleRingBuffer::clear() {
cur_idx = 0;
cur_len = 0;
}
void SimpleRingBuffer::destroy() {
free(_data);
_data = NULL;
}
#include "SparkIntervalTimer.h"
bool IntervalTimer::SIT_used[];
IntervalTimer::ISRcallback IntervalTimer::SIT_CALLBACK[];
// ------------------------------------------------------------
#if defined(STM32F10X_MD) || !defined(PLATFORM_ID) //Core
void Wiring_TIM2_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[0]();
}
}
void Wiring_TIM3_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[1]();
}
}
void Wiring_TIM4_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[2]();
}
}
#elif defined(STM32F2XX) && defined(PLATFORM_ID) //Photon
void Wiring_TIM3_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[0]();
}
}
void Wiring_TIM4_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[1]();
}
}
void Wiring_TIM5_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM5, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[2]();
}
}
void Wiring_TIM6_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[3]();
}
}
void Wiring_TIM7_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
IntervalTimer::SIT_CALLBACK[4]();
}
}
#else
#error "*** PARTICLE device not supported by this library. PLATFORM should be Core or Photon ***"
#endif
// ------------------------------------------------------------
// this function inits and starts the timer, using the specified
// function as a callback and the period provided. must be passed
// the name of a function taking no arguments and returning void.
// make sure this function can complete within the time allowed.
// attempts to allocate a timer using available resources,
// returning true on success or false in case of failure.
// Period units is defined by scale, where scale = uSec or hmSec
// and = 1-65535 microsecond (uSec)
// or 1-65535 0.5ms increments (hmSec)
// ------------------------------------------------------------
bool IntervalTimer::beginCycles(void (*isrCallback)(), intPeriod Period, bool scale, TIMid id) {
// if this interval timer is already running, stop and deallocate it
if (status == TIMER_SIT) {
stop_SIT();
status = TIMER_OFF;
}
// store callback pointer
myISRcallback = isrCallback;
if (id > NUM_SIT) { // Allocate specified timer (id=0 to 2/4) or auto-allocate from pool (id=255)
// attempt to allocate this timer
if (allocate_SIT(Period, scale, id)) status = TIMER_SIT; //255 means allocate from pool
else status = TIMER_OFF;
}
else {
// attempt to allocate this timer
if (allocate_SIT(Period, scale, AUTO)) status = TIMER_SIT; //255 means allocate from pool
else status = TIMER_OFF;
}
// check for success and return
if (status != TIMER_OFF) return true;
return false;
}
// ------------------------------------------------------------
// enables the SIT clock if not already enabled, then checks to
// see if any SITs are available for use. if one is available,
// it's initialized and started with the specified value, and
// the function returns true, otherwise it returns false
// ------------------------------------------------------------
bool IntervalTimer::allocate_SIT(intPeriod Period, bool scale, TIMid id) {
if (id < NUM_SIT) { // Allocate specified timer (id=TIMER3/4/5) or auto-allocate from pool (id=AUTO)
if (!SIT_used[id]) {
start_SIT(Period, scale);
SIT_used[id] = true;
return true;
}
}
else {
// Auto allocate - check for an available SIT, and if so, start it
for (uint8_t tid = 0; tid < NUM_SIT; tid++) {
if (!SIT_used[tid]) {
SIT_id = tid;
start_SIT(Period, scale);
SIT_used[tid] = true;
return true;
}
}
}
// Specified or no auto-allocate SIT available
return false;
}
// ------------------------------------------------------------
// configuters a SIT's TIMER registers, etc and enables
// interrupts, effectively starting the timer upon completion
// ------------------------------------------------------------
void IntervalTimer::start_SIT(intPeriod Period, bool scale) {
TIM_TimeBaseInitTypeDef timerInitStructure;
NVIC_InitTypeDef nvicStructure;
intPeriod prescaler;
TIM_TypeDef* TIMx;
//use SIT_id to identify TIM#
switch (SIT_id) {
#if defined(STM32F10X_MD) || !defined(PLATFORM_ID) //Core
case 0: // TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
TIMx = TIM2;
break;
case 1: // TIM3
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 2: // TIM4
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
#elif defined(STM32F2XX) && defined(PLATFORM_ID) //Photon
case 0: // TIM3
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 1: // TIM4
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
case 2: // TIM5
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM5_IRQn;
TIMx = TIM5;
break;
case 3: // TIM6
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM6_DAC_IRQn;
TIMx = TIM6;
break;
case 4: // TIM7
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM7_IRQn;
TIMx = TIM7;
break;
#endif
}
// Initialize Timer
switch (scale) {
case uSec:
prescaler = SIT_PRESCALERu; // Set prescaler for 1MHz clock, 1us period
break;
case hmSec:
prescaler = SIT_PRESCALERm; // Set prescaler for 2Hz clock, .5ms period
break;
default:
prescaler = SIT_PRESCALERu;
scale = uSec; // Default to microseconds
break;
}
timerInitStructure.TIM_Prescaler = prescaler;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitStructure.TIM_Period = Period;
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
timerInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIMx, &timerInitStructure);
TIM_Cmd(TIMx, ENABLE);
TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
// point to the correct SIT ISR
SIT_CALLBACK[SIT_id] = myISRcallback;
//Enable Timer Interrupt
nvicStructure.NVIC_IRQChannelPreemptionPriority = 10;
nvicStructure.NVIC_IRQChannelSubPriority = 1;
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvicStructure);
}
// ------------------------------------------------------------
// stop the timer if it's currently running, using its status
// to determine what hardware resources the timer may be using
// ------------------------------------------------------------
void IntervalTimer::end() {
if (status == TIMER_SIT) stop_SIT();
status = TIMER_OFF;
}
// ------------------------------------------------------------
// stops an active SIT by disabling its interrupt and TIMER
// and freeing up its state for future use.
// ------------------------------------------------------------
void IntervalTimer::stop_SIT() {
NVIC_InitTypeDef nvicStructure;
TIM_TypeDef* TIMx;
//use SIT_id to identify TIM#
switch (SIT_id) {
#if defined(STM32F10X_MD) || !defined(PLATFORM_ID) //Core
case 0: // TIM2
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
TIMx = TIM2;
break;
case 1: // TIM3
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 2: // TIM4
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
#elif defined(STM32F2XX) && defined(PLATFORM_ID) //Photon
case 0: // TIM3
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 1: // TIM4
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
case 2: // TIM5
nvicStructure.NVIC_IRQChannel = TIM5_IRQn;
TIMx = TIM5;
break;
case 3: // TIM6
nvicStructure.NVIC_IRQChannel = TIM6_DAC_IRQn;
TIMx = TIM6;
break;
case 4: // TIM7
nvicStructure.NVIC_IRQChannel = TIM7_IRQn;
TIMx = TIM7;
break;
#endif
}
// disable counter
TIM_Cmd(TIMx, DISABLE);
// disable interrupt
nvicStructure.NVIC_IRQChannelCmd = DISABLE;
NVIC_Init(&nvicStructure);
// disable timer peripheral
TIM_DeInit(TIMx);
// free SIT for future use
SIT_used[SIT_id] = false;
}
// ------------------------------------------------------------
// Enables or disables an active SIT's interrupt without
// removing the SIT.
// ------------------------------------------------------------
void IntervalTimer::interrupt_SIT(action ACT)
{
NVIC_InitTypeDef nvicStructure;
TIM_TypeDef* TIMx;
//use SIT_id to identify TIM#
switch (SIT_id) {
#if defined(STM32F10X_MD) || !defined(PLATFORM_ID) //Core
case 0: // TIM2
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
TIMx = TIM2;
break;
case 1: // TIM3
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 2: // TIM4
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
#elif defined(STM32F2XX) && defined(PLATFORM_ID) //Photon
case 0: // TIM3
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 1: // TIM4
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
case 2: // TIM5
nvicStructure.NVIC_IRQChannel = TIM5_IRQn;
TIMx = TIM5;
break;
case 3: // TIM6
nvicStructure.NVIC_IRQChannel = TIM6_DAC_IRQn;
TIMx = TIM6;
break;
case 4: // TIM7
nvicStructure.NVIC_IRQChannel = TIM7_IRQn;
TIMx = TIM7;
break;
#endif
}
switch (ACT) {
case INT_ENABLE:
//Enable Timer Interrupt
nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
nvicStructure.NVIC_IRQChannelSubPriority = 1;
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvicStructure);
break;
case INT_DISABLE:
// disable interrupt
nvicStructure.NVIC_IRQChannelCmd = DISABLE;
NVIC_Init(&nvicStructure);
break;
default:
//Do nothing
break;
}
}
// ------------------------------------------------------------
// Set new period for the SIT without
// removing the SIT.
// ------------------------------------------------------------
void IntervalTimer::resetPeriod_SIT(intPeriod newPeriod, bool scale)
{
//TIM_TimeBaseInitTypeDef timerInitStructure;
TIM_TypeDef* TIMx;
intPeriod prescaler;
//use SIT_id to identify TIM#
switch (SIT_id) {
#if defined(STM32F10X_MD) || !defined(PLATFORM_ID) //Core
case 0: // TIM2
TIMx = TIM2;
break;
case 1: // TIM3
TIMx = TIM3;
break;
case 2: // TIM4
TIMx = TIM4;
break;
#elif defined(STM32F2XX) && defined(PLATFORM_ID) //Photon
case 0: // TIM3
TIMx = TIM3;
break;
case 1: // TIM4
TIMx = TIM4;
break;
case 2: // TIM5
TIMx = TIM5;
break;
case 3: // TIM6
TIMx = TIM6;
break;
case 4: // TIM7
TIMx = TIM7;
break;
#endif
}
switch (scale) {
case uSec:
prescaler = SIT_PRESCALERu; // Set prescaler for 1MHz clock, 1us period
break;
case hmSec:
prescaler = SIT_PRESCALERm; // Set prescaler for 2Hz clock, .5ms period
break;
default:
scale = uSec; // Default to microseconds
prescaler = SIT_PRESCALERu;
break;
}
TIMx->ARR = newPeriod;
TIMx->PSC = prescaler;
TIMx->EGR = TIM_PSCReloadMode_Immediate;
TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
}
// ------------------------------------------------------------
// Returns -1 if timer not allocated or sid number:
// 0 = TMR2, 1 = TMR3, 2 = TMR4
// ------------------------------------------------------------
int8_t IntervalTimer::isAllocated_SIT(void)
{
if (status == TIMER_SIT)
return -1;
else
return SIT_id;
}
#ifndef SIMPLERINGBUFFER_H
#define SIMPLERINGBUFFER_H
class SimpleRingBuffer {
protected:
uint8_t* _data;
unsigned int data_size;
unsigned int cur_idx;
unsigned int cur_len;
public:
SimpleRingBuffer();
void init(unsigned int size);
bool put(uint8_t value);
uint8_t get();
unsigned int getSize();
unsigned int getCapacity();
void clear();
void destroy();
};
#endif
Comments