Changeset - 851511077b87
[Not reviewed]
default
0 4 0
Ethan Zonca (ethanzonca) - 9 years ago 2017-01-03 15:13:32
e@ethanzonca.com
Fix issue where miso was never initted
4 files changed with 7 insertions and 10 deletions:
0 comments (0 inline, 0 general)
Libraries/Si446x/si446x.c
Show inline comments
 
//
 
// Si446x: Initializes and configures a Si446x transceiver over SPI
 
//
 
 
#include "stm32f0xx_hal.h"
 
 
#include "si446x.h"
 
#include "config.h"
 
#include "error.h"
 
#include "system/gpio.h"
 
#include "system/sysclk.h"
 
 
 
// Private variables
 
static SPI_HandleTypeDef hspi1;
 
static uint8_t si446x_cw_status = 0;
 
 
 
// Private function prototypes
 
static void __init_spi1(void);
 
 
 
// Initialize Si446x in 2FSK transmit mode
 
void si446x_init(void)
 
{
 
	// init spi port
 
	__init_spi1();
 
 
	GPIO_InitTypeDef GPIO_InitStruct;
 
 
	// GPIO: TCXO control
 
	GPIO_InitStruct.Pin = SI446x_TCXO_EN_PIN;
 
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 
	GPIO_InitStruct.Pull = GPIO_NOPULL;
 
	HAL_GPIO_Init(SI446x_TCXO_EN_PORT, &GPIO_InitStruct);
 
	HAL_GPIO_WritePin(SI446x_TCXO_EN_PORT, SI446x_TCXO_EN_PIN, 1);
 
 
	// GPIO: VHF radio shutdown control
 
	GPIO_InitStruct.Pin = SI446x_SHUTDOWN_PIN;
 
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 
	GPIO_InitStruct.Pull = GPIO_NOPULL;
 
	HAL_GPIO_Init(SI446x_SHUTDOWN_PORT, &GPIO_InitStruct);
 
 
	// Perform PoR (takes 20ms) and turn device on
 
	si446x_reset();
 
 
    // Divide SI446x_VCXO_FREQ into its bytes; MSB first
 
    uint16_t x3 = SI446x_VCXO_FREQ / 0x1000000;
 
    uint16_t x2 = (SI446x_VCXO_FREQ - x3 * 0x1000000) / 0x10000;
 
    uint16_t x1 = (SI446x_VCXO_FREQ - x3 * 0x1000000 - x2 * 0x10000) / 0x100;
 
    uint16_t x0 = (SI446x_VCXO_FREQ - x3 * 0x1000000 - x2 * 0x10000 - x1 * 0x100);
 
 
    // Power up radio module                          boot  xtal  _XO_frequency_
 
 
    //TCXO
 
    const char init_command[] = {SI446x_CMD_POWER_UP, 0x01, 0x01, x3, x2, x1, x0};
 
    si446x_sendcmd(7, init_command, SI446x_CHECK_ACK);
 
 
    HAL_Delay(10);
 
 
	// Change to SPI Ready state EMZ added for re-init, might help on startup
 
	uint8_t change_state_commanda[] = {SI446x_CMD_CHANGE_STATE, 0x02}; //  Change to spi ready
 
    si446x_sendcmd(2, change_state_commanda, SI446x_CHECK_ACK);
 
    HAL_Delay(10);
 
 
    // Radio ready: clear all pending interrupts and get the interrupt status back
 
	uint8_t get_int_status_command[] = {SI446x_CMD_GET_INT_STATUS, 0x00, 0x00, 0x00};
 
    si446x_sendcmd(4, get_int_status_command, SI446x_CHECK_ACK);
 
 
    HAL_Delay(10);
 
 
    // GPIO config: Set all GPIOs LOW; Link NIRQ to CTS; Link SDO to MISO; Max drive strength
 
	uint8_t gpio_pin_cfg_command[] = {
 
    		SI446x_CMD_GPIO_PIN_CFG, // Command
 
			SI446x_GPIO_LOW,      // GPIO0 - Power amp control DAC AD5611 Sync Pin
 
			SI446x_GPIO_INPUT,    // GPIO1 - Input for modulation
 
			SI446x_GPIO_LOW,      // GPIO2 - Blue LED
 
			SI446x_GPIO_NOCHANGE, // GPIO3 - Unused
 
			SI446x_GPIO_NOCHANGE, // NIRQ
 
			SI446x_GPIO_NOCHANGE, // 0x11, // SDO
 
			SI446x_GPIO_NOCHANGE, // Gencfg
 
	};
 
    si446x_sendcmd(8, gpio_pin_cfg_command, SI446x_CHECK_ACK);
 
 
    HAL_Delay(10);
 
 
//	uint8_t tune_xo_cmd[] = {
 
//    		SI446x_CMD_SET_PROPERTY,
 
//			SI446x_XO_TUNE_REGISTER_GROUP,
 
//			0x1, // num data
 
//			SI446x_XO_TUNE_REGISTER_PROP,
 
//			SI446x_CRYSTAL_LOAD_TUNING,
 
//    };
 
//    si446x_sendcmd(5, tune_xo_cmd, SI446x_CHECK_ACK);
 
 
    HAL_Delay(10);
 
 
    // Tune to frequency specified
 
	si446x_setchannel(TUNE_FREQUENCY);
 
 
	HAL_Delay(10);
 
 
    // Set to 2FSK mode
 
    uint8_t modemconfig = SI446x_MOD_TYPE_2FSK | SI446x_MOD_TYPE_SOURCE_DIRECTMODE | SI446x_MOD_TYPE_DIRECT_ASYNCH | SI446x_MOD_TYPE_DIRECT_SOURCE_GPIO1; //SI446x_MOD_TYPE_SOURCE_PACKETHANDLER
 
    uint8_t set_modem_mod_type_command[] = {
 
    		SI446x_CMD_SET_PROPERTY,
 
    		SI446x_MOD_TYPE_REGISTER_GROUP,
 
			0x01, // num data
 
			SI446x_MOD_TYPE_REGISTER_PROP,
 
			modemconfig
 
    };
 
    si446x_sendcmd(5, set_modem_mod_type_command, SI446x_CHECK_ACK);
 
 
    HAL_Delay(10);
 
 
	// Set Si446x initial output power, input to power amp (0-0x7F, 0mW - 40mw?)
 
	uint8_t basepower = 0x02;
 
	// FIXME: basepower should be 0x10 for underperforming units and 0x04 for normal units
 
    uint8_t set_power_level_command[] = {SI446x_CMD_SET_PROPERTY, 0x22, 0x01, 0x01, basepower};
 
	si446x_sendcmd(5, set_power_level_command, SI446x_CHECK_ACK);
 
 
 
	HAL_Delay(10);
 
 
    // Set air data rate
 
    si446x_setdatarate();
 
    HAL_Delay(10);
 
 
	// Tune TX
 
	uint8_t change_state_command[] = {SI446x_CMD_CHANGE_STATE, 0x05}; //  Change to TX tune state
 
    si446x_sendcmd(2, change_state_command, SI446x_CHECK_ACK);
 
    HAL_Delay(10);
 
 
	si446x_cw_status = 0;
 
}
 
 
 
// Perform power-on-reset of Si446x. Takes 20ms.
 
void si446x_reset(void)
 
{
 
	si446x_shutdown();
 
	HAL_Delay(10);
 
	si446x_wakeup();
 
	HAL_Delay(10);
 
}
 
 
 
// Set GPIO pin state on Si446x
 
void si446x_gpio(uint8_t gpio, uint8_t state, uint8_t doack)
 
{
 
	// GPIO invalid
 
	if(gpio > 7)
 
		return;
 
 
	// Default to not changing any GPIO
 
	uint8_t gpio_pin_cfg_command[] = {
 
			SI446x_CMD_GPIO_PIN_CFG, // Command
 
			SI446x_GPIO_NOCHANGE, // GPIO0 - Power amp control DAC AD5611 Sync Pin
 
			SI446x_GPIO_NOCHANGE, // GPIO1 - Input for modulation
 
			SI446x_GPIO_NOCHANGE, // GPIO2 - Blue
 
			SI446x_GPIO_NOCHANGE, // GPIO3 - Unused
 
			SI446x_GPIO_NOCHANGE, // NIRQ
 
			SI446x_GPIO_NOCHANGE, // 0x11, // SDO
 
			SI446x_GPIO_NOCHANGE, // Gencfg
 
	};
 
 
	// Set requested GPIO to requested state
 
	gpio_pin_cfg_command[gpio+1] = state;
 
 
	si446x_sendcmd(8, gpio_pin_cfg_command, doack);
 
}
 
 
 
// Set over-air data rate
 
void si446x_setdatarate(void)
 
{
 
    // Set data rate (unsure if this actually affects direct modulation)
 
                         //        set prop   group     numprops  startprop   data
 
	uint8_t set_data_rate_command[] = {SI446x_CMD_SET_PROPERTY,     0x20,     0x03,     0x03,       0x0F, 0x42, 0x40};
 
    si446x_sendcmd(7, set_data_rate_command, SI446x_CHECK_ACK);
 
}
 
 
 
// Block write data to the Si446x SPI interface, up to 128 byte length
 
void si446x_senddata(uint8_t* data, uint8_t len)
 
{
 
	uint8_t dummy[128];
 
	SI446x_SELECT;
 
	HAL_SPI_TransmitReceive(&hspi1, data, dummy, len, SI446x_TIMEOUT);
 
	SI446x_DESELECT;
 
}
 
 
 
// Delay approximately 20us
 
static void delay_cycles(void)
 
{
 
	uint32_t delay_cycles = 180;
 
	while(delay_cycles>0)
 
	{
 
		asm("NOP");
 
		delay_cycles--;
 
	}
 
}
 
 
 
// Send a command to the radio (Blocking)
 
// Avoid calling this during code runtime as it will block for a significant period of time due to delays
 
void si446x_sendcmd(uint8_t tx_len, uint8_t* data, uint8_t doack)
 
{
 
    SI446x_SELECT;
 
 
    delay_cycles();
 
 
    uint8_t dummyrx[25];
 
    if(tx_len >=25)
 
    {
 
    	SI446x_DESELECT;
 
    	return;
 
    }
 
 
    // using transmit receive to transmit data because it actually blocks until the data is sent
 
    // an additional byte is added on to the transmission so we can receive the CTS byte
 
 
    HAL_StatusTypeDef res = HAL_SPI_TransmitReceive(&hspi1, data, dummyrx, tx_len+1, SI446x_TIMEOUT);
 
    volatile HAL_StatusTypeDef res = HAL_SPI_TransmitReceive(&hspi1, data, dummyrx, tx_len+1, SI446x_TIMEOUT);
 
 
    if(res != HAL_OK)
 
    {
 
    	SI446x_DESELECT;
 
    	return;
 
    }
 
 
    SI446x_DESELECT;
 
 
    // If checking for the ACK, perform a SPI read and see if the command was acknowledged
 
    if(doack)
 
    {
 
		delay_cycles();
 
 
		SI446x_SELECT;
 
 
		int reply = 0x00;
 
		uint8_t tx_requestack[2];
 
		tx_requestack[0] = SI446x_CMD_READ_CMD_BUFF;
 
		tx_requestack[1] = 0x00;
 
 
		uint16_t attempts = 0;
 
		volatile uint16_t attempts = 0;
 
 
		// Keep trying receive until it returns 0xFF (successful ACK)
 
		while (reply != 0xFF)
 
		{
 
			// Attempt to receive two bytes from the Si446x which should be an ACK
 
			uint8_t tmprx[2] = {0,0};
 
			res = HAL_SPI_TransmitReceive(&hspi1, tx_requestack, tmprx, 2, SI446x_TIMEOUT);
 
			if(res != HAL_OK)
 
			{
 
				//error_assert_silent(ERR_VHF_SPIBUSY);
 
		    	break; // Break out, deinit, and exit
 
			}
 
			reply = tmprx[1];
 
 
			// Cycle chip select line on and off
 
			if (reply != 0xFF)
 
			{
 
				delay_cycles();
 
				SI446x_DESELECT;
 
				delay_cycles();
 
				SI446x_SELECT;
 
				delay_cycles();
 
				//HAL_GPIO_TogglePin(LED_ACT);
 
			}
 
 
			// Maximum number of attempts exceeded
 
			if(attempts > 1024)
 
			{
 
				//error_assert_silent(ERR_VHF_TIMEOUT);
 
				volatile uint32_t test = 344;
 
				break; // Break out, deinit and exit
 
			}
 
			attempts++;
 
		}
 
    }
 
 
    // Turn off activity LED
 
    //HAL_GPIO_WritePin(LED_ACT, GPIO_PIN_RESET);
 
 
    SI446x_DESELECT;
 
    delay_cycles();
 
}
 
 
 
// Set transmit frequency of Si446x
 
void si446x_setchannel(uint32_t frequency)
 
{
 
 
    // Set the output divider according to recommended ranges given in si446x datasheet
 
    uint32_t outdiv = 4;
 
    uint32_t band = 0;
 
    if (frequency < 705000000UL) { outdiv = 6;  band = 1;};
 
    if (frequency < 525000000UL) { outdiv = 8;  band = 2;};
 
    if (frequency < 353000000UL) { outdiv = 12; band = 3;};
 
    if (frequency < 239000000UL) { outdiv = 16; band = 4;};
 
    if (frequency < 177000000UL) { outdiv = 24; band = 5;};
 
 
    uint32_t f_pfd = 2 * SI446x_VCXO_FREQ / outdiv;
 
 
    uint32_t n = ((uint32_t)(frequency / f_pfd)) - 1;
 
 
    float ratio = (float)frequency / (float)f_pfd;
 
    float rest  = ratio - (float)n;
 
 
    uint32_t m = (uint32_t)(rest * 524288UL);
 
 
    // Set the band parameter
 
    uint32_t sy_sel = 8;
 
    uint8_t set_band_property_command[] = {SI446x_CMD_SET_PROPERTY, 0x20, 0x01, 0x51, (band + sy_sel)};
 
    si446x_sendcmd(5, set_band_property_command, SI446x_CHECK_ACK);
 
 
    // Set the pll parameters
 
    uint32_t m2 = m / 0x10000;
 
    uint32_t m1 = (m - m2 * 0x10000) / 0x100;
 
    uint32_t m0 = (m - m2 * 0x10000 - m1 * 0x100);
 
 
    // Assemble parameter string
 
    uint8_t set_frequency_property_command[] = {SI446x_CMD_SET_PROPERTY, 0x40, 0x04, 0x00, n, m2, m1, m0};
 
    si446x_sendcmd(8, set_frequency_property_command, SI446x_CHECK_ACK);
 
 
    // Set frequency deviation
 
    // ...empirically 0xF9 looks like about 5khz. Sketchy.
 
 
    //                           set prop   group     numprops  startprop   data                     // Was 0x0F 00 for ~40kHz dev, switched to 56khzish? dev
 
 
    //2DF5 is correct for 56khz
 
    uint8_t set_frequency_separation[] = {SI446x_CMD_SET_PROPERTY, 0x20,     0x03,      0x0a,     0x00, 0x03, 0x00};
 
    si446x_sendcmd(7, set_frequency_separation, SI446x_CHECK_ACK);
 
 
}
 
 
 
// Turn CW transmit on
 
void si446x_cw_on(void)
 
{
 
    // Change to TX state
 
	uint8_t change_state_command[] = {SI446x_CMD_CHANGE_STATE, 0x07};
 
    si446x_sendcmd(2, change_state_command, SI446x_CHECK_ACK);
 
    si446x_cw_status = 1;
 
}
 
 
 
// Turn CW transmit off
 
void si446x_cw_off(void)
 
{
 
    // Change to ready state
 
	uint8_t change_state_command[] = {SI446x_CMD_CHANGE_STATE, 0x03};
 
    si446x_sendcmd(2, change_state_command, SI446x_CHECK_ACK);
 
    si446x_cw_status = 0;
 
}
 
 
 
// Returns 1 if CW is on or 0 if CW is off
 
inline uint8_t si446x_tx_status(void)
 
{
 
	return si446x_cw_status;
 
}
 
 
 
// Initialize SPI port for generation of direct modulation for Si446x
 
static void __init_spi1(void)
 
{
 
 
	GPIO_InitTypeDef GPIO_InitStruct;
 
 
	// GPIO: VHF chip select
 
	GPIO_InitStruct.Pin = SI446x_CS_PIN;
 
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 
	GPIO_InitStruct.Pull = GPIO_NOPULL;
 
	GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
 
	HAL_GPIO_Init(SI446x_CS_PORT, &GPIO_InitStruct);
 
	SI446x_DESELECT;
 
 
	// SPI pins
 
	__SPI1_CLK_ENABLE();
 
	GPIO_InitStruct.Pin = SI446x_SCK_PIN|SI446x_MOSI_PIN|SI446x_MOSI_PIN;
 
	GPIO_InitStruct.Pin = SI446x_SCK_PIN|SI446x_MOSI_PIN|SI446x_MISO_PIN;
 
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 
	GPIO_InitStruct.Pull = GPIO_NOPULL;
 
	GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
 
	GPIO_InitStruct.Alternate = GPIO_AF0_SPI1;
 
	HAL_GPIO_Init(SI446x_SCK_PORT, &GPIO_InitStruct);
 
 
	// SPI peripheral
 
	hspi1.Instance = SPI1;
 
	hspi1.Init.Mode = SPI_MODE_MASTER;
 
 
	hspi1.Init.Direction = SPI_DIRECTION_2LINES;
 
 
	hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
 
	hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // Double-check, this is usually high, but might be low for this chip
 
	hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // was 1edge before (rising edge of clock)
 
	hspi1.Init.NSS = SPI_NSS_SOFT;
 
	hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
 
	hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
 
	hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
 
	hspi1.Init.TIMode = SPI_TIMODE_DISABLED;
 
	hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED;
 
	hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLED;
 
	HAL_SPI_Init(&hspi1);
 
}
 
 
 
// Place the Si446x into shutdown
 
void si446x_shutdown(void)
 
{
 
    HAL_GPIO_WritePin(SI446x_SHUTDOWN, GPIO_PIN_SET);
 
}
 
 
 
// Wake up the Si446x from shutdown
 
void si446x_wakeup(void)
 
{
 
    HAL_GPIO_WritePin(SI446x_SHUTDOWN, GPIO_PIN_RESET);
 
}
 
 
 
// Accessor for SPI1  handle
 
SPI_HandleTypeDef* spi1_get(void)
 
{
 
	return &hspi1;
 
}
Libraries/Si446x/si446x.h
Show inline comments
 
#ifndef SI446X_H
 
#define SI446X_H
 
 
#include "stm32f0xx_hal.h"
 
 
 
// Hardware Options ///////////////////
 
 
// Timeout for blocking writes
 
#define SI446x_TIMEOUT 10
 
#define SI446x_TIMEOUT 50
 
 
// Crystal/oscillator frequency
 
#define SI446x_VCXO_FREQ  26000000UL
 
#define SI446x_SPI SPI1
 
 
// GPIO assignments
 
#define SI446x_CS_PORT GPIOA
 
#define SI446x_CS_PIN GPIO_PIN_15
 
 
#define SI446x_GPIO_PORT GPIOA
 
#define SI446x_GPIO_PIN GPIO_PIN_10
 
 
#define SI446x_SHUTDOWN_PORT GPIOA
 
#define SI446x_SHUTDOWN_PIN GPIO_PIN_9
 
 
#define SI446x_MOSI_PORT GPIOB
 
#define SI446x_MOSI_PIN GPIO_PIN_5
 
 
#define SI446x_MISO_PORT GPIOB
 
#define SI446x_MISO_PIN GPIO_PIN_4
 
 
#define SI446x_SCK_PORT GPIOB
 
#define SI446x_SCK_PIN GPIO_PIN_3
 
 
 
#define SI446x_TCXO_EN_PORT GPIOA
 
#define SI446x_TCXO_EN_PIN GPIO_PIN_8
 
 
 
///////////////////////////////////////
 
 
 
// Registers /////////////////////////
 
#define SI446x_CMD_POWER_UP 0x02
 
#define SI446x_CMD_NOP 0x00
 
#define SI446x_CMD_PART_INFO 0x01
 
#define SI446x_CMD_FUNC_INFO 0x10
 
#define SI446x_CMD_SET_PROPERTY 0x11
 
#define SI446x_CMD_GET_PROPERTY 0x12
 
#define SI446x_CMD_GPIO_PIN_CFG 0x13
 
#define SI446x_CMD_GET_ADC_READING 0x14
 
#define SI446x_CMD_FIFO_INFO 0x15
 
#define SI446x_CMD_PACKET_INFO 0x16
 
#define SI446x_CMD_IRCAL 0x17
 
#define SI446x_CMD_PROTOCOL_CFG 0x18
 
#define SI446x_CMD_GET_INT_STATUS 0x20
 
#define SI446x_CMD_GET_PH_STATUS 0x21
 
#define SI446x_CMD_GET_MODEM_STATUS 0x22
 
#define SI446x_CMD_GET_CHIP_STATUS 0x23
 
#define SI446x_CMD_START_TX 0x31
 
#define SI446x_CMD_START_RX 0x32
 
#define SI446x_CMD_REQUEST_DEVICE_STATE 0x33
 
#define SI446x_CMD_CHANGE_STATE 0x34
 
#define SI446x_CMD_READ_CMD_BUFF 0x44
 
#define SI446x_CMD_FRR_A_READ 0x50
 
#define SI446x_CMD_FRR_B_READ 0x51
 
#define SI446x_CMD_FRR_C_READ 0x53
 
#define SI446x_CMD_FRR_D_READ 0x57
 
#define SI446x_CMD_WRITE_TX_FIFO 0x66
 
#define SI446x_CMD_READ_RX_FIFO 0x77
 
#define SI446x_CMD_RX_HOP 0x36
 
 
// GPIO pin configuration options
 
#define SI446x_GPIO_NOCHANGE 0
 
#define SI446x_GPIO_LOW 2
 
#define SI446x_GPIO_HIGH 3
 
#define SI446x_GPIO_INPUT 4
 
#define SI446x_GPIO_TXFIFO_LOW 35
 
#define SI446x_GPIO_TXENABLED 32
 
 
// GPIO pin definitions
 
#define SI446x_GPIO0 0
 
#define SI446x_GPIO1 1
 
#define SI446x_GPIO2 2
 
#define SI446x_GPIO3 3
 
 
// Property MOD_TYPE parameters
 
#define SI446x_MOD_TYPE_REGISTER_GROUP 0x20
 
#define SI446x_MOD_TYPE_REGISTER_PROP 0x00
 
#define SI446x_MOD_TYPE_CW    0
 
#define SI446x_MOD_TYPE_OOK   1
 
#define SI446x_MOD_TYPE_2FSK  2
 
#define SI446x_MOD_TYPE_2GFSK 3
 
#define SI446x_MOD_TYPE_4FSK  4
 
#define SI446x_MOD_TYPE_4GFSK 5
 
 
#define SI446x_MOD_TYPE_SOURCE_PACKETHANDLER (0<<3)
 
#define SI446x_MOD_TYPE_SOURCE_DIRECTMODE    (1<<3)
 
#define SI446x_MOD_TYPE_SOURCE_PSEUDORANDOM  (2<<3)
 
 
#define SI446x_MOD_TYPE_DIRECT_SYNCH (0<<7)
 
#define SI446x_MOD_TYPE_DIRECT_ASYNCH (1<<7)
 
 
#define SI446x_MOD_TYPE_DIRECT_SOURCE_GPIO0 (0<<5)
 
#define SI446x_MOD_TYPE_DIRECT_SOURCE_GPIO1 (1<<5)
 
#define SI446x_MOD_TYPE_DIRECT_SOURCE_GPIO2 (2<<5)
 
#define SI446x_MOD_TYPE_DIRECT_SOURCE_GPIO3 (3<<5)
 
 
// XO Tune
 
#define SI446x_XO_TUNE_REGISTER_GROUP 0x00
 
#define SI446x_XO_TUNE_REGISTER_PROP 0x00
 
#define SI446x_XO_TUNE_MAX 0x7F
 
 
// Modem bit mapping
 
#define SI446x_MODEM_MAP_GROUP 0x20
 
#define SI446x_MODEM_MAP_PROP 0x01
 
 
// Preamble
 
#define SI446x_PREAMBLE_TX_LENGTH_GROUP 0x10
 
#define SI446x_PREAMBLE_TX_LENGTH_PROP 0x00
 
 
// Sync
 
#define SI446x_SYNC_LENGTH_GROUP 0x11
 
#define SI446x_SYNC_LENGTH_PROP 0x00
 
 
 
// Power amp control DAC options
 
#define AD56XX_NORMAL_OPERATION 0b00000000
 
#define AD56XX_1K_TO_GROUND     0b01000000
 
#define AD56XX_100K_TO_GROUND   0b10000000
 
#define AD56XX_TRI_STATE        0b11000000
 
 
 
 
// Internal definitions
 
#define SI446x_CS SI446x_CS_PORT, SI446x_CS_PIN
 
