Changeset - c5bc128e44a6
[Not reviewed]
default
0 3 0
Ethan Zonca - 10 years ago 2016-04-02 20:44:19
ez@ethanzonca.com
WSPR and gps now coexist, don't call jtencode_init because it inits the reed-solomon encoder that wspr doesn't even use
3 files changed with 64 insertions and 36 deletions:
0 comments (0 inline, 0 general)
STM32F031G6_FLASH.ld
Show inline comments
 
/*
 
*****************************************************************************
 
**
 
**  File        : stm32_flash.ld
 
**
 
**  Abstract    : Linker script for STM32F031G6 Device with
 
**                32KByte FLASH, 4KByte RAM
 
**
 
**                Set heap size, stack size and stack location according
 
**                to application requirements.
 
**
 
**                Set memory bank area and size if external memory is used.
 
**
 
**  Target      : STMicroelectronics STM32
 
**
 
**  Environment : Atollic TrueSTUDIO(R)
 
**
 
**  Distribution: The file is distributed “as is,” without any warranty
 
**                of any kind.
 
**
 
**  (c)Copyright Atollic AB.
 
**  You may use this file as-is or modify it according to the needs of your
 
**  project. This file may only be built (assembled or compiled and linked)
 
**  using the Atollic TrueSTUDIO(R) product. The use of this file together
 
**  with other tools than Atollic TrueSTUDIO(R) is not permitted.
 
**
 
*****************************************************************************
 
*/
 

	
 
/* Entry Point */
 
ENTRY(Reset_Handler)
 

	
 
/* Highest address of the user mode stack */
 
_estack = 0x20001000;    /* end of RAM */
 

	
 
/* Generate a link error if heap and stack don't fit into RAM */
 
_Min_Heap_Size = 0;      /* required amount of heap  */
 
_Min_Stack_Size = 0x300; /* required amount of stack */
 
_Min_Stack_Size = 0x200; /* required amount of stack */
 

	
 
/* Specify the memory areas */
 
MEMORY
 
{
 
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 32K
 
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 4K
 
}
 

	
 
/* Define output sections */
 
SECTIONS
 
{
 
  /* The startup code goes first into FLASH */
 
  .isr_vector :
 
  {
 
    . = ALIGN(4);
 
    KEEP(*(.isr_vector)) /* Startup code */
 
    . = ALIGN(4);
 
  } >FLASH
 

	
 
  /* The program code and other data goes into FLASH */
 
  .text :
 
  {
 
    . = ALIGN(4);
 
    *(.text)           /* .text sections (code) */
 
    *(.text*)          /* .text* sections (code) */
 
    *(.glue_7)         /* glue arm to thumb code */
 
    *(.glue_7t)        /* glue thumb to arm code */
 
    *(.eh_frame)
 

	
 
    KEEP (*(.init))
 
    KEEP (*(.fini))
 

	
 
    . = ALIGN(4);
 
    _etext = .;        /* define a global symbols at end of code */
 
  } >FLASH
 

	
 
  /* Constant data goes into FLASH */
 
  .rodata :
 
  {
 
    . = ALIGN(4);
 
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
 
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
 
    . = ALIGN(4);
 
  } >FLASH
 

	
 
  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
 
  .ARM : {
 
    __exidx_start = .;
 
    *(.ARM.exidx*)
 
    __exidx_end = .;
 
  } >FLASH
 

	
 
  .preinit_array     :
 
  {
 
    PROVIDE_HIDDEN (__preinit_array_start = .);
 
    KEEP (*(.preinit_array*))
 
    PROVIDE_HIDDEN (__preinit_array_end = .);
 
  } >FLASH
 
  .init_array :
 
  {
 
    PROVIDE_HIDDEN (__init_array_start = .);
 
    KEEP (*(SORT(.init_array.*)))
 
    KEEP (*(.init_array*))
 
    PROVIDE_HIDDEN (__init_array_end = .);
 
  } >FLASH
 
  .fini_array :
 
  {
 
    PROVIDE_HIDDEN (__fini_array_start = .);
 
    KEEP (*(SORT(.fini_array.*)))
 
    KEEP (*(.fini_array*))
 
    PROVIDE_HIDDEN (__fini_array_end = .);
 
  } >FLASH
 

	
 
  /* used by the startup to initialize data */
 
  _sidata = LOADADDR(.data);
 

	
 
  /* Initialized data sections goes into RAM, load LMA copy after code */
 
  .data : 
 
  {
 
    . = ALIGN(4);
 
    _sdata = .;        /* create a global symbol at data start */
 
    *(.data)           /* .data sections */
 
    *(.data*)          /* .data* sections */
 

	
 
    . = ALIGN(4);
 
    _edata = .;        /* define a global symbol at data end */
 
  } >RAM AT> FLASH
 

	
 
  
 
  /* Uninitialized data section */
 
  . = ALIGN(4);
 
  .bss :
 
  {
 
    /* This is used by the startup in order to initialize the .bss secion */
 
    _sbss = .;         /* define a global symbol at bss start */
 
    __bss_start__ = _sbss;
 
    *(.bss)
 
    *(.bss*)
 
    *(COMMON)
 

	
 
    . = ALIGN(4);
 
    _ebss = .;         /* define a global symbol at bss end */
 
    __bss_end__ = _ebss;
 
  } >RAM
 

	
 
  /* User_heap_stack section, used to check that there is enough RAM left */
 
  ._user_heap_stack :
 
  {
 
    . = ALIGN(4);
 
    PROVIDE ( end = . );
 
    PROVIDE ( _end = . );
 
    . = . + _Min_Heap_Size;
 
    . = . + _Min_Stack_Size;
 
    . = ALIGN(4);
 
  } >RAM
 

	
 
  
 

	
 
  /* Remove information from the standard libraries */
 
  /DISCARD/ :
 
  {
 
    libc.a ( * )
 
    libm.a ( * )
 
    libgcc.a ( * )
 
  }
 

	
 
  .ARM.attributes 0 : { *(.ARM.attributes) }
 
}
lib/jtencode/jtencode.c
Show inline comments
 
