flipperzero-firmware/applications/cc1101-workaround/cc1101.cpp
あく 584c0962d8
[FL-781] FURI, CLI, stdlib: stdout hooks, integration between subsystems, uniform printf usage (#311)
* FURI stdglue: stdout hooks, local and global, ISR safe printf. Uniform newlines for terminal/debug output. Power: prevent sleep while core 2 has not started.
* Furi record, stdglue: check mutex allocation
* remove unused test
* Furi stdglue: buferized output, dynamically allocated state. Furi record: dynamically allocated state. Input dump: proper line ending. Hal VCP: dynamically allocated state.
* Interrupt manager: explicitly init list.
* Makefile: cleanup rules, fix broken dfu upload. F4: add compiler stack protection options.
* BLE: call debug uart callback on transmission complete
* FreeRTOS: add configUSE_NEWLIB_REENTRANT
* API HAL Timebase: fix issue with idle thread stack corruption caused by systick interrupt. BT: cleanup debug info output. FreeRTOS: disable reentry for newlib.
* F4: update stack protection CFLAGS to match used compiller
* F4: disable compiller stack protection because of incompatibility with current compiller
* Makefile: return openocd logs to gdb
* BLE: fixed pin, moar power, ble trace info.
* Prevent sleep when connection is active
* Makefile: return serial port to upload rule, add workaround for mac os
* Furi: prevent usage of stack for cmsis functions.
* F4: add missing includes, add debugger breakpoints
* Applications: per app stack size.
* Furi: honor kernel state in stdglue
* FreeRTOS: remove unused hooks
* Cleanup and format sources

Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
2021-01-29 03:09:33 +03:00

484 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <furi.h>
#include "cc1101-workaround/cc1101.h"
#include "spi.h"
#include <math.h>
// ******************************************************************************
#define WRITE_BURST 0x40
#define READ_SINGLE 0x80
#define READ_BURST 0xC0
#define BYTES_IN_FIFO 0x7F //used to detect FIFO underflow or overflow
/*********************ss_pin as global variable****************************** */
/* cc1101 */
/******************************************************************************/
GpioPin ss_pin;
CC1101::CC1101(GpioPin* ss_pin) {
/*
pinMode(gdo0_pin, OUTPUT); //GDO0 as asynchronous serial mode input
pinMode(gdo2_pin, INPUT); //GDO2 as asynchronous serial mode output
*/
gpio_init(ss_pin, GpioModeOutputPushPull);
this->ss_pin = ss_pin;
// TODO open record
this->miso_pin = MISO_PIN;
this->miso_pin_record = &this->miso_pin;
}
//******************************************************************************
//SpiInit
/******************************************************************************/
extern SPI_HandleTypeDef SPI_R;
void CC1101::SpiInit(void) {
//initialize spi pins
//Enable spi master, MSB, SPI mode 0, FOSC/4
SpiMode(0);
CC1101_SPI_Reconfigure();
}
void CC1101::SpiEnd(void) {
/*
SPCR = ((0<<SPE) | // SPI Enable
(0<<SPIE)| // SPI Interupt Enable
(0<<DORD)| // Data Order (0:MSB first / 1:LSB first)
(1<<MSTR)| // Master/Slave select
(0<<SPR1)|(0<<SPR0)| // SPI Clock Rate ( 0 0 = osc/4; 0 1 = osc/16; 1 0 = osc/64; 1 1= 0sc/128)
(0<<CPOL)| // Clock Polarity (0:SCK low / 1:SCK hi when idle)
(0<<CPHA)); // Clock Phase (0:leading / 1:trailing edge sampling)
//SPSR = (0<<SPI2X); // Double Clock Rate
*/
}
/******************************************************************************
Function: SpiMode
*INPUT : config mode
(0<<CPOL) | (0 << CPHA) 0
(0<<CPOL) | (1 << CPHA) 1
(1<<CPOL) | (0 << CPHA) 2
(1<<CPOL) | (1 << CPHA) 3
*OUTPUT :none
******************************************************************************/
void CC1101::SpiMode(uint8_t config) {
/*
uint8_t tmp;
// enable SPI master with configuration byte specified
SPCR = 0;
SPCR = (config & 0x7F) | (1<<SPE) | (1<<MSTR);
tmp = SPSR;
tmp = SPDR;
*/
}
/****************************************************************
*FUNCTION NAME:SpiTransfer
*FUNCTION :spi transfer
*INPUT :value: data to send
*OUTPUT :data to receive
****************************************************************/
uint8_t CC1101::SpiTransfer(uint8_t value) {
uint8_t buf[1] = {value};
uint8_t rxbuf[1] = {0};
HAL_SPI_TransmitReceive(&SPI_R, buf, rxbuf, 1, HAL_MAX_DELAY);
return rxbuf[0];
}
uint8_t last_status;
/****************************************************************
*FUNCTION NAME:SpiWriteReg
*FUNCTION :CC1101 write data to register
*INPUT :addr: register address; value: register value
*OUTPUT :none
****************************************************************/
void CC1101::SpiWriteReg(uint8_t addr, uint8_t value) {
gpio_write(ss_pin, false);
while(gpio_read(this->miso_pin_record))
;
last_status = SpiTransfer(addr);
last_status = SpiTransfer(value);
gpio_write(ss_pin, true);
}
/****************************************************************
*FUNCTION NAME:SpiWriteBurstReg
*FUNCTION :CC1101 write burst data to register
*INPUT :addr: register address; buffer:register value array; num:number to write
*OUTPUT :none
****************************************************************/
void CC1101::SpiWriteBurstReg(uint8_t addr, uint8_t* buffer, uint8_t num) {
gpio_write(ss_pin, false);
while(gpio_read(this->miso_pin_record))
;
last_status = SpiTransfer(addr | WRITE_BURST);
for(uint8_t i = 0; i < num; i++) {
last_status = SpiTransfer(buffer[i]);
}
gpio_write(ss_pin, true);
}
/****************************************************************
*FUNCTION NAME:SpiStrobe
*FUNCTION :CC1101 Strobe
*INPUT :strobe: command; //refer define in CC1101.h//
*OUTPUT :none
****************************************************************/
void CC1101::SpiStrobe(uint8_t strobe) {
gpio_write(ss_pin, false);
while(gpio_read(this->miso_pin_record))
;
last_status = SpiTransfer(strobe);
gpio_write(ss_pin, true);
}
/****************************************************************
*FUNCTION NAME:SpiReadReg
*FUNCTION :CC1101 read data from register
*INPUT :addr: register address
*OUTPUT :register value
****************************************************************/
uint8_t CC1101::SpiReadReg(uint8_t addr) {
gpio_write(ss_pin, false);
while(gpio_read(this->miso_pin_record))
;
last_status = SpiTransfer(addr | READ_SINGLE);
uint8_t value = SpiTransfer(0);
gpio_write(ss_pin, true);
return value;
}
/****************************************************************
*FUNCTION NAME:SpiReadBurstReg
*FUNCTION :CC1101 read burst data from register
*INPUT :addr: register address; buffer:array to store register value; num: number to read
*OUTPUT :none
****************************************************************/
void CC1101::SpiReadBurstReg(uint8_t addr, uint8_t* buffer, uint8_t num) {
gpio_write(ss_pin, false);
while(gpio_read(this->miso_pin_record))
;
last_status = SpiTransfer(addr | READ_BURST);
for(uint8_t i = 0; i < num; i++) {
buffer[i] = SpiTransfer(0);
}
gpio_write(ss_pin, true);
}
/****************************************************************
*FUNCTION NAME:SpiReadStatus
*FUNCTION :CC1101 read status register
*INPUT :addr: register address
*OUTPUT :status value
****************************************************************/
uint8_t CC1101::SpiReadStatus(uint8_t addr) {
gpio_write(ss_pin, false);
while(gpio_read(this->miso_pin_record))
;
last_status = SpiTransfer(addr | READ_BURST);
uint8_t value = SpiTransfer(0);
gpio_write(ss_pin, true);
return value;
}
/****************************************************************
*FUNCTION NAME:Reset
*FUNCTION :CC1101 reset //details refer datasheet of CC1101/CC1100//
*INPUT :none
*OUTPUT :none
****************************************************************/
void CC1101::Reset(void) {
gpio_write(ss_pin, false);
delay(1);
gpio_write(ss_pin, true);
delay(1);
gpio_write(ss_pin, false);
while(gpio_read(this->miso_pin_record))
;
last_status = SpiTransfer(CC1101_SRES);
while(gpio_read(this->miso_pin_record))
;
gpio_write(ss_pin, true);
}
bool CC1101::SpiSetRegValue(uint8_t reg, uint8_t value, uint8_t msb, uint8_t lsb) {
if((msb > 7) || (lsb > 7) || (lsb > msb)) {
return false;
}
uint8_t current_value = SpiReadReg(reg);
uint8_t mask = ~((0b11111111 << (msb + 1)) | (0b11111111 >> (8 - lsb)));
uint8_t new_value = (current_value & ~mask) | (value & mask);
SpiWriteReg(reg, new_value);
return true;
}
/****************************************************************
*FUNCTION NAME:Init
*FUNCTION :CC1101 initialization
*INPUT :none
*OUTPUT :none
****************************************************************/
uint8_t CC1101::Init(void) {
#ifdef CC1101_DEBUG
printf("Init SPI...\r\n");
#endif
SpiInit(); //spi initialization
gpio_write(ss_pin, true);
// gpio_write(SCK_PIN, true);
// gpio_write(MOSI_PIN, false);
#ifdef CC1101_DEBUG
printf("Reset CC1101...\r\n");
#endif
Reset(); // CC1101 reset
osDelay(150);
uint8_t partnum __attribute__((unused));
uint8_t version;
partnum = SpiReadStatus(CC1101_PARTNUM);
version = SpiReadStatus(CC1101_VERSION);
#ifdef CC1101_DEBUG
printf("Partnum:0x%02X, Version:0x%02X\n", partnum, version);
#endif
#ifdef CC1101_DEBUG
printf("Init CC1101...");
#endif
// RegConfigSettings(); //CC1101 register config
#ifdef CC1101_DEBUG
printf("Done!\r\n");
#endif
return version;
}
/****************************************************************
*FUNCTION NAME:SetMod
*FUNCTION :CC1101 modulation type
*INPUT :byte mode
*OUTPUT :none
****************************************************************/
void CC1101::SetMod(uint8_t mode) {
SpiWriteReg(CC1101_MDMCFG2, mode); //no sync/preamble; ASK/OOK only support up to -1dbm
if((mode | 0x30) == ASK) {
SpiWriteReg(CC1101_FREND0, 0x11); //use first up to PATABLE(0)
uint8_t PaTabel[8] = {0x00, POWER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
SpiWriteBurstReg(CC1101_PATABLE, PaTabel, 8); //CC1101 PATABLE config
} else {
SpiWriteReg(CC1101_FREND0, 0x10); //use first up to PATABLE(0)
uint8_t PaTabel[8] = {POWER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
SpiWriteBurstReg(CC1101_PATABLE, PaTabel, 8); //CC1101 PATABLE config
}
#ifdef CC1101_DEBUG
switch(mode | 0x30) {
case GFSK: {
printf("CC1101 Modulation: GFSK");
break;
}
case MSK: {
printf("CC1101 Modulation: MSK");
break;
}
case ASK: {
printf("CC1101 Modulation: ASK/OOK");
break;
}
case FSK2: {
printf("CC1101 Modulation: 2-FSK");
break;
}
case FSK4: {
printf("CC1101 Modulation: 4-FSK");
break;
}
default: //default to GFSK
{
printf("Modulation mode not supported");
break;
}
}
printf("\r\n");
#endif
}
/****************************************************************
*FUNCTION NAME:RegConfigSettings
*FUNCTION :CC1101 register config //details refer datasheet of CC1101/CC1100//
*INPUT :none
*OUTPUT :none
****************************************************************/
void CC1101::RegConfigSettings(void) {
SpiWriteReg(CC1101_FSCTRL1, 0x06); //IF frequency
SpiWriteReg(CC1101_FSCTRL0, 0x00); //frequency offset before synthesizer
SpiWriteReg(CC1101_MDMCFG4, 0xCC); // RX filter bandwidth 100k(0xcc)
SpiWriteReg(
CC1101_MDMCFG3, 0x43); //datarate config 512kBaud for the purpose of fast rssi measurement
SpiWriteReg(CC1101_MDMCFG1, 0x21); //FEC preamble etc. last 2 bits for channel spacing
SpiWriteReg(CC1101_MDMCFG0, 0xF8); //100khz channel spacing
//CC1101_CHANNR moved to SetChannel func
//SpiWriteReg(CC1101_DEVIATN, 0x47);
SpiWriteReg(
CC1101_MCSM0, 0x18); // calibrate when going from IDLE to RX or TX ; 149 - 155 μs timeout
SpiWriteReg(CC1101_FOCCFG, 0x16); //frequency compensation
//SpiWriteReg(CC1101_BSCFG, 0x1C); //bit synchronization config
SpiWriteReg(CC1101_AGCCTRL2, 0x43);
SpiWriteReg(CC1101_AGCCTRL1, 0x49);
SpiWriteReg(CC1101_AGCCTRL0, 0x91);
//freq synthesizer calibration
SpiWriteReg(CC1101_FSCAL3, 0xEA);
SpiWriteReg(CC1101_FSCAL2, 0x2A);
SpiWriteReg(CC1101_FSCAL1, 0x00);
SpiWriteReg(CC1101_FSCAL0, 0x1F);
SpiWriteReg(CC1101_TEST2, 0x81);
SpiWriteReg(CC1101_TEST1, 0x35);
SpiWriteReg(CC1101_TEST0, 0x0B); //should be 0x0B for lower than 430.6MHz and 0x09 for higher
//SpiWriteReg(CC1101_FREND1, 0x56);
//SpiWriteReg(CC1101_IOCFG2, 0x0B); //serial clock.synchronous to the data in synchronous serial mode
//SpiWriteReg(CC1101_IOCFG0, 0x06); //asserts when sync word has been sent/received, and de-asserts at the end of the packet
SpiWriteReg(CC1101_IOCFG2, 0x0D); //data output pin for asynchronous mode
SpiWriteReg(
CC1101_IOCFG0,
0x2E); //High impedance (3-state), GDO0 configed as data input for asynchronous mode
//SpiWriteReg(CC1101_PKTCTRL0, 0x05); //whitening off;CRC Enablevariable length packets, packet length configured by the first byte after sync word
SpiWriteReg(
CC1101_PKTCTRL0, 0x33); //whitening off; asynchronous serial mode; CRC diablereserved
//SpiWriteReg(CC1101_PKTLEN, 0x3D); //61 bytes max length
SpiWriteReg(
CC1101_FIFOTHR,
0x47); //Adc_retention enabled for RX filter bandwidth less than 325KHz; defalut fifo threthold.
}
/****************************************************************
*FUNCTION NAME:SetFreq
*FUNCTION :SetFreq
*INPUT :Freq2, Freq1, Freq0
*OUTPUT :none
****************************************************************/
void CC1101::SetFreq(uint8_t freq2, uint8_t freq1, uint8_t freq0) {
SpiWriteReg(CC1101_FREQ2, freq2);
SpiWriteReg(CC1101_FREQ1, freq1);
SpiWriteReg(CC1101_FREQ0, freq0);
}
/****************************************************************
*FUNCTION NAME:SetChannel
*FUNCTION :SetChannel
*INPUT :int channel
*OUTPUT :none
****************************************************************/
void CC1101::SetChannel(int channel) {
#ifdef CC1101_DEBUG
printf("Set CC1101 channel to: %d \n", channel);
#endif
SpiWriteReg(CC1101_CHANNR, (uint8_t)channel); //related to channel numbers
}
/****************************************************************
*FUNCTION NAME:SetReceive
*FUNCTION :SetReceive
*INPUT :none
*OUTPUT :none
****************************************************************/
void CC1101::SetReceive(void) {
SpiStrobe(CC1101_SRX);
while(SpiReadStatus(CC1101_MARCSTATE) ^ CC1101_STATUS_RX) {
// delay(1);
// printf("wait status\r\n");
}
}
/****************************************************************
*FUNCTION NAME:SetTransmit
*FUNCTION :
*INPUT :none
*OUTPUT :none
****************************************************************/
void CC1101::SetTransmit(void) {
SpiStrobe(CC1101_STX);
while(SpiReadStatus(CC1101_MARCSTATE) ^ CC1101_STATUS_TX)
;
}
//cc1101 cc1101;
bool CC1101::setRxBandwidth(float bandwidth) {
if(bandwidth < 58.0 || bandwidth > 821.0) return false;
// set mode to standby
SpiStrobe(CC1101_SIDLE);
// calculate exponent and mantissa values
for(int8_t e = 3; e >= 0; e--) {
for(int8_t m = 3; m >= 0; m--) {
float point = (F_OSC) / (8 * (m + 4) * ((uint32_t)1 << e));
if(fabs((bandwidth * 1000.0) - point) <= 1000) {
// set Rx channel filter bandwidth
SpiSetRegValue(CC1101_MDMCFG4, (e << 6) | (m << 4), 7, 4);
return true;
}
}
}
return false;
}
static void getExpMant(
float target,
uint16_t mantOffset,
uint8_t divExp,
uint8_t expMax,
uint8_t& exp,
uint8_t& mant) {
// get table origin point (exp = 0, mant = 0)
float origin = (mantOffset * F_OSC) / ((uint32_t)1 << divExp);
// iterate over possible exponent values
for(int8_t e = expMax; e >= 0; e--) {
// get table column start value (exp = e, mant = 0);
float intervalStart = ((uint32_t)1 << e) * origin;
// check if target value is in this column
if(target >= intervalStart) {
// save exponent value
exp = e;
// calculate size of step between table rows
float stepSize = intervalStart / (float)mantOffset;
// get target point position (exp = e, mant = m)
mant = ((target - intervalStart) / stepSize);
// we only need the first match, terminate
return;
}
}
}
bool CC1101::setBitRate(float bitrate) {
if(bitrate < 0.6 || bitrate > 500.0) return false;
// set mode to standby
SpiStrobe(CC1101_SIDLE);
// calculate exponent and mantissa values
uint8_t e = 0;
uint8_t m = 0;
getExpMant(bitrate * 1000.0, 256, 28, 14, e, m);
// set bit rate value
SpiSetRegValue(CC1101_MDMCFG4, e, 3, 0);
SpiSetRegValue(CC1101_MDMCFG3, m, 7, 0);
return true;
}