#define SI446x_MOSI SI446x_MOSI_PORT, SI446x_MOSI_PIN
 
#define SI446x_MISO SI446x_MISO_PORT, SI446x_MISO_PIN
 
#define SI446x_SCK SI446x_SCK_PORT, SI446x_SCK_PIN
 
#define SI446x_GPIO SI446x_GPIO_PORT, SI446x_GPIO_PIN
 
#define SI446x_SHUTDOWN SI446x_SHUTDOWN_PORT , SI446x_SHUTDOWN_PIN
 
 
 
// Communication options
 
#define SI446x_IGNORE_ACK 0
 
#define SI446x_CHECK_ACK 1
 
 
 
// Helper macros
 
#define SI446x_SELECT HAL_GPIO_WritePin(SI446x_CS, GPIO_PIN_RESET)
 
#define SI446x_DESELECT HAL_GPIO_WritePin(SI446x_CS, GPIO_PIN_SET)
 
 
void si446x_init(void);
 
 
void si446x_reset(void);
 
 
void si446x_gpio(uint8_t gpio, uint8_t state, uint8_t doack);
 
 
void si446x_setdatarate(void);
 
 
void si446x_senddata(uint8_t* data, uint8_t len);
 
void si446x_sendcmd(uint8_t tx_len, uint8_t* data, uint8_t doack);
 
 
 
void si446x_setchannel(uint32_t frequency);
 
 
void si446x_cw_on(void);
 
void si446x_cw_off(void);
 
uint8_t si446x_tx_status(void);
 
 
SPI_HandleTypeDef* spi1_get(void);
 
 
void si446x_shutdown(void);
 
void si446x_wakeup(void);
 
 
#endif
Libraries/aprs/afsk.c
Show inline comments
 
/*
 
 * FeatherHAB 
 
 *
 
 * This file is part of FeatherHAB.
 
 *
 
 * FeatherHab is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * FeatherHab is distributed in the hope that it will be useful,
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 * GNU Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with FeatherHAB. If not, see <http://www.gnu.org/licenses/>.
 
 * 
 
 * Ethan Zonca
 
 *
 
 */
 

	
 
#include "stm32f0xx_hal.h"
 

	
 
#include "aprs/afsk.h"
 
#include "Si446x/si446x.h"
 
#include "config.h"
 

	
 

	
 
const uint8_t afsk_sine_table[512] = 
 
{127, 129, 130, 132, 133, 135, 136, 138, 139, 141, 143, 144, 146, 147, 149, 150, 152, 153, 155, 156, 158, 159, 161, 162, 164, 165, 167, 168, 170, 171, 173, 174, 176, 177, 178, 180, 181, 183, 184, 185, 187, 188, 190, 191, 192, 194, 195, 196, 198, 199, 200, 201, 203, 204, 205, 206, 208, 209, 210, 211, 212, 213, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 233, 234, 235, 236, 237, 238, 238, 239, 240, 240, 241, 242, 242, 243, 244, 244, 245, 245, 246, 247, 247, 248, 248, 249, 249, 249, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 253, 253, 253, 253, 253, 252, 252, 252, 252, 251, 251, 251, 250, 250, 249, 249, 249, 248, 248, 247, 247, 246, 245, 245, 244, 244, 243, 242, 242, 241, 240, 240, 239, 238, 238, 237, 236, 235, 234, 233, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 213, 212, 211, 210, 209, 208, 206, 205, 204, 203, 201, 200, 199, 198, 196, 195, 194, 192, 191, 190, 188, 187, 185, 184, 183, 181, 180, 178, 177, 176, 174, 173, 171, 170, 168, 167, 165, 164, 162, 161, 159, 158, 156, 155, 153, 152, 150, 149, 147, 146, 144, 143, 141, 139, 138, 136, 135, 133, 132, 130, 129, 127, 125, 124, 122, 121, 119, 118, 116, 115, 113, 111, 110, 108, 107, 105, 104, 102, 101, 99, 98, 96, 95, 93, 92, 90, 89, 87, 86, 84, 83, 81, 80, 78, 77, 76, 74, 73, 71, 70, 69, 67, 66, 64, 63, 62, 60, 59, 58, 56, 55, 54, 53, 51, 50, 49, 48, 46, 45, 44, 43, 42, 41, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 21, 20, 19, 18, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, 9, 8, 7, 7, 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 19, 20, 21, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 45, 46, 48, 49, 50, 51, 53, 54, 55, 56, 58, 59, 60, 62, 63, 64, 66, 67, 69, 70, 71, 73, 74, 76, 77, 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 115, 116, 118, 119, 121, 122, 124, 125 };
 

	
 
const uint16_t TABLE_SIZE = sizeof(afsk_sine_table);
 

	
 
static inline uint8_t afsk_read_sample(uint16_t phase)
 
{
 
    return afsk_sine_table[phase];
 
}
 

	
 

	
 
// constants
 
//#define MODEM_CLOCK_RATE F_CPU
 
//#define PLAYBACK_RATE MODEM_CLOCK_RATE / 256  // Fast PWM
 

	
 
#define BAUD_RATE 1200
 
//#define SAMPLES_PER_BAUD PLAYBACK_RATE / BAUD_RATE // = 156
 