/*
 
 * JTEncode.cpp - JT65/JT9/WSPR encoder library for Arduino
 
 *
 
 * Copyright (C) 2015 Jason Milldrum <milldrum@gmail.com>
 
 *
 
 * Based on the algorithms presented in the WSJT software suite.
 
 * Thanks to Andy Talbot G4JNT for the whitepaper on the WSPR encoding
 
 * process that helped me to understand all of this.
 
 *
 
 * This program is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * This program 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 General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#include "jtencode.h"
 

	
 
#include <string.h>
 
#include <stdlib.h>
 
#include <stdint.h>
 
#include <ctype.h>
 

	
 

	
 
uint8_t jt_code(char);
 
uint8_t wspr_code(char);
 
uint8_t gray_code(uint8_t);
 
void jt_message_prep(char *);
 
void wspr_message_prep(char *, char *, uint8_t);
 
void jt65_bit_packing(char *, uint8_t *);
 
void jt9_bit_packing(char *, uint8_t *);
 
void wspr_bit_packing(uint8_t *);
 
void jt65_interleave(uint8_t *);
 
void jt9_interleave(uint8_t *);
 
void wspr_interleave(uint8_t *);
 
void jt9_packbits(uint8_t *, uint8_t *);
 
void jt_gray_code(uint8_t *, uint8_t);
 
void jt65_merge_sync_vector(uint8_t *, uint8_t *);
 
void jt9_merge_sync_vector(uint8_t *, uint8_t *);
 
void jt4_merge_sync_vector(uint8_t *, uint8_t *);
 
void wspr_merge_sync_vector(uint8_t *, uint8_t *);
 
void convolve(uint8_t *, uint8_t *, uint8_t, uint8_t);
 
void rs_encode(uint8_t *, uint8_t *);
 
void encode_rs_int(void *,data_t *, data_t *);
 
void free_rs_int(void *);
 
void * init_rs_int(int, int, int, int, int, int);
 
void * rs_inst;
 
char callsign[7];
 
char locator[5];
 
uint8_t power;
 

	
 

	
 
static void emz_memcpy( uint8_t *pDest, uint8_t *pSrc, uint32_t len )
 
{
 
    uint32_t i;
 

	
 
    // Manually copy the data
 
    for ( i = 0; i < len; i++ )
 
    {
 
        // Copy data from source to destination
 
        *pDest++ = *pSrc++;
 
    }
 
}
 

	
 

	
 
/* Public Class Members */
 

	
 
void* jtencode_init(void)
 
{
 
  // Initialize the Reed-Solomon encoder
 
  rs_inst = (struct rs *)(intptr_t)init_rs_int(6, 0x43, 3, 1, 51, 0);
 
}
 

	
 
/*
 
 * jt65_encode(char * message, uint8_t * symbols)
 
 *
 
 * Takes an arbitrary message of up to 13 allowable characters and returns
 
 * a channel symbol table.
 
 *
 
 * message - Plaintext Type 6 message.
 
 * symbols - Array of channel symbols to transmit retunred by the method.
 
 *  Ensure that you pass a uint8_t array of size JT65_SYMBOL_COUNT to the method.
 
 *
 
 */
 
void jt65_encode(char * message, uint8_t * symbols)
 
{
 
  // Ensure that the message text conforms to standards
 
  // --------------------------------------------------
 
  jt_message_prep(message);
 

	
 
  // Bit packing
 
  // -----------
 
  uint8_t c[12];
 
  jt65_bit_packing(message, c);
 

	
 
  // Reed-Solomon encoding
 
  // ---------------------
 
  uint8_t s[JT65_ENCODE_COUNT];
 
  rs_encode(c, s);
 

	
 
  // Interleaving
 
  // ------------
 
  jt65_interleave(s);
 

	
 
  // Gray Code
 
  // ---------
 
  jt_gray_code(s, JT65_ENCODE_COUNT);
 

	
 
  // Merge with sync vector
 
  // ----------------------
 
  jt65_merge_sync_vector(s, symbols);
 
}
 

	
 
/*
 
 * jt9_encode(char * message, uint8_t * symbols)
 
 *
 
 * Takes an arbitrary message of up to 13 allowable characters and returns
 
 * a channel symbol table.
 
 *
 
 * message - Plaintext Type 6 message.
 
 * symbols - Array of channel symbols to transmit retunred by the method.
 
 *  Ensure that you pass a uint8_t array of size JT9_SYMBOL_COUNT to the method.
 
 *
 
 */
 
void jt9_encode(char * message, uint8_t * symbols)
 
{
 
  // Ensure that the message text conforms to standards
 
  // --------------------------------------------------
 
  jt_message_prep(message);
 

	
 
  // Bit packing
 
  // -----------
 
  uint8_t c[13];
 
  jt9_bit_packing(message, c);
 

	
 
  // Convolutional Encoding
 
  // ---------------------
 
  uint8_t s[JT9_BIT_COUNT];
 
  convolve(c, s, 13, JT9_BIT_COUNT);
 

	
 
  // Interleaving
 
  // ------------
 
  jt9_interleave(s);
 

	
 
  // Pack into 3-bit symbols
 
  // -----------------------
 
  uint8_t a[JT9_ENCODE_COUNT];
 
  jt9_packbits(s, a);
 

	
 
  // Gray Code
 
  // ---------
 
  jt_gray_code(a, JT9_ENCODE_COUNT);
 

	
 
  // Merge with sync vector
 
  // ----------------------
 
  jt9_merge_sync_vector(a, symbols);
 
}
 

	
 
/*
 
 * jt9_encode(char * message, uint8_t * symbols)
 
 *
 
 * Takes an arbitrary message of up to 13 allowable characters and returns
 
 * a channel symbol table.
 
 *
 
 * message - Plaintext Type 6 message.
 
 * symbols - Array of channel symbols to transmit retunred by the method.
 
 *  Ensure that you pass a uint8_t array of size JT9_SYMBOL_COUNT to the method.
 
 *
 
 */
 
void jt4_encode(char * message, uint8_t * symbols)
 
{
 
    // emz comment memmove
 
  // Ensure that the message text conforms to standards
 
  // --------------------------------------------------
 
  jt_message_prep(message);
 

	
 
  // Bit packing
 
  // -----------
 
  uint8_t c[13];
 
  jt9_bit_packing(message, c);
 

	
 
  // Convolutional Encoding
 
  // ---------------------
 
  uint8_t s[JT4_SYMBOL_COUNT];
 
  convolve(c, s, 13, JT4_BIT_COUNT);
 

	
 
  // Interleaving
 
  // ------------
 
  jt9_interleave(s);
 
  memmove(s + 1, s, JT4_BIT_COUNT);
 
  s[0] = 0; // Append a 0 bit to start of sequence
 

	
 
  // Merge with sync vector
 
  // ----------------------
 
  jt4_merge_sync_vector(s, symbols);
 
//  // --------------------------------------------------
 
//  jt_message_prep(message);
 
//
 
//  // Bit packing
 
//  // -----------
 
//  uint8_t c[13];
 
//  jt9_bit_packing(message, c);
 
//
 
//  // Convolutional Encoding
 
//  // ---------------------
 
//  uint8_t s[JT4_SYMBOL_COUNT];
 
//  convolve(c, s, 13, JT4_BIT_COUNT);
 
//
 
//  // Interleaving
 
//  // ------------
 
//  jt9_interleave(s);
 
//  memmove(s + 1, s, JT4_BIT_COUNT);
 
//  s[0] = 0; // Append a 0 bit to start of sequence
 
//
 
//  // Merge with sync vector
 
//  // ----------------------
 
//  jt4_merge_sync_vector(s, symbols);
 
}
 

	
 
/*
 
 * wspr_encode(char * call, char * loc, uint8_t dbm, uint8_t * symbols)
 
 *
 
 * Takes an arbitrary message of up to 13 allowable characters and returns
 
 *
 
 * call - Callsign (6 characters maximum).
 
 * loc - Maidenhead grid locator (4 charcters maximum).
 
 * loc - Maidenhead grid locator (4 characters maximum).
 
 * dbm - Output power in dBm.
 
 * symbols - Array of channel symbols to transmit retunred by the method.
 
 * symbols - Array of channel symbols to transmit returned by the method.
 
 *  Ensure that you pass a uint8_t array of size WSPR_SYMBOL_COUNT to the method.
 
 *
 
 */
 
void wspr_encode(char * call, char * loc, uint8_t dbm, uint8_t * symbols)
 
{
 
    // Ensure that the message text conforms to standards
 
  // --------------------------------------------------
 
  wspr_message_prep(call, loc, dbm);
 

	
 
  // Bit packing
 
  // -----------
 
  uint8_t c[11];
 
  wspr_bit_packing(c);
 

	
 
  // Convolutional Encoding
 
  // ---------------------
 
  uint8_t s[WSPR_SYMBOL_COUNT];
 
  convolve(c, s, 11, WSPR_BIT_COUNT);
 

	
 
  // Interleaving
 
  // ------------
 
  wspr_interleave(s);
 

	
 
  // Merge with sync vector
 
  // ----------------------
 
  wspr_merge_sync_vector(s, symbols);
 
}
 

	
 
/* Private Class Members */
 

	
 
uint8_t jt_code(char c)
 
{
 
  // Validate the input then return the proper integer code.
 
  // Return 255 as an error code if the char is not allowed.
 

	
 
  if(isdigit(c))
 
  {
 
    return (uint8_t)(c - 48);
 
  }
 
  else if(c >= 'A' && c <= 'Z')
 
  {
 
    return (uint8_t)(c - 55);
 
  }
 
  else if(c == ' ')
 
  {
 
    return 36;
 
  }
 
  else if(c == '+')
 
  {
 
    return 37;
 
  }
 
  else if(c == '-')
 
  {
 
    return 38;
 
  }
 
  else if(c == '.')
 
  {
 
    return 39;
 
  }
 
  else if(c == '/')
 
  {
 
    return 40;
 
  }
 
  else if(c == '?')
 
  {
 
    return 41;
 
  }
 
  else
 
  {
 
    return 255;
 
  }
 
}
 

	
 
uint8_t wspr_code(char c)
 
{
 
  // Validate the input then return the proper integer code.
 
  // Return 255 as an error code if the char is not allowed.
 

	
 
  if(isdigit(c))
 
	{
 
		return (uint8_t)(c - 48);
 
	}
 
	else if(c == ' ')
 
	{
 
		return 36;
 
	}
 
	else if(c >= 'A' && c <= 'Z')
 
	{
 
		return (uint8_t)(c - 55);
 
	}
 
	else
 
	{
 
		return 255;
 
	}
 
}
 

	
 
uint8_t gray_code(uint8_t c)
 
{
 
  return (c >> 1) ^ c;
 
}
 

	
 
void jt_message_prep(char * message)
 
{
 
  uint8_t i, j;
 

	
 
  // Convert all chars to uppercase
 
  for(i = 0; i < 13; i++)
 
  {
 
    if(islower(message[i]))
 
    {
 
      message[i] = toupper(message[i]);
 
    }
 
  }
 

	
 
  // Pad the message with trailing spaces
 
  uint8_t len = strlen(message);
 
  if(len < 13)
 
  {
 
    for(i = len; i < 13; i++)
 
    {
 
      message[i] = ' ';
 
    }
 
  }
 
}
 

	
 
void wspr_message_prep(char * call, char * loc, uint8_t dbm)
 
{
 
  // Callsign validation and padding
 
  // -------------------------------
 

	
 
	// If only the 2nd character is a digit, then pad with a space.
 
	// If this happens, then the callsign will be truncated if it is
 
	// longer than 5 characters.
 
	if((call[1] >= '0' && call[1] <= '9') && (call[2] < '0' || call[2] > '9'))
 
	{
 
		memmove(call + 1, call, 5);
 
		//memmove(call + 1, call, 5);
 
		//call[0] = ' ';
 

	
 
	    call[6] = '\0';
 
		call[5] = call[4];
 
		call[4] = call[3];
 
		call[3] = call[2];
 
		call[2] = call[1];
 
		call[1] = call[0];
 
		call[0] = ' ';
 

	
 

	
 
	}
 

	
 
	// Now the 3rd charcter in the callsign must be a digit
 
	if(call[2] < '0' || call[2] > '9')
 
	{
 
    // TODO: need a better way to handle this
 
		call[2] = '0';
 
	}
 

	
 
	// Ensure that the only allowed characters are digits and
 
	// uppercase letters
 
	uint8_t i;
 
	for(i = 0; i < 6; i++)
 
	{
 
		call[i] = toupper(call[i]);
 
		if(!(isdigit(call[i]) || isupper(call[i])))
 
		{
 
			call[i] = ' ';
 
		}
 
	}
 

	
 
  memcpy(callsign, call, 6);
 
	emz_memcpy(callsign, call, 6);
 

	
 
	// Grid locator validation
 
	for(i = 0; i < 4; i++)
 
	{
 
		loc[i] = toupper(loc[i]);
 
		if(!(isdigit(loc[i]) || (loc[i] >= 'A' && loc[i] <= 'R')))
 
		{
 
			loc = "AA00";
 
		}
 
	}
 

	
 
  memcpy(locator, loc, 4);
 
	emz_memcpy(locator, loc, 4);
 

	
 
	// Power level validation
 
	// Only certain increments are allowed
 
	if(dbm > 60)
 
	{
 
		dbm = 60;
 
	}
 
  const uint8_t valid_dbm[19] =
 
    {0, 3, 7, 10, 13, 17, 20, 23, 27, 30, 33, 37, 40,
 
     43, 47, 50, 53, 57, 60};
 
  for(i = 0; i < 19; i++)
 
  {
 
    if(dbm == valid_dbm[i])
 
    {
 
      power = dbm;
 
    }
 
  }
 
  // If we got this far, we have an invalid power level, so we'll round down
 
  for(i = 1; i < 19; i++)
 
  {
 
    if(dbm < valid_dbm[i] && dbm >= valid_dbm[i - 1])
 
    {
 
      power = valid_dbm[i - 1];
 
    }
 
  }
 
}
 

	
 
void jt65_bit_packing(char * message, uint8_t * c)
 
{
 
  uint32_t n1, n2, n3;
 

	
 
  // Find the N values
 
  n1 = jt_code(message[0]);
 
  n1 = n1 * 42 + jt_code(message[1]);
 
  n1 = n1 * 42 + jt_code(message[2]);
 
  n1 = n1 * 42 + jt_code(message[3]);
 
  n1 = n1 * 42 + jt_code(message[4]);
 

	
 
  n2 = jt_code(message[5]);
 
  n2 = n2 * 42 + jt_code(message[6]);
 
  n2 = n2 * 42 + jt_code(message[7]);
 
  n2 = n2 * 42 + jt_code(message[8]);
 
  n2 = n2 * 42 + jt_code(message[9]);
 

	
 
  n3 = jt_code(message[10]);
 
  n3 = n3 * 42 + jt_code(message[11]);
 
  n3 = n3 * 42 + jt_code(message[12]);
 

	
 
  // Pack bits 15 and 16 of N3 into N1 and N2,
 
  // then mask reset of N3 bits
 
  n1 = (n1 << 1) + ((n3 >> 15) & 1);
 
  n2 = (n2 << 1) + ((n3 >> 16) & 1);
 
  n3 = n3 & 0x7fff;
 

	
 
  // Set the freeform message flag
 
  n3 += 32768;
 

	
 
  c[0] = (n1 >> 22) & 0x003f;
 
  c[1] = (n1 >> 16) & 0x003f;
 
  c[2] = (n1 >> 10) & 0x003f;
 
  c[3] = (n1 >> 4) & 0x003f;
 
  c[4] = ((n1 & 0x000f) << 2) + ((n2 >> 26) & 0x0003);
 
  c[5] = (n2 >> 20) & 0x003f;
 
  c[6] = (n2 >> 14) & 0x003f;
 
  c[7] = (n2 >> 8) & 0x003f;
 
  c[8] = (n2 >> 2) & 0x003f;
 
  c[9] = ((n2 & 0x0003) << 4) + ((n3 >> 12) & 0x000f);
 
  c[10] = (n3 >> 6) & 0x003f;
 
  c[11] = n3 & 0x003f;
 
}
 

	
 
void jt9_bit_packing(char * message, uint8_t * c)
 
{
 
  uint32_t n1, n2, n3;
 

	
 
  // Find the N values
 
  n1 = jt_code(message[0]);
 
  n1 = n1 * 42 + jt_code(message[1]);
 
  n1 = n1 * 42 + jt_code(message[2]);
 
  n1 = n1 * 42 + jt_code(message[3]);
 
  n1 = n1 * 42 + jt_code(message[4]);
 

	
 
  n2 = jt_code(message[5]);
 
  n2 = n2 * 42 + jt_code(message[6]);
 
  n2 = n2 * 42 + jt_code(message[7]);
 
  n2 = n2 * 42 + jt_code(message[8]);
 
  n2 = n2 * 42 + jt_code(message[9]);
 

	
 
  n3 = jt_code(message[10]);
 
  n3 = n3 * 42 + jt_code(message[11]);
 
  n3 = n3 * 42 + jt_code(message[12]);
 

	
 
  // Pack bits 15 and 16 of N3 into N1 and N2,
 
  // then mask reset of N3 bits
 
  n1 = (n1 << 1) + ((n3 >> 15) & 1);
 
  n2 = (n2 << 1) + ((n3 >> 16) & 1);
 
  n3 = n3 & 0x7fff;
 

	
 
  // Set the freeform message flag
 
  n3 += 32768;
 

	
 
  // 71 message bits to pack, plus 1 bit flag for freeform message.
 
  // 31 zero bits appended to end.
 
  // N1 and N2 are 28 bits each, N3 is 16 bits
 
  // A little less work to start with the least-significant bits
 
  c[3] = (uint8_t)((n1 & 0x0f) << 4);
 
  n1 = n1 >> 4;
 
  c[2] = (uint8_t)(n1 & 0xff);
 
  n1 = n1 >> 8;
 
  c[1] = (uint8_t)(n1 & 0xff);
 
  n1 = n1 >> 8;
 
  c[0] = (uint8_t)(n1 & 0xff);
 

	
 
  c[6] = (uint8_t)(n2 & 0xff);
 
  n2 = n2 >> 8;
 
  c[5] = (uint8_t)(n2 & 0xff);
 
  n2 = n2 >> 8;
 
  c[4] = (uint8_t)(n2 & 0xff);
 
  n2 = n2 >> 8;
 
  c[3] |= (uint8_t)(n2 & 0x0f);
 

	
 
  c[8] = (uint8_t)(n3 & 0xff);
 
  n3 = n3 >> 8;
 
  c[7] = (uint8_t)(n3 & 0xff);
 

	
 
  c[9] = 0;
 
  c[10] = 0;
 
  c[11] = 0;
 
  c[12] = 0;
 
}
 

	
 
void wspr_bit_packing(uint8_t * c)
 
{
 
  uint32_t n, m;
 

	
 
	n = wspr_code(callsign[0]);
 
	n = n * 36 + wspr_code(callsign[1]);
 
	n = n * 10 + wspr_code(callsign[2]);
 
	n = n * 27 + (wspr_code(callsign[3]) - 10);
 
	n = n * 27 + (wspr_code(callsign[4]) - 10);
 
	n = n * 27 + (wspr_code(callsign[5]) - 10);
 

	
 
	m = ((179 - 10 * (locator[0] - 'A') - (locator[2] - '0')) * 180) +
 
		(10 * (locator[1] - 'A')) + (locator[3] - '0');
 
	m = (m * 128) + power + 64;
 

	
 
	// Callsign is 28 bits, locator/power is 22 bits.
 
	// A little less work to start with the least-significant bits
 
	c[3] = (uint8_t)((n & 0x0f) << 4);
 
	n = n >> 4;
 
	c[2] = (uint8_t)(n & 0xff);
 
	n = n >> 8;
 
	c[1] = (uint8_t)(n & 0xff);
 
	n = n >> 8;
 
	c[0] = (uint8_t)(n & 0xff);
 

	
 
	c[6] = (uint8_t)((m & 0x03) << 6);
 
	m = m >> 2;
 
	c[5] = (uint8_t)(m & 0xff);
 
	m = m >> 8;
 
	c[4] = (uint8_t)(m & 0xff);
 
	m = m >> 8;
 
	c[3] |= (uint8_t)(m & 0x0f);
 
	c[7] = 0;
 
	c[8] = 0;
 
	c[9] = 0;
 
	c[10] = 0;
 
}
 

	
 
void jt65_interleave(uint8_t * s)
 
{
 
  uint8_t i, j;
 
  uint8_t d[JT65_ENCODE_COUNT];
 
  uint8_t d1[7][9];
 

	
 
  // Fill temp d1 array
 
  for(i = 0; i < 9; i++)
 
  {
 
    for(j = 0; j < 7; j++)
 
    {
 
      d1[i][j] = s[(i * 7) + j];
 
    }
 
  }
 

	
 
  // Interleave and translate back to 1D destination array
 
  for(i = 0; i < 7; i++)
 
  {
 
    for(j = 0; j < 9; j++)
 
    {
 
      d[(i * 9) + j] = d1[j][i];
 
    }
 
  }
 

	
 
  memcpy(s, d, JT65_ENCODE_COUNT);
 
  emz_memcpy(s, d, JT65_ENCODE_COUNT);
 
}
 

	
 
void jt9_interleave(uint8_t * s)
 
{
 
  uint8_t i, j, k, n;
 
  uint8_t d[JT9_BIT_COUNT];
 
  uint8_t j0[JT9_BIT_COUNT];
 

	
 
  k = 0;
 

	
 
  // Build the interleave table
 
  for(i = 0; i < 255; i++)
 
  {
 
    n = 0;
 

	
 
    for(j = 0; j < 8; j++)
 
    {
 
      n = (n << 1) + ((i >> j) & 1);
 
    }
 

	
 
    if(n < 206)
 
    {
 
      j0[k] = n;
 
      k++;
 
    }
 

	
 
    if(k >= 206)
 
    {
 
      break;
 
    }
 
  }
 

	
 
  // Now do the interleave
 
  for(i = 0; i < 206; i++)
 
  {
 
    d[j0[i]] = s[i];
 
  }
 

	
 
  memcpy(s, d, JT9_BIT_COUNT);
 
  emz_memcpy(s, d, JT9_BIT_COUNT);
 
}
 

	
 
void wspr_interleave(uint8_t * s)
 
{
 
  uint8_t d[WSPR_BIT_COUNT];
 
	uint8_t rev, index_temp, i, j, k;
 

	
 
	i = 0;
 

	
 
	for(j = 0; j < 255; j++)
 
	{
 
		// Bit reverse the index
 
		index_temp = j;
 
		rev = 0;
 

	
 
		for(k = 0; k < 8; k++)
 
		{
 
			if(index_temp & 0x01)
 
			{
 
				rev = rev | (1 << (7 - k));
 
			}
 
			index_temp = index_temp >> 1;
 
		}
 

	
 
		if(rev < WSPR_BIT_COUNT)
 
		{
 
			d[rev] = s[i];
 
			i++;
 
		}
 

	
 
		if(i >= WSPR_BIT_COUNT)
 
		{
 
			break;
 
		}
 
	}
 

	
 
  memcpy(s, d, WSPR_BIT_COUNT);
 
	emz_memcpy(s, d, WSPR_BIT_COUNT);
 
}
 

	
 
void jt9_packbits(uint8_t * d, uint8_t * a)
 
{
 
  uint8_t i, k;
 
  k = 0;
 
  memset(a, 0, JT9_ENCODE_COUNT);
 

	
 
  //memset(a, 0, JT9_ENCODE_COUNT);
 
  for(uint8_t iter = 0; iter<JT9_ENCODE_COUNT; iter++)
 
  {
 
      a[iter] = 0;
 
  }
 

	
 
  for(i = 0; i < JT9_ENCODE_COUNT; i++)
 
  {
 
    a[i] = (d[k] & 1) << 2;
 
    k++;
 

	
 
    a[i] |= ((d[k] & 1) << 1);
 
    k++;
 

	
 
    a[i] |= (d[k] & 1);
 
    k++;
 
  }
 
}
 

	
 
void jt_gray_code(uint8_t * g, uint8_t symbol_count)
 
{
 
  uint8_t i;
 

	
 
  for(i = 0; i < symbol_count; i++)
 
  {
 
    g[i] = gray_code(g[i]);
 
  }
 
}
 

	
 
void jt65_merge_sync_vector(uint8_t * g, uint8_t * symbols)
 
{
 
  uint8_t i, j = 0;
 
  const uint8_t sync_vector[JT65_SYMBOL_COUNT] =
 
  {1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0,
 
   0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1,
 
   0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1,
 
   0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
 
   1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1,
 
   0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
 
   1, 1, 1, 1, 1, 1};
 

	
 
  for(i = 0; i < JT65_SYMBOL_COUNT; i++)
 
  {
 
    if(sync_vector[i])
 
    {
 
      symbols[i] = 0;
 
    }
 
    else
 
    {
 
      symbols[i] = g[j] + 2;
 
      j++;
 
    }
 
  }
 
}
 

	
 
void jt9_merge_sync_vector(uint8_t * g, uint8_t * symbols)
 
{
 
  uint8_t i, j = 0;
 
  const uint8_t sync_vector[JT9_SYMBOL_COUNT] =
 
  {1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
 
   0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
 
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1,
 
   0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
 
   0, 0, 1, 0, 1};
 

	
 
  for(i = 0; i < JT9_SYMBOL_COUNT; i++)
 
  {
 
    if(sync_vector[i])
 
    {
 
      symbols[i] = 0;
 
    }
 
    else
 
    {
 
      symbols[i] = g[j] + 1;
 
      j++;
 
    }
 
  }
 
}
 

	
 
void jt4_merge_sync_vector(uint8_t * g, uint8_t * symbols)
 
{
 
  uint8_t i;
 
  const uint8_t sync_vector[JT4_SYMBOL_COUNT] =
 
	{0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0,
 
   0, 0, 0, 0, 1, 1, 0, 0, 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0 ,1 ,1,
 
   0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0,
 
   1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0,
 
   0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0,
 
   1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1,
 
   1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
 
   0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
 
   1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1,
 
   0, 1, 1, 1, 1, 0, 1, 0, 1};
 

	
 
	for(i = 0; i < JT4_SYMBOL_COUNT; i++)
 
	{
 
		symbols[i] = sync_vector[i] + (2 * g[i]);
 
	}
 
}
 

	
 
void wspr_merge_sync_vector(uint8_t * g, uint8_t * symbols)
 
{
 
  uint8_t i;
 
  const uint8_t sync_vector[WSPR_SYMBOL_COUNT] =
 
	{1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0,
 
	 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0,
 
	 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1,
 
	 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0,
 
	 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,
 
	 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1,
 
	 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
 
	 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0};
 

	
 
	for(i = 0; i < WSPR_SYMBOL_COUNT; i++)
 
	{
 
		symbols[i] = sync_vector[i] + (2 * g[i]);
 
	}
 
}
 

	
 
void convolve(uint8_t * c, uint8_t * s, uint8_t message_size, uint8_t bit_size)
 
{
 
  uint32_t reg_0 = 0;
 
  uint32_t reg_1 = 0;
 
  uint32_t reg_temp = 0;
 
  uint8_t input_bit, parity_bit;
 
  uint8_t bit_count = 0;
 
  uint8_t i, j, k;
 

	
 
  for(i = 0; i < message_size; i++)
 
  {
 
    for(j = 0; j < 8; j++)
 
    {
 
      // Set input bit according the MSB of current element
 
      input_bit = (((c[i] << j) & 0x80) == 0x80) ? 1 : 0;
 

	
 
      // Shift both registers and put in the new input bit
 
      reg_0 = reg_0 << 1;
 
      reg_1 = reg_1 << 1;
 
      reg_0 |= (uint32_t)input_bit;
 
      reg_1 |= (uint32_t)input_bit;
 

	
 
      // AND Register 0 with feedback taps, calculate parity
 
      reg_temp = reg_0 & 0xf2d05351;
 
      parity_bit = 0;
 
      for(k = 0; k < 32; k++)
 
      {
 
        parity_bit = parity_bit ^ (reg_temp & 0x01);
 
        reg_temp = reg_temp >> 1;
 
      }
 
      s[bit_count] = parity_bit;
 
      bit_count++;
 

	
 
      // AND Register 1 with feedback taps, calculate parity
 
      reg_temp = reg_1 & 0xe4613c47;
 
      parity_bit = 0;
 
      for(k = 0; k < 32; k++)
 
      {
 
        parity_bit = parity_bit ^ (reg_temp & 0x01);
 
        reg_temp = reg_temp >> 1;
 
      }
 
      s[bit_count] = parity_bit;
 
      bit_count++;
 
      if(bit_count >= bit_size)
 
      {
 
        break;
 
      }
 
    }
 
  }
 
}
 

	
 
void rs_encode(uint8_t * data, uint8_t * symbols)
 
{
 
  // Adapted from wrapkarn.c in the WSJT-X source code
 
  unsigned int dat1[12];
 
  unsigned int b[51];
 
  unsigned int i;
 

	
 
  // Reverse data order for the Karn codec.
 
  for(i = 0; i < 12; i++)
 
  {
 
    dat1[i] = data[11 - i];
 
  }
 

	
 
  // Compute the parity symbols
 
  encode_rs_int(rs_inst, dat1, b);
 

	
 
  // Move parity symbols and data into symbols array, in reverse order.
 
  for (i = 0; i < 51; i++)
 
  {
 
    symbols[50 - i] = b[i];
 
  }
 

	
 
  for (i = 0; i < 12; i++)
 
  {
 
    symbols[i + 51] = dat1[11 - i];
 
  }
 
}
src/main.c
Show inline comments
 
//
 
// WSPRHAB: Minimal high-altitude balloon tracker with WSPR telemetry
 
//
 
 
#include "stm32f0xx_hal.h"
 
#include "si5351.h"
 
#include "jtencode.h"
 
#include "adc.h"
 
#include "i2c.h"
 
#include "usart.h"
 
#include "gpio.h"
 
#include "gps.h"
 
 
#define WSPR_DEFAULT_FREQ 10140100UL
 
#define WSPR_TONE_SPACING 146 // ~1.46 Hz
 
#define WSPR_CTC 10672 // CTC value for WSPR
 
 
// Private functions
 
void sysclk_init(void);
 
void enter_sleep(void);
 
void enter_deepsleep(void);
 
 
// Test stuff
 
char call[7] = "KD8TDF";
 
char loc[5] = "EN72";
 
uint8_t dbm = 10;
 
uint8_t tx_buffer[255];
 
 
// Frequencies and channel info
 
uint32_t freq = WSPR_DEFAULT_FREQ;
 
uint8_t symbol_count = WSPR_SYMBOL_COUNT;
 
uint16_t ctc = WSPR_CTC;
 
uint16_t tone_spacing = WSPR_TONE_SPACING;
 
volatile uint8_t proceed = 0;
 
 
// Bring up TCXO and oscillator IC
 
void encode_wspr(void)
 
{
 
    HAL_GPIO_WritePin(OSC_NOTEN, 0);
 
    HAL_GPIO_WritePin(TCXO_EN, 1);
 
    HAL_Delay(100);
 
 
    // Bring up the chip
 
    si5351_init(i2c_get(), SI5351_CRYSTAL_LOAD_8PF, 0);
 
    si5351_set_correction(0);
 
    //si5351_set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
 
    //si5351_set_ms_source(SI5351_CLK0, SI5351_PLLA);
 
    si5351_set_freq(WSPR_DEFAULT_FREQ * 100, 0, SI5351_CLK0);
 
    si5351_drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA); // Set for max power if desired (8ma max)
 
    si5351_output_enable(SI5351_CLK0, 1);
 
    //si5351_pll_reset(SI5351_PLLA);
 
 
    // Make sure the other outputs of the SI5351 are disabled
 
    si5351_output_enable(SI5351_CLK1, 0); // Disable the clock initially
 
    si5351_output_enable(SI5351_CLK2, 0); // Disable the clock initially
 
 
    // disable clock powers
 
    si5351_set_clock_pwr(SI5351_CLK1, 0);
 
    si5351_set_clock_pwr(SI5351_CLK2, 0);
 
 
 
    // Encode message to transmit
 
    wspr_encode(call, loc, dbm, tx_buffer);
 
 
    // Key transmitter
 
    si5351_output_enable(SI5351_CLK0, 1);
 
 
    // Loop through and transmit symbols TODO: Do this from an ISR or ISR-triggered main loop function call (optimal)
 
    uint8_t i;
 
    for(i=0; i<symbol_count; i++)
 
    {
 
        uint32_t freq2 = (freq * 100) + (tx_buffer[i] * tone_spacing);
 
        si5351_set_freq(freq2, 0, SI5351_CLK0);
 
        HAL_GPIO_TogglePin(LED_BLUE);
 
 
        proceed = 0;
 
        while(!proceed);
 
    }
 
 
    // Disable transmitter
 
    si5351_output_enable(SI5351_CLK0, 0);
 
 
    HAL_GPIO_WritePin(OSC_NOTEN, 1);
 
    HAL_GPIO_WritePin(TCXO_EN, 0);
 
}
 
 
 
TIM_HandleTypeDef htim1;
 
 
int main(void)
 
{
 
    HAL_Init();
 
 
    sysclk_init();
 
    gpio_init();
 
    adc_init();
 
    i2c_init();
 
    gps_init();
 
 
 
//    jtencode_init();
 
    //jtencode_init();
 
 
    HAL_Delay(300);
 
 
    // Turn GPS on
 
    HAL_GPIO_WritePin(GPS_NOTEN, 0);
 
 
    // Disable ICs
 
    HAL_GPIO_WritePin(OSC_NOTEN, 1);
 
    HAL_GPIO_WritePin(TCXO_EN, 0);
 
 
    HAL_GPIO_TogglePin(LED_BLUE);
 
 
    // Start timer for WSPR
 
    __TIM1_CLK_ENABLE();
 
    htim1.Instance = TIM1;
 
    htim1.Init.Prescaler = 512; // gives 64uS ticks from 8MHz ahbclk
 
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
 
    htim1.Init.Period = ctc; // Count up to this value (how many 64uS ticks per symbol)
 
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 
    htim1.Init.RepetitionCounter = 0;
 
    HAL_TIM_Base_Init(&htim1);
 
    HAL_TIM_Base_Start_IT(&htim1);
 
    HAL_NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0, 0);
 
    HAL_NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);
 
 
 
    uint32_t led_timer = HAL_GetTick();
 
    uint32_t last_gps  = HAL_GetTick();
 
    uint32_t last_wspr  = 0xfffff; // start immediately.
 
    uint32_t last_wspr  = HAL_GetTick(); //0xfffff; // start immediately.
 
 
    HAL_GPIO_TogglePin(LED_BLUE);
 
    HAL_Delay(100);
 
    HAL_GPIO_TogglePin(LED_BLUE);
 
    HAL_Delay(100);
 
    HAL_GPIO_TogglePin(LED_BLUE);
 
    HAL_Delay(100);
 
    HAL_GPIO_TogglePin(LED_BLUE);
 
    HAL_Delay(100);
 
 
 
    while (1)
 
    {
 
        if(HAL_GetTick() - last_wspr > 120000)
 
        {
 
            //encode_wspr();
 
            encode_wspr();
 
            last_wspr = HAL_GetTick();
 
        }
 
 
        if(HAL_GetTick() - led_timer > 100)
 
        {
 
            HAL_GPIO_TogglePin(LED_BLUE);
 
            led_timer = HAL_GetTick();
 
        }
 
        if(HAL_GetTick() - last_gps > 10)
 
        {
 
            parse_gps_transmission();
 
            last_gps = HAL_GetTick();
 
        }
 
 
        //enter_sleep();
 
    }
 
}
 
 
 
void enter_sleep(void)
 
{
 
    //HAL_SuspendTick();
 
    HAL_TIM_Base_Stop_IT(&htim1);
 
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
 
    HAL_TIM_Base_Start_IT(&htim1);
 
    //HAL_ResumeTick();
 
}
 
 
 
void enter_deepsleep(void) 
 
{
 
    // Request to enter STOP mode with regulator in low power mode
 
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
 
    // After wake-up from STOP reconfigure the PLL
 
    sysclk_init();
 
}
 
 
 
// Initialize system clocks
 
void sysclk_init(void)
 
{
 
    RCC_OscInitTypeDef RCC_OscInitStruct;
 
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    RCC_PeriphCLKInitTypeDef PeriphClkInit;
 
 
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
 
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
 
    RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
 
    RCC_OscInitStruct.HSICalibrationValue = 16;
 
    RCC_OscInitStruct.HSI14CalibrationValue = 16;
 
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
 
    HAL_RCC_OscConfig(&RCC_OscInitStruct);
 
 
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
 
                                |RCC_CLOCKTYPE_PCLK1;
 
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
 
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
 
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
 
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
 
 
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1;
 
    PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_SYSCLK; //RCC_USART1CLKSOURCE_PCLK1;
 
    PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_SYSCLK;
 
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
 
 
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
 
 
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 
 
    __SYSCFG_CLK_ENABLE();
 
    // SysTick_IRQn interrupt configuration 
 
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
 
}
 
0 comments (0 inline, 0 general)