siwicom/C/SiWiCom.c

793 lines
23 KiB
C

/*================================================================================================
SiWiCom.c
SiWiCom - Simple Wireless Communication
copyright 2006 v1.0 (2006-06-22)
Name: Joakim Nilsson E-mail: mail@jopin.se
Name: Christoffer Martinsson E-mail: cm@cmtec.se
================================================================================================*/
#include "hardware.h"
#include <util/delay.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include "spi.h"
#include "cc1100.h"
#include "rfProtocol.h"
#include "pcProtocol.h"
#include "usart.h"
#define ERR_COUNTER_THR 10
// Counter related declarations
static uint8_t errCounter = 0;
static uint16_t timeOutCounter = 0;
static uint8_t wakeUpCounter = 0; // Wakeup counter
static uint8_t wakeUpValue = 0; // Time to "fully" wakeup (sec)
static uint8_t wakeUpValueSleep = 5;
// Data related declarations
static uint8_t inputBuffer[6]; // Buffer for incoming data
static uint8_t outputBuffer[3] = {0,0,0}; // Buffer for outgoing data
static uint8_t updateBuffer[16][3];
// Program state related declarations
enum {IDLE, MASTER, SLAVE, NOICEMAKER};
static uint8_t programMode;
static uint8_t remoteMode;
static uint8_t programModeTemp;
// RF related declarations
/* Chipcon */
/* Product = CC1100 */
/* Crystal accuracy = 25 ppm */
/* X-tal frequency = 26 MHz */
/* RF output power = 0 dBm */
/* RX filterbandwidth = 101.562500 kHz */
/* Deviation = 19 kHz */
/* Datarate = 9.992599 kbps */
/* Modulation = (1) 2-GFSK */
/* Manchester enable = (1) Manchester enabled */
/* RF Frequency = 868.299866 MHz */
/* Channel spacing = 199.951172 kHz */
/* Channel number = 0 */
/* Optimization = - */
/* Sync mode = (3) 30/32 sync word bits detected */
/* Format of RX/TX data = (0) Normal mode, use FIFOs for RX and TX */
/* CRC operation = (1) CRC calculation in TX and CRC check in RX enabled */
/* Forward Error Correction = (0) FEC disabled */
/* Packetlength = 6 */
/* Preamble count = (2) 4 bytes */
/* Append status = 1 */
/* Address check = (0) No address check */
uint8_t rfSetupData[] = {
0x0A, // IOCFG2
0x2E, // IOCFG1
0x80, // IOCFG0D
0x07, // FIFOTHR
0xD3, // SYNC1
0x91, // SYNC0
0x06, // PKTLEN
0x04, // PKTCTRL1
0x44, // PKTCTRL0
0x00, // ADDR
0x00, // CHANNR
0x06, // FSCTRL1
0x00, // FSCTRL0
0x21, // FREQ2
0x65, // FREQ1
0x6A, // FREQ0
0xC8, // MDMCFG4
0x93, // MDMCFG3
0x1B, // MDMCFG2
0x22, // MDMCFG1
0xF8, // MDMCFG0
0x34, // DEVIATN
0x07, // MCSM2
0x30, // MCSM1
0x18, // MCSM0
0x16, // FOCCFG
0x6C, // BSCFG
0x43, // AGCCTRL2
0x40, // AGCCTRL1
0x91, // AGCCTRL0
0x87, // WOREVT1
0x6B, // WOREVT0
0xF8, // WORCTRL
0x56, // FREND1
0x10, // FREND0
0xE9, // FSCAL3
0x2A, // FSCAL2
0x00, // FSCAL1
0x1F, // FSCAL0
0x41, // RCCTRL1
0x00, // RCCTRL0
0x59, // FSTEST
0x7F, // PTEST
0x3F, // AGCTST
0x81, // TEST2
0x35, // TEST1
0x09 // TEST0
};
int main (void);
/*================================================================================================
Functions
================================================================================================*/
/*================================================================================================
flashLed
Description: Flash LED.
Input: led (LED2, LED3, LED4, LED5)
Output: -
------------------------------------------------------------------------------------------------*/
void flashLed(uint8_t led)
{
if(LED_PORT & (1<<led))
{
LED_PORT &= ~(1<<led);
_delay_ms(10);
LED_PORT |= (1<<led);
}
else
{
LED_PORT |= (1<<led);
_delay_ms(10);
LED_PORT &= ~(1<<led);
}
}
/*================================================================================================
enterSleepMode
Description: Enter sleep mode for ATmega48
(Only start timer2 if USB is active, and not enter SLEEP mode)
Input: newWakeUpValue (time to next wakeup from SLEEP mode)
newCompareValue (compare value for timer2. 0 or 255 = 8sec trigg, 32 = 1sec trigg)
Output: -
------------------------------------------------------------------------------------------------*/
void enterSleepMode(uint8_t newWakeUpValue, uint8_t newCompareValue)
{
wakeUpValue = newWakeUpValue; // set wakeUpValue to 5sec //1 minute
wakeUpCounter = 0; // Clear wakeUpCounter
cli();
TCCR2B |=(1<<CS20)|(1<<CS21)|(1<<CS22); // Enable timer2 (Prescaler = 1024)
if(USB_ACTIVE_PIN & (1<<USB_ACTIVE))
{
OCR2A = newCompareValue; // Timer2 compare value (32 -> 1sec)
TCNT2 = 0; // Reset Timer2 counter register
while(ASSR &((1<<OCR2AUB)|(1<<TCN2UB))); // Wait until safe to sleep. s155
while((wakeUpCounter < wakeUpValue) && (remoteMode == SLAVE))
{
if(TIFR2 & (1<<OCF2A))
{
wakeUpCounter++;
TIFR2 |= (1<<OCF2A);
}
}
}
else
{
while(wakeUpCounter++ < wakeUpValue)
{
OCR2A = newCompareValue; // Timer2 compare value (32 -> 1sec)
TCNT2 = 0; // Reset Timer2 counter register
while(ASSR &((1<<OCR2AUB)|(1<<TCN2UB))); // Wait until safe to sleep. s155
SMCR |= (1<<SE); // Sleep mode enable
SMCR |= (1<<SM1)|(1<<SM0); // Sleep mode = power save
sei(); // Enable global interrupt
sleep_cpu(); // Enter sleep mode
}
}
TCCR2B &= ~((1<<CS20)|(1<<CS21)|(1<<CS22)); // Disable timer2
sei();
cc1100WriteCommand(SIDLE); // cc1100 = IDLE
}
/*================================================================================================
loadSettingsFromEEPROM
Description: Load settings from EEPROM
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
void loadSettingsFromEEPROM(void)
{
uint8_t *n = 0;
eeprom_busy_wait();
rfProtocolSetMasterAddr(eeprom_read_byte(n++)); // Default target MASTER address
eeprom_busy_wait();
rfProtocolSetDeviceAddr(eeprom_read_byte(n++)); // Device address for this module
}
/*================================================================================================
saveSettingsToEEPROM
Description: Save settings to EEPROM
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
void saveSettingsToEEPROM(void)
{
uint8_t *n = 0;
eeprom_busy_wait();
eeprom_write_byte(n++,rfProtocolGetMasterAddr());
eeprom_busy_wait();
eeprom_write_byte(n++,rfProtocolGetDeviceAddr());
}
/*================================================================================================
activateRxMode
Description: Activate cc1100 RX mode. This will flush cc1100 RXFIFO and then set cc1100 in
RX mode
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
void activateRxMode(void)
{
cc1100WriteCommand(SFRX); // Flush cc1100 RXFIFO
cc1100WriteCommand(SRX); // cc1100 = RX and clear CRC_OK flag
// wait for cc1100 to settle from calibration and enter RX mode
while(!(cc1100ReadStatusReg(MARCSTATE) == 0x0D));
}
/*================================================================================================
getPowerFromDipSW
Description: Get power settings from Dip-switch
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
uint8_t getPowerFromDipSW(void)
{
SW_DDR &= ~((1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4)); // Set pins as input
SW_PORT |= (1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4); // Enable pullup on dipSW
_delay_us(2);
uint8_t tmpSW = 0;
// DipSwitch power settings
if((SW_PIN & ((1<<SW3)|(1<<SW4))) == ((1<<SW3)|(1<<SW4)))
{
tmpSW = 0x60;
}
else if((SW_PIN & ((1<<SW3)|(1<<SW4))) == (1<<SW3))
{
tmpSW = 0x85;
}
else if((SW_PIN & ((1<<SW3)|(1<<SW4))) == (1<<SW4))
{
tmpSW = 0xCC;
}
else
{
tmpSW = 0xC3;
}
SW_PORT &= ~((1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4)); // Disable pullup on dipSW
SW_DDR |= (1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4); // Set pins as output
return tmpSW;
}
/*================================================================================================
getProgramModeFromDipSW
Description: Get power settings from Dip-switch
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
uint8_t getProgramModeFromDipSW(void)
{
SW_DDR &= ~((1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4)); // Set pins as input
SW_PORT |= (1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4); // Enable pullup on dipSW
_delay_us(2);
uint8_t tmpSW = 0;
// DipSwitch program settings
if((SW_PIN & ((1<<SW1)|(1<<SW2))) == ((1<<SW1)|(1<<SW2)))
{
tmpSW = IDLE;
}
else if((SW_PIN & ((1<<SW1)|(1<<SW2))) == (1<<SW1))
{
tmpSW = MASTER;
}
else if((SW_PIN & ((1<<SW1)|(1<<SW2))) == (1<<SW2))
{
tmpSW = SLAVE;
}
else
{
tmpSW = NOICEMAKER;
}
SW_PORT &= ~((1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4)); // Disable pullup on dipSW
SW_DDR |= (1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4); // Set pins as output
return tmpSW;
}
/*================================================================================================
resetCC1100
Description: Reset cc11100 and init all register in cc1100
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
void resetCC1100(void)
{
spiChipSelect(CS_CC1100);
_delay_ms(1);
spiChipDeSelect();
_delay_ms(40);
spiChipSelect(CS_CC1100);
cc1100WriteCommand(SRES); // cc1100 = RESET
cc1100WriteBurstReg(0x00, rfSetupData, sizeof(rfSetupData)); // Set all config register
cc1100WriteReg(PATABLE, getPowerFromDipSW()); // Set PATABLE from dipSwitch
cc1100WriteCommand(SPWD); // cc1100 = SLEEP
}
/*================================================================================================
initIO
Description: Initialise I/O
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
void initIO (void)
{
// Init timer2
ASSR |= (1<<AS2); // Run Timer2 in asyncron mode
TIMSK2 |=(1<<OCIE2A); // Timer 2 Compare interrupt to OCR2A enable
// Init leds and switches
LED_DDR |=(1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5); // Output (direction for LED_DDR)
SW_PORT |=(1<<SW1)|(1<<SW2)|(1<<SW3)|(1<<SW4); // Pull-up on switch input
DDRB |= (1<<PB0); // Pin for measure battery level
// Init ADC
ADMUX |=(1<<ADLAR); // Left adjust the result to make an 8bit
ADMUX |=(1<<REFS0)|(1<<REFS1); // Internal 1,1V voltage ref for AD
// sample value.(instead of 10)
// Init external interrupt
EICRA |= (1<<ISC01); // Interrupt0 on falling edge
EIMSK |= (1<<INT0); // Enable external interrupt0
// Init Usart
usartSetBaud(12); // Set usartbaudrate. 12 = baudrate 9600
}
/*================================================================================================
readSensorValue
Description: Store an 8bit sample-value of temp and battery level.
Input: *storeSensor1 (addres to store sensorvalue 1 - Temperature sensor)
*storeSensor2 (addres to store sensorvalue 2 - Battery sensor)
*storeSensor3 (addres to store sensorvalue 3 - Aux sensor)
Output: -
------------------------------------------------------------------------------------------------*/
void readSensorValue(uint8_t *storeSensor1, uint8_t *storeSensor2, uint8_t *storeSensor3)
{
PRR &=~ (1<<PRADC); // Power reduction for ADC disabled
ADCSRA |= (1<<ADEN); // AD-converter enable
ADMUX &= ~((1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0));// ADC0 input pin.
PORTB |= (1<<PB0); // PB0=1 for batt. measurement;
ADCSRA |= (1<<ADSC); // Start conversion
while(ADCSRA&(1<<ADSC)); // ADSC will go low when conversion is done
PORTB &=~(1<<PB0); // PB0=0;
*storeSensor1 = ADCH;
ADMUX |= (1<<MUX0); // ADC1 input pin.
ADCSRA |= (1<<ADSC); // Start conversion
while(ADCSRA&(1<<ADSC)); // ADSC will go low when conversion is done
*storeSensor2 = ADCH;
ADCSRA &= ~(1<<ADEN); // AD-converter disable
PRR |= (1<<PRADC); // Power reduction for ADC enable
*storeSensor3 = 0x55;
}
/*================================================================================================
delay
Description: 500ms delay
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
void delay(void)
{
_delay_ms(200);
_delay_ms(200);
_delay_ms(100);
}
/*================================================================================================
showDeviceSettings
Description: Show device setting on indication leds.
Input: -
Output: -
------------------------------------------------------------------------------------------------*/
void showDeviceSettings(void)
{
LED_PORT &= ~((1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5));
delay();
// Show deviceAddr
uint8_t i;
for(i = 0; i < rfProtocolGetDeviceAddr(); i++)
{
flashLed(LED2);
delay();
}
if(programMode == SLAVE)
{
// Show masterAddr
for(i = 0; i < rfProtocolGetMasterAddr(); i++)
{
flashLed(LED3);
delay();
}
}
delay();
LED_PORT |= (1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5);
delay();
LED_PORT &= ~((1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5));
delay();
if(programMode == MASTER)
{
LED_PORT |= (1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5);
delay();
LED_PORT &= ~((1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5));
delay();
}
}
/*================================================================================================
Interrupt Service Routines
================================================================================================*/
/*================================================================================================
USART_RXC
Description: Interrup routine for USART receive
------------------------------------------------------------------------------------------------*/
ISR(USART_RX_vect)
{
if(pcProtocolAddByteToBuffer(usartReceive()))
{
flashLed(LED5);
uint8_t inputPCBuffer[9];
pcProtocolGetData(inputPCBuffer);
if(inputPCBuffer[1] == PC_COMMAND_GET_LOCAL_DATA)
{
// Measure temperature and battery level
readSensorValue(&outputBuffer[0], &outputBuffer[1], &outputBuffer[2]);
// Relay data to PC
pcProtocolSendData(PC_COMMAND_LOCAL_DATA, rfProtocolGetDeviceAddr(), outputBuffer[0],
outputBuffer[1], outputBuffer[2], 0x00);
}
else if(inputPCBuffer[1] == PC_COMMAND_REMOTE_UPDATE)
{
pcProtocolSendACK();
updateBuffer[inputPCBuffer[2]][0] = inputPCBuffer[3];
updateBuffer[inputPCBuffer[2]][1] = inputPCBuffer[4];
updateBuffer[inputPCBuffer[2]][2] = inputPCBuffer[5];
}
else if(inputPCBuffer[1] == PC_COMMAND_LOCAL_UPDATE)
{
pcProtocolSendACK();
if(inputPCBuffer[2])
{
rfProtocolSetMasterAddr(inputPCBuffer[2]);
}
if(inputPCBuffer[3])
{
rfProtocolSetDeviceAddr(inputPCBuffer[3]);
}
if(inputPCBuffer[4])
{
wakeUpValueSleep = inputPCBuffer[4];
}
if(inputPCBuffer[5])
{
remoteMode = inputPCBuffer[5]-1;
}
saveSettingsToEEPROM();
}
}
}
/*================================================================================================
INT0
Description: Interrup routine for external interrupt 0 (connected to USB active pin).
Disables usart when USB is disconnected.
------------------------------------------------------------------------------------------------*/
ISR(INT0_vect)
{
usartDisable();
LED_PORT &= ~(1<<LED5); // Turn off LED indicating USB present
main(); // Reset
}
/*================================================================================================
TIMER2_COMPA_vect
Description: Interrupt routine for Timer2. Used as wakeup timer.
------------------------------------------------------------------------------------------------*/
ISR(TIMER2_COMPA_vect)
{
}
/*================================================================================================
Main
================================================================================================*/
int main (void){
cli(); // Disable global interrupt
initIO();
spiInit();
resetCC1100();
loadSettingsFromEEPROM(); // Set device and master address from EEPROM
programMode = getProgramModeFromDipSW(); // Set programMode from DipSwitch
remoteMode = MASTER; // Default programMode when USB is active
programModeTemp = 0xFF; // Used for trigg change of programMode
activateRxMode();
sei();
while(1)
{
// Check USB connection
if(USB_ACTIVE_PIN &(1<<USB_ACTIVE))
{
usartEnable(); // Enable USART
LED_PORT |= (1<<LED5); // Turn on LED indicating USB present
programMode = remoteMode;
}
if(programMode == IDLE)
{
// Init IDLE
if(programModeTemp != programMode)
{
resetCC1100();
LED_PORT &= ~((1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5));
}
}
else if(programMode == MASTER)
{
// Init MASTER
if(programModeTemp != programMode)
{
resetCC1100();
showDeviceSettings();
}
// Check CRC_OK flag (packet received)
if(cc1100ReadStatusReg(PKTSTATUS) & (1<<7))
{
// Check if packet is a DATA-packet
if(rfProtocolReadPacket(inputBuffer) == RF_COMMAND_DATA)
{
_delay_ms(10);
flashLed(LED2); // Correct data received - GREEN flash (makes a 10ms delay)
// Send ACK and update data to slave
rfProtocolSendACKAndUpdate(inputBuffer[0], updateBuffer[inputBuffer[0]]);
if(cc1100ReadStatusReg(MARCSTATE) == 0x01)
{
flashLed(LED3); // ACK sent - YELLOW_2 flash (makes a 10ms delay)
if(USB_ACTIVE_PIN &(1<<USB_ACTIVE))
{
// Relay data to PC
pcProtocolSendData(PC_COMMAND_REMOTE_DATA, inputBuffer[0],
inputBuffer[2], inputBuffer[3], inputBuffer[4],
inputBuffer[5]);
flashLed(LED5); // Data sent to PC - YELLOW_1 flash (makes a 10ms delay)
}
}
else
{
cc1100WriteCommand(SFTX); // Flush cc1100 TXFIFO
flashLed(LED4); // Channel busy - RED flash (makes a 10ms delay)
}
}
timeOutCounter = 0;
activateRxMode();
}
// Restart RX mode if error packet is received (RX overflow)
else if((!(cc1100ReadStatusReg(MARCSTATE) == 0x0D)) && (++timeOutCounter > 100))
{
timeOutCounter = 0;
activateRxMode();
}
}
else if(programMode == SLAVE)
{
// Init SLAVE
if(programModeTemp != programMode)
{
resetCC1100();
showDeviceSettings();
}
activateRxMode();
// Measure temperature and battery level
readSensorValue(&outputBuffer[0], &outputBuffer[1], &outputBuffer[2]);
rfProtocolSendData(outputBuffer); // Send data to master
if(cc1100ReadStatusReg(MARCSTATE) == 0x01)
{
flashLed(LED3);
activateRxMode();
timeOutCounter = 0;
while((!(cc1100ReadStatusReg(MARCSTATE) == 0x01)) && (++timeOutCounter < 500));
// Check CRC_OK flag (packet received)
if(cc1100ReadStatusReg(PKTSTATUS) & (1<<7))
{
// Check if packet is a ACK-packet
if(rfProtocolReadPacket(inputBuffer) == RF_COMMAND_ACK_AND_UPDATE)
{
flashLed(LED2);
cc1100WriteCommand(SIDLE); // cc1100 = IDLE
cc1100WriteCommand(SPWD); // cc1100 = SLEEP
uint8_t storeNewSettings = 0;
if((inputBuffer[2]) && (inputBuffer[2] != rfProtocolGetMasterAddr()))
{
rfProtocolSetMasterAddr(inputBuffer[2]);
storeNewSettings = 1;
}
if((inputBuffer[3]) && (inputBuffer[3] != rfProtocolGetDeviceAddr()))
{
rfProtocolSetDeviceAddr(inputBuffer[3]);
storeNewSettings = 1;
}
if((inputBuffer[4]) && (inputBuffer[4] != wakeUpValueSleep))
{
wakeUpValueSleep = inputBuffer[4];
}
if(storeNewSettings)
{
saveSettingsToEEPROM();
}
errCounter = 0;
enterSleepMode(wakeUpValueSleep, 32); // Enter sleep mode for 8sec
}
else
{
flashLed(LED4);
cc1100WriteCommand(SIDLE); // cc1100 = IDLE
cc1100WriteCommand(SPWD); // cc1100 = SLEEP
if(++errCounter < ERR_COUNTER_THR)
{
enterSleepMode(1, ((rand() % 31)+1)); // Enter sleep mode (random 0-1sec)
}
else
{
errCounter = 0;
enterSleepMode(wakeUpValueSleep, 32); // Enter sleep mode for 8sec
}
}
}
else
{
//if(cc1100ReadStatusReg(MARCSTATE) == 0x01)
//{
// activateRxMode();
//}
flashLed(LED4);
cc1100WriteCommand(SIDLE); // cc1100 = IDLE
cc1100WriteCommand(SPWD); // cc1100 = SLEEP
if(++errCounter < ERR_COUNTER_THR)
{
enterSleepMode(1, ((rand() % 31)+1)); // Enter sleep mode (random 0-1sec)
}
else
{
errCounter = 0;
enterSleepMode(wakeUpValueSleep, 32); // Enter sleep mode for 8sec
}
}
}
else
{
cc1100WriteCommand(SFTX); // Flush cc1100 TXFIFO
flashLed(LED4);
cc1100WriteCommand(SIDLE); // cc1100 = IDLE
cc1100WriteCommand(SPWD); // cc1100 = SLEEP
if(++errCounter < ERR_COUNTER_THR)
{
enterSleepMode(1, ((rand() % 31)+1)); // Enter sleep mode (random 0-1sec)
}
else
{
errCounter = 0;
enterSleepMode(wakeUpValueSleep, 32); // Enter sleep mode for 8sec
}
}
}
else if(programMode == NOICEMAKER)
{
// Init NOICEMAKER
if(programModeTemp != programMode)
{
resetCC1100();
LED_PORT &= ~((1<<LED2)|(1<<LED3)|(1<<LED4)|(1<<LED5));
cc1100WriteReg(PKTCTRL0, 0x22);
cc1100WriteCommand(STX);
}
flashLed(LED2);
flashLed(LED3);
flashLed(LED5);
flashLed(LED4);
_delay_ms(200);
}
programModeTemp = programMode;
}
}
/*================================================================================================
End
================================================================================================*/