//#define SAMPLES_PER_BAUD 36
 

	
 
// factor of 4 for the 4x clcokdiv on the counter (maybe kill that)
 
//#define SAMPLES_PER_BAUD 156 / 4 
 
#define SAMPLES_PER_BAUD 156 / 2 / 3
 

	
 

	
 
// phase offset of 830 gives ~1900 Hz
 
// phase offset of 1550 gives ~2200 Hz
 
//#define PHASE_DELTA_1200 1800
 
//#define PHASE_DELTA_2200 2200
 
//#define PHASE_DELTA_1200 8000
 
//#define PHASE_DELTA_2200 10000 
 

	
 
#define PHASE_DELTA_1200 830 * 3 //1800
 
#define PHASE_DELTA_2200 1550*3  //1550 * 6
 

	
 

	
 
// Module globals
 
volatile unsigned char current_byte;
 
volatile uint8_t current_sample_in_baud;    // 1 bit = SAMPLES_PER_BAUD samples
 
volatile uint16_t phase = 10;
 
volatile uint16_t phasedelta = 3300;
 

	
 
volatile uint8_t request_cwoff = 0;
 
volatile uint8_t go = 0;                 
 

	
 
volatile uint32_t packet_pos;                 // Next bit to be sent out
 

	
 
volatile uint32_t afsk_packet_size = 10;
 
volatile const uint8_t *afsk_packet;
 

	
 

	
 
#define PRESCALE_1200 155 * 6
 
#define PRESCALE_2200 84 * 6
 

	
 
volatile TIM_HandleTypeDef htim1;
 

	
 

	
 
////////////////////////////////////////////////
 
void afsk_init(void) 
 
{
 
    GPIO_InitTypeDef GPIO_InitStruct;
 
    GPIO_InitStruct.Pin = GPIO_PIN_10;
 
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 
    GPIO_InitStruct.Pull = GPIO_NOPULL;
 
    //GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM1;
 
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 

	
 
//    __HAL_RCC_TIM1_CLK_ENABLE();
 

	
 
    __TIM1_CLK_ENABLE();
 

	
 
	TIM_MasterConfigTypeDef sMasterConfig;
 
	TIM_OC_InitTypeDef sConfigOC;
 
	TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
 

	
 
	htim1.Instance = TIM1;
 
	htim1.Init.Prescaler = 1;
 
	htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
 
	htim1.Init.Period = 255;
 
	htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 
	htim1.Init.RepetitionCounter = 0;
 
	HAL_TIM_OC_Init(&htim1);
 

	
 
	sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 
	HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
 

	
 
	sConfigOC.OCMode = TIM_OCMODE_PWM1;
 
	sConfigOC.Pulse = 300;
 
	sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
 
	sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
 
	sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
 
	sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
 
	sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 
	HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3);
 

	
 
	sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
 
	sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
 
	sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
 
	sBreakDeadTimeConfig.DeadTime = 0;
 
	sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
 
	sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
 
	sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
 
	HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);
 

	
 
//
 
//
 
//    // Init gpio of pin
 
//    gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO10); //pa10 radio gp1
 
//    gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_HIGH,  GPIO10);
 
//    gpio_set_af(GPIOA, GPIO_AF2, GPIO10);
 
//
 
//    // Reset and configure timer
 
//    timer_reset(TIM1);
 
//    //TIM_CR1_CKD_CK_INT_MUL_4
 
//    timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, //TIM_CR1_CMS_CENTER_1,
 
//                       TIM_CR1_DIR_UP);
 
//    timer_set_prescaler(TIM1, 1); // 155 is 1200hz, 84 is 2200hz
 
//
 
//
 
//    timer_set_oc_mode(TIM1, TIM_OC3, TIM_OCM_PWM1);
 
//    timer_enable_oc_output(TIM1, TIM_OC3);
 
//    timer_enable_break_main_output(TIM1);
 
//    timer_set_oc_value(TIM1, TIM_OC3, 300);
 
//    timer_set_period(TIM1, 255); // should this be 256? FIXME
 
//    timer_enable_counter(TIM1);
 
//
 
//    // Enable interrupt for timer capture/compare
 
//    timer_enable_irq(TIM1, TIM_DIER_CC3IE);
 

	
 
    while(afsk_busy());
 
	//HAL_TIM_OC_Start_IT(&htim1, TIM_CHANNEL_3);
 

	
 
//    HAL_NVIC_SetPriority(TIM1_CC_IRQn, 0, 0);
 
    HAL_NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0, 0);
 
//	HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
 
	HAL_NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);
 

	
 
}
 

	
 

	
 
void afsk_output_sample(uint8_t s)
 
{
 
    //timer_set_oc_value(TIM1, TIM_OC3, s);
 
    htim1.Instance->CCR3 = s;
 
}
 

	
 
uint8_t afsk_request_cwoff(void)
 
{
 
    if(request_cwoff)
 
    {
 
        request_cwoff = 0;
 
        return 1;
 
    }
 
    else
 
    {
 
        return 0;
 
    }
 
}
 

	
 
volatile uint32_t freqctr = 0;
 

	
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
 
{
 
	if (go) {
 
			if (packet_pos == afsk_packet_size)
 
			{
 
					request_cwoff = 1;
 
					//gpio_toggle(GPIOB, GPIO0);
 
					//gpio_toggle(GPIOB, GPIO7);
 
					go = 0;         // End of transmission
 
					afsk_timer_stop();  // Disable modem
 
					return; // done
 
			}
 

	
 
			// If sent SAMPLES_PER_BAUD already, go to the next bit
 
			if (current_sample_in_baud == 0)  // Load up next bit
 
			{
 
					if ((packet_pos & 7) == 0) // Load up next byte
 
					{
 
							current_byte = afsk_packet[packet_pos >> 3];
 
					}
 
					else
 
					{
 
							current_byte = current_byte / 2;  // ">>1" forces int conversion
 
					}
 
					if ((current_byte & 1) == 0)
 
					{
 
							// Toggle tone (1200 <> 2200)
 
							phasedelta ^= (PHASE_DELTA_1200 ^ PHASE_DELTA_2200);
 
					}
 
			}
 

	
 
			phase += phasedelta;
 
			uint8_t s = afsk_read_sample((phase >> 7) & (TABLE_SIZE - 1));
 

	
 
			afsk_output_sample(s); //real afsk
 

	
 
			if(++current_sample_in_baud == SAMPLES_PER_BAUD)
 
			{
 
					current_sample_in_baud = 0;
 
					packet_pos++;
 
			}
 

	
 
	}
 
}
 

	
 

	
 
void afsk_timer_start()
 
{
 
    // Clear the overflow flag, so that the interrupt doesn't go off
 
    // immediately and overrun the next one (p.163).
 
    // Enable interrupt when TCNT2 reaches TOP (0xFF) (p.151, 163)
 
    //nvic_enable_irq(NVIC_TIM1_CC_IRQ);
 
//	HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
 
	HAL_NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);
 
	__HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
 
	HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_3);
 

	
 
    //timer_enable_counter(TIM1);
 
}
 

	
 
void afsk_timer_stop()
 
{
 
	// Resting duty cycle
 
	// Output 0v (could be 255/2, which is 0v after coupling... doesn't really matter)
 
	//OCR2A = 0x80;
 

	
 
	// Disable playback interrupt
 
	//TIMSK2 &= ~_BV(TOIE2);
 
    //nvic_disable_irq(NVIC_TIM1_CC_IRQ);
 
//	HAL_NVIC_DisableIRQ(TIM1_CC_IRQn);
 
//	HAL_NVIC_DisableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);
 
	HAL_TIM_PWM_Stop_IT(&htim1, TIM_CHANNEL_3);
 
    //timer_disable_counter(TIM1);
 
}
 

	
 

	
 
// External
 

	
 
void afsk_start(void)
 
{
 
	phasedelta = PHASE_DELTA_1200;
 
	phase = 0;
 
	packet_pos = 0;
 
	current_sample_in_baud = 0;
 
	go = 1;
 

	
 
    // Wake up and configure radio
 
    //si446x_prepare();
 
    HAL_Delay(100);
 

	
 
	// Key the radio
 
	si446x_cw_on();
 

	
 
	// Start transmission
 
	afsk_timer_start();
 
}
 

	
 
uint8_t afsk_busy(void)
 
{
 
	return go;
 
}
 

	
 
void afsk_send(const uint8_t *buffer, uint16_t len)
 
{
 
	afsk_packet_size = len;
 
	afsk_packet = buffer;
 
}
 

	
 
TIM_HandleTypeDef* afsk_timer_gethandle(void)
 
{
 
	return &htim1;
 
}
 

	
 

	
 
// vim:softtabstop=4 shiftwidth=4 expandtab 
Source/main.c
Show inline comments
 
//
 
// mBuoy Depth Select Firmware
 
// Copyright 2015 SeaLandAire Technologies
 
//
 
 
#include "config.h"
 
#include "error.h"
 
#include "system/gpio.h"
 
#include "system/sysclk.h"
 
#include "system/watchdog.h"
 
#include "system/uart.h"
 
#include "stm32f0xx_hal.h"
 
#include "si446x/si446x.h"
 
#include "aprs/aprs.h"
 
#include "aprs/afsk.h"
 
#include "gps.h"
 
 
 
int main(void)
 
{
 
  hal_init();
 
  sysclock_init();
 
  gpio_init();
 
 
 
  afsk_init();
 
  si446x_init();
 
  gps_poweron();
 
 
  // Software timers
 
  uint32_t last_led = HAL_GetTick();
 
 
  while (1)
 
  {
 
	  // Blink LEDs
 
	  if(HAL_GetTick() - last_led > 1500)
 
	  if(HAL_GetTick() - last_led > 50)
 
	  {
 
		  gps_update_data();
 
		  aprs_send();
 
		  while(afsk_busy());
 
 
		  last_led = HAL_GetTick();
 
	  }
 
 
	  if(afsk_request_cwoff())
 
		  si446x_cw_off();
 
 
	  // High-frequency function calls
 
//	  gpio_process_shutdown();
 
//	  watchdog_feed();
 
  }
 
}
 
0 comments (0 inline, 0 general)