📄 rfmodem_cc1000.c
字号:
/****************************************************************************/
/* Application note AN015 */
/* Reference design : CC1000 RF Modem */
/* */
/* File: rfmodem_cc1000.c */
/* Revision: 1.0 */
/* */
/* Microcontroller: */
/* Microchip PIC16F876 */
/* */
/* Author: Karl H. Torvmark, Field Applications Engineer, Chipcon */
/* */
/* Contact: Chipcon AS +47 22 95 85 44 */
/* wireless@chipcon.com */
/****************************************************************************/
/****************************************************************************/
/* Description: */
/* */
/* This application note presents a software and hardware reference design */
/* for the CC1000. This software can serve as a starting point for */
/* developing custom software. A full protocol is implemented, with */
/* provisions for addressing, error checking etc. */
/* */
/* This software contains routines for detecting the preamble, searching */
/* for the start-of-frame (SOF) marker and doing byte synchronisation, */
/* and reading and handling header data such as the packet length. */
/* */
/* The software also contains all necessary routines to configure the */
/* CC1000, and demonstrates how to read and write data to the data */
/* interface. Configuration data is stored in non-volatile EEPROM memory, */
/* so that changes are preserved even when power is removed. Easy */
/* modification of parameters such as RF frequency and data rate is */
/* supported through a configuration menu. */
/* */
/* Please see the main text of application note AN015 for a more detailed */
/* description. */
/****************************************************************************/
/* *
* Revision history: *
* *
* $Log: rfmodem_cc1000.c,v $
* Revision 2.5 2003/05/07 15:02:31 tos
* Modified LOCK monitor in RX/TX setup: wait for LOCK before turning PA on.
*
* Revision 2.4 2003/04/25 13:30:06 tos
* Removed UART baudrate test (baudrate=2.4 kBaud).
*
* Revision 2.3 2003/04/25 13:12:47 tos
* Corrected inconsistent [UART interrupt request flag] monitor.
*
* Revision 2.2 2003/04/24 12:44:59 tos
* Corrected inconsistent monitoring of CC1000: [calibration complete] + [lock].
*
* Revision 2.1 2003/03/04 10:43:18 tos
* Corrected inconsistent AND instruction in CalibrateCC1000().
*
* *
****************************************************************************/
#include "io16f876.h"
#include "inpic.h"
#include "CC1000.h"
#include "modemhw.h"
#include "configure.h"
#include "simpleio.h"
#include <stdio.h>
#define INTERRUPT_VECTOR 0x04
#define HEADER_SIZE 4 // 4 bytes header
// The wrap-around functionality has been optimised by ANDing with a bit mask.
// Please note that if RX_BUFFER_SIZE is to be changed, the lines of code which
// do this must also be changed. They are located in the main loop of the program
// and in
#define TX_BUFFER_SIZE 64 // Size (in bytes) of transmit buffer
#define RX_BUFFER_SIZE 64 // Size (in bytes) of receive ring-buffer
#define PREAMBLE_LENGTH 4 // Number of bytes of preamble to send */
#define PREAMBLE_REQ 4 /* Number of bits required in addition to */
/* the initial 8 bits for the preamble to be */
/* accepted */
#define UI1 0x33 /* First byte of unique identifier */
#define UI2 0xCC /* Second byte of unique identifier */
#define FALSE 0
#define TRUE (!FALSE)
#define ON TRUE
#define OFF FALSE
// Macros for turning on and off the LEDs
#define SET_RXLED(STATE) \
if (STATE) \
TRISA&=~0x04; \
else \
TRISA|=0x04; \
#define SET_TXLED(STATE) \
if (STATE) \
TRISA&=~0x20; \
else \
TRISA|=0x20; \
// Variables
// Unit address is not used
extern __eeprom char UnitAddress;
// Union for shifting bits in or out of the CC1000
union {
char ShiftReg;
struct {
unsigned char ShiftRegLSB :1;
unsigned char :1;
unsigned char :1;
unsigned char :1;
unsigned char :1;
unsigned char :1;
unsigned char :1;
unsigned char ShiftRegMSB :1;
};
};
// Buffers for transmitted and received data
// These are put into different banks so that they can be as large as possible
// The TX buffer is filled up with data to be sent in the next data packet
// The RX buffer is a ring buffer in which received data is stored waiting to be
// sent to the UART
volatile __bank1 char TXBuffer[TX_BUFFER_SIZE];
volatile __bank2 char RXBuffer[RX_BUFFER_SIZE];
// Index pointers for use with buffers
volatile char TXBufferIndex;
char RXBufferReadIndex;
volatile char RXBufferWriteIndex;
// Counter variables
char PreambleCount;
char PreambleError;
char ByteCounter;
char BitCounter;
// Contains the total number of bytes to send in TX, including preamble and header
char BytesToSend;
// The number of bytes of data to receive in RX
char BytesToReceive;
// State variable stores the current state of the state machine
enum StateType {IDLE_STATE=0,RX_STATE=1,TX_STATE=2} State;
// This variable stores the state to switch to
// It is updated by the interrupt routine, while the main program
// does the actual state switch
volatile enum StateType NextState;
// This struct stores various flags in a space-efficient manner
volatile struct
{
unsigned char PreambleFound:1;
unsigned char UI1Found:1;
unsigned char LastPreambleBit:1;
unsigned char LockAverage:1;
unsigned char UnlockAverage:1;
};
// This routine initialises the TX buffer at startup
void InitializeTXBuffer(void)
{
char i;
// Put preamble into buffer
for (i=0;i<PREAMBLE_LENGTH;i++) {
TXBuffer[i]=0xAA;
}
TXBuffer[PREAMBLE_LENGTH]=UI1; // First byte of unique identifier
TXBuffer[PREAMBLE_LENGTH+1]=UI2; // Second byte of unique identifier
TXBuffer[PREAMBLE_LENGTH+2]=UnitAddress; // Unit address
TXBuffer[PREAMBLE_LENGTH+3]=0x00; // Default : no data
}
// This routine handles setup needed when changing states
void ChangeState(void)
{
switch (NextState) {
case RX_STATE:
if (State==TX_STATE) {
OPTION=0xC0; /* INT on rising edge */
TRISC|=0x02; /* Set DIO as input */
SetupCC1000RX(RXCurrent,RXPLL);
}
State=RX_STATE;
SET_RXLED(ON);
SET_TXLED(OFF);
READY=1; // HW Handshake : Not Ready
BitCounter=0;
ByteCounter=0;
break;
case TX_STATE:
if (State!=TX_STATE) {
OPTION=0x80; /* INT on falling edge */
TRISC&=~(0x02); /* Set DIO as output */
SetupCC1000TX(TXCurrent,TXPLL);
}
State=TX_STATE;
SET_RXLED(OFF);
SET_TXLED(ON);
READY=1; // HW Handshake : Not Ready
RCIE=0; // Disable UART Interrupts
BytesToSend=TXBufferIndex; // Number of bytes to send
TXBuffer[PREAMBLE_LENGTH+3]=BytesToSend-HEADER_SIZE-PREAMBLE_LENGTH;
TXBufferIndex=0;
BitCounter=0;
ShiftReg=TXBuffer[TXBufferIndex++];
break;
case IDLE_STATE:
if (State==TX_STATE) {
OPTION=0xC0; /* INT on rising edge */
TRISC|=0x02; /* Set DIO as input */
SetupCC1000RX(RXCurrent,RXPLL);
}
State=IDLE_STATE;
SET_RXLED(OFF);
SET_TXLED(OFF);
READY=0; // HW Handshake : Ready
RCIE=1; // Enable UART Interrupts
TXBufferIndex=HEADER_SIZE+PREAMBLE_LENGTH;
PreambleCount=0;
PreambleError=0;
PreambleFound=FALSE;
UI1Found=FALSE;
break;
}
}
// Main program
void main(void)
{
char dummy;
PORTC=0x00;
TRISC=0x92;
PORTB=0x00;
TRISB=0xFD;
PORTA=0x00;
TRISA=0x0B;
ADCON0=0x89;
ADCON1=0x84;
TXSTA=0x24;
RCSTA=0x90;
SPBRG=10; // 10.0000MHz, 57600baud
TMR1L=0x00;
TMR1H=0x00;
T1CON=0x31; /* Enable timer 1 */
#ifdef SPI
// Configure SPI
SSPSTAT=0x40;
SSPCON=0x30;
#endif
// PIE1=0x21; // UART and timer 1 enabled
PIE1=0x00;
/* Timer 2 time-out value */
/* Set to 10ms at 10MHz = 25000 instructions*/
T2CON=0x4B;
PR2=0xFF;
PALE=1;
RXBufferReadIndex=0;
RXBufferWriteIndex=0;
SetupCC1000PD();
ResetCC1000();
SetupCC1000All();
WakeUpCC1000ToTX(TXCurrent,TXPLL);
SetupCC1000TX(TXCurrent,TXPLL);
OPTION=0x80; // INT on falling edge
TRISC&=~(0x02); // Set DIO as output
if (!CalibrateCC1000())
writeln("TX Calibration failed");
// Calibration data is stored in the chip indexed by the selected frequency register
SetupCC1000RX(RXCurrent,RXPLL);
OPTION=0xC0; // INT on rising edge
TRISC|=0x02; // Set DIO as input
if (!CalibrateCC1000())
writeln("RX Calibration failed");
// Now the CC1000 is calibrated for both RX and TX, we do not need to recalibrate
// unless the frequency is changed, the temperature changes by 40 degrees C
// or if the supply voltage changes by more than 0.5V
// Force update
State=TX_STATE;
NextState=IDLE_STATE;
InitializeTXBuffer();
// Set all buttons to light, if we want to turn them off, we set them as inputs to avoid
// the output being shorted to VDD if a button is pressed
CARRIER_LED=0;
RD_LED=0;
TD_LED=0;
dummy=TRISA;
TRISA|=0x24;
if (BUTTON1==0) {
}
if (BUTTON2==0) {
ConfigurationMode();
}
TRISA=dummy;
AWAKE=0;
SYNC=0;
LockAverage=0;
UnlockAverage=0;
/* Startup message */
writeln("RF Modem ready");
writestr("Compiled ");
writestr(__DATE__);
writestr(" ");
writestr(__TIME__);
writeln("");
dummy=ReadFromCC1000Register(CC1000_MODEM0);
switch(dummy) {
case 0x07:
writestr("0.3kbit/s Manchester");
break;
case 0x03:
writestr("0.6kbit/s NRZ");
break;
case 0x17:
writestr("0.6kbit/s Manchester");
break;
case 0x13:
writestr("1.2kbit/s NRZ");
break;
case 0x27:
writestr("1.2kbit/s Manchester");
break;
case 0x23:
writestr("2.4kbit/s NRZ");
break;
case 0x37:
writestr("2.4kbit/s Manchester");
break;
case 0x33:
writestr("4.8kbit/s NRZ");
break;
case 0x47:
writestr("4.8kbit/s Manchester");
break;
case 0x43:
writestr("9.6kbit/s NRZ");
break;
case 0x57:
writestr("9.6kbit/s Manchester");
break;
case 0x53:
writestr("19.2kbit/s NRZ");
break;
case 0x55:
writestr("19.2kbit/s Manchester");
break;
case 0x51:
writestr("38.4kbit/s NRZ");
break;
case 0x54:
writestr("38.4kbit/s Manchester");
break;
case 0x50:
writestr("76.8kbit/s NRZ");
break;
default:
writestr("Invalid data rate");
break;
}
writeln("");
AverageFreeRunCC1000();
INTCON=0x90;
while(1) {
/* If state change, handle it */
if (State!=NextState)
ChangeState();
if (UnlockAverage) {
UnlockAverage=0;
AverageFreeRunCC1000();
}
if (LockAverage) {
LockAverage=0;
AverageManualLockCC1000();
}
/* If data in receive buffer, write it to the serial port */
if (RXBufferReadIndex!=RXBufferWriteIndex) {
putchar(RXBuffer[RXBufferReadIndex]);
// Increase read index, wrap around when reached end of buffer
//RXBufferReadIndex=(RXBufferReadIndex+1)%RX_BUFFER_SIZE;
RXBufferReadIndex++;
RXBufferReadIndex&=0x3F;
}
if (RCIF==1) {
// UART receive interrupt. PC has sent data to us.
if (State==IDLE_STATE) { // Only handle data if idle
TMR2=0; // Reset time-out timer
TMR2ON=1; // Start time-out timer
// TODO : Handle framing and overflow errors
if (OERR) {
TXEN=0;
TXEN=1;
CREN=0;
CREN=1;
}
if (FERR) {
dummy=RCREG;
TXEN=0;
TXEN=1;
}
TXBuffer[TXBufferIndex++]=RCREG;
// IMPORTANT : We may have another interrupt or two occur before we can change mode
// Therefore, leave safety margin!
if (TXBufferIndex>=(TX_BUFFER_SIZE-3)) { // Change mode early to have safety margin
NextState=TX_STATE;
}
RCIF=0;
}
}
if (TMR2IF==1) {
// Timer 2 interrupt. TX timer has timed out.
TMR2ON=0; // Turn off timer 2
TMR2IE=0; // Turn off timer 2 interrupt
NextState=TX_STATE;
TMR2IF=0;
}
if (TMR1IF==1) {
// Timer 1 interrupt. Check button status
dummy=TRISA;
TRISA|=0x24; // Set port to read button status
if (BUTTON1==0) {
TXBuffer[TXBufferIndex++]=Button1Char;
NextState=TX_STATE;
}
if (BUTTON2==0) {
TXBuffer[TXBufferIndex++]=Button2Char;
NextState=TX_STATE;
}
TRISA=dummy; // Set port back to LED operation
TMR1IF=0;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -