G4TGJ AVR Radio Library¶
This library is a set of source files for AVR processors that provide functionality of use in amateur radio applications, although many of the files could also be used in non-radio systems.
These files have been tested on the ATTiny85, ATtiny817, ATtiny3216 and ATMega328P. They should work with most tinyAVR 1-series, mega and xmega devices.
When adding these files to your project in Atmel Studio you should add them as links rather than copying them over. This way you can easily update the library when I issue
new versions. You should add as links the C files that you need so that they are compiled. You can also link to the header files but this does not bring them into the compilation.
For the compiler to find them you need to add ../../../TARL
and ..
to Project Properties/Toolchain/C Compiler/Directories
.
The source code is available at TARL. A good way to learn how to use the library is to look at three projects which use the library: Serial817, TATC and FreqGen5351. You can, of course, use these projects as the basis for your project - feel free to clone or fork. Just remember that if you distribute your program in binary form you must also make the source code available under the GPL. I would welcome pull requests with any modifications you may make.
Drivers¶
config.h¶
All source files include config.h
. It is used to customise the library for specific hardware, e.g. IO ports, I2C address, frequency ranges. It also contains the definition of the bool
type and the
values true
and false
.
/*
* config.h
*
* Configuration settings for the whole project
*
*/
#ifndef CONFIG_H_
#define CONFIG_H_
#include <avr/io.h>
// General definitions
typedef uint8_t bool;
#define true 1
#define false 0
#endif /* CONFIG_H_ */
config.h
is not part of the library but should be provided as part of your project. For each driver I have listed the definitions required with example values - these will
obviously have to match your requirements and hardware.
main.c and main.h¶
Your program requires a main.c
(although you can name it anything you like) which contains main()
which should call the init function for each driver you are using and then
it should start a continuous loop. This will continually call into those drivers that require to do regular processing (e.g. morseScanPaddles()
in the morse keyer. You obviously
have to include your own application code here too.
Some drivers include main.h
which defines the functions that the driver calls to feed back into the main program e.g. the morse keyer will call keyDown()
whenever it is
sending a dot or a dash.
io.c and io.h¶
Many of the drivers require an I/O driver e.g. to read the state of a switch. The functions should be declared in io.h
and implemented in io.c
. Again, for each driver I have
listed the required functions.
CAT¶
CAT control simulating the Yaesu FT450D. Requires the serial driver.
Requires the main application to implement a number of functions for setting VFO frequency, RIT etc.
Functions¶
-
void
catInit
()¶ Initialise CAT control.
-
void
catControl
()¶ Called from the main loop to do CAT control.
// Time between scans of the CAT interface
#define CAT_CHARACTER_DELAY 10
Required functions to provide in main.c
and main.h
:
-
void
setVFOFrequency
(uint8_t vfo, uint32_t freq)¶ Sets a VFO frequency.
- Parameters
vfo – VFO number
freq – Frequency
-
uint32_t
getVFOFreq
(uint8_t vfo)¶ Returns a VFO’s frequency.
- Parameters
vfo – VFO number
-
uint32_t
getCurrentVFOFreq
()¶ Returns the current VFO’s frequency.
-
uint32_t
getOtherVFOFreq
()¶ Returns the other VFO’s frequency. e.g. if the current VFO is A, then return B’s frequency.
-
int16_t
getCurrentVFOOffset
()¶ Returns the current VFO’s offset.
-
bool
getCurrentVFORIT
()¶ Returns the current VFO’s RIT setting.
-
bool
getCurrentVFOXIT
()¶ Returns the current VFO’s XIT setting.
-
int16_t
getOtherVFOOffset
()¶ Returns the other VFO’s offset.
-
bool
getOtherVFORIT
()¶ Returns the other VFO’s RIT setting.
-
bool
getOtherVFOXIT
()¶ Returns the other VFO’s XIT setting.
-
uint8_t
getCurrentVFO
()¶ Returns the current VFO.
-
void
setCurrentVFO
(uint8_t vfo)¶ Sets the current VFO.
- Parameters
vfo – VFO number
-
void
setCurrentVFORIT
(bool bRIT)¶ Set the current VFO in or out of RIT.
- Parameters
bRIT – true if going into RIT
-
bool
getVFOSplit
()¶ Return true if in split VFO mode.
-
void
setVFOSplit
(bool bSplit)¶ Sets split VFO mode.
- Parameters
bSplit – true if setting split
-
bool
getTransmitting
()¶ Return true if currently transmitting.
-
void
vfoSwap
()¶ Swap the two VFOs.
-
void
vfoEqual
()¶ Set the VFOs to be equal.
-
void
setCurrentVFOOffset
(int16_t rit)¶ Set the current VFO’s offset.
- Parameters
rit – RIT value to set
-
void
setCWReverse
(bool bCWReverse)¶ Sets CW reverse mode i.e. swaps sidebands.
- Parameters
bCWReverse – true if setting CW reverse
Display¶
Display driver. Requires the LCD driver.
You must specify the width and height of the display in config.h
.
If you are short of flash space then defining DISPLAY_DISABLE_SCROLLING
will save some space. Obviously this disables
scrolling and displaySplitLine()
is not available.
Functions¶
-
void
displayInit
()¶ Initialise the display module.
-
void
displayText
(uint8_t line, char *text, bool bReplace)¶ Display text on the specified line replacing the existing text or scrolling from the right.
- Parameters
[in] line
: Line number[in] text
: Pointer to the text to display[in] bReplace
: If true then replace the existing line, otherwise scroll the text in
-
void
displayCursor
(uint8_t col, uint8_t line, enum eCursorState state)¶ Set the cursor position and state (off, underline or blink).
- Parameters
[in] col
: Column number[in] line
: Line number[in] state
: cursorOff, cursorUnderline or cursorBlink
-
void
displaySplitLine
(uint8_t col, uint8_t line)¶ Split a line at the given column. This means text will scroll up to that point.
- Parameters
[in] col
: Column number[in] line
: Line number
-
enum
eCursorState
¶ Possible cursor states.
Values:
-
enumerator
cursorOff
¶
-
enumerator
cursorUnderline
¶
-
enumerator
cursorBlink
¶
-
enumerator
// The size of the LCD screen
#define LCD_WIDTH 16
#define LCD_HEIGHT 2
// Define to disable scrolling
#define DISPLAY_DISABLE_SCROLLING
EEPROM¶
EEPROM driver.
Functions¶
-
void
eepromInit
()¶ Initialise the EEPROM driver.
-
uint8_t
eepromRead
(uint16_t uiAddress)¶ Read a byte from the EEPROM.
- Return
The byte read from the address
- Parameters
[in] uiAddress
: EEPROM address
-
void
eepromWrite
(uint16_t uiAddress, uint8_t ucData)¶ Write a byte to the EEPROM.
- Parameters
[in] uiAddress
: EEPROM address[in] ucData
: Data to write
I2C¶
Implements the Philips I2C (Inter Integrated Circuit) bus. Some manufacturers call it TWI (Two Wire Interface), presumably to avoid trademark infringement.
Currently only the master side is implemented. Multi-master buses are not supported.
Files¶
-
i2c.h
Header file.
-
i2c.c
Implementation.
Pulls in the required source file for the device. Supports ATMega, tinyAVR 1-series and devices that use the Universal Serial Interface (USI) to provide I2C (such as the ATTiny85).
Functions¶
-
void
i2cInit
()¶ Initialise the I2C driver.
Must be called before any other I2C functions.
-
uint8_t
i2cWriteRegister
(uint8_t addr, uint8_t reg, uint8_t data)¶ Write to an 8 bit register over I2C.
- Parameters
[in] addr
: I2C address[in] reg
: Register address[in] data
: Data to write to register
-
uint8_t
i2cReadRegister
(uint8_t addr, uint8_t reg, uint8_t *data)¶ Read from an 8 bit register over I2C.
- Parameters
[in] addr
: I2C address[in] reg
: Register address[out] data
: Pointer to data location to write contents of register to
LCD¶
Low level LCD display driver. Normally you would use the display driver rather than the LCD driver.
Files¶
-
lcd.h
Header file.
-
lcd.c
Implementation. Requires either
lcd_i2c.c
orlcd_port.c
. Alternatively, you can uselcd_if.c
and define eitherLCD_PORT
orLCD_I2C
to target code at either type of display.
-
lcd_i2c.c
Use this with LCD displays connected over I2C.
-
lcd_port.c
Use with LCD displays directly connected to the IO ports on the AVR controller.
-
lcd_if.c
Pulls in either
lcd_i2c.c
(ifLCD_I2C
is defined) orlcd_port.c
(ifLCD_PORT
is defined).
Functions¶
-
void
lcdInit
()¶ Initialise the LCD driver.
-
void
lcdBegin
(uint8_t cols, uint8_t rows)¶ Set the number of rows and columns.
- Parameters
[in] cols
: Number of columns[in] rows
: Number of rows
-
void
lcdClear
()¶ Clear the LCD screen.
-
void
lcdHome
()¶ Move the cursor back to the first location.
-
void
lcdDisplayOff
()¶ Turn off the display.
-
void
lcdDisplayOn
()¶ Turn on the display.
-
void
lcdBlinkOff
()¶ Stop the cursor blinking.
-
void
lcdBlinkOn
()¶ Start the cursor blinking.
-
void
lcdCursorOff
()¶ Turn off the cursor.
-
void
lcdCcursorOn
()¶ Turn on the cursor.
-
void
lcdScrollDisplayLeft
()¶ Scroll the display left.
-
void
lcdScrollDisplayRight
()¶ Scroll the display right.
-
void
lcdScrollLeftToRight
()¶ Set the scroll direction to left to right.
-
void
lcdScrollRightToLeft
()¶ Set the scroll direction to right to left.
-
void
lcdAutoscrollOn
()¶ Set the display to automatically scroll.
-
void
lcdAutoscrollOff
()¶ Turn off automatic scrolling.
-
void
lcdSetCursor
(uint8_t col, uint8_t row)¶ Set the cursor position.
- Parameters
[in] col
: Column number[in] row
: Row number
-
void
lcdPrint
(const char *string)¶ Print text on the LCD screen.
- Parameters
[in] string
: Pointer to the null terminated string
-
void
lcdBacklight
(bool bOn)¶ Turn the backlight on or off
This function is only available with the I2C interface unless separately implemented
- Parameters
[in] bOn
: True to turn on backlight
// Use the I2C version of the LCD driver
#define LCD_I2C
// Address of the LCD display
#define LCD_I2C_ADDRESS 0x27
// LCD Port definitions
#define LCD_ENABLE_PORT PORTB
#define LCD_ENABLE_DDR DDRB
#define LCD_ENABLE_PIN PB3
#define LCD_RS_PORT PORTB
#define LCD_RS_DDR DDRB
#define LCD_RS_PIN PB4
#define LCD_DATA_PORT_0 PORTD
#define LCD_DATA_DDR_0 DDRD
#define LCD_DATA_PIN_0 PD7
#define LCD_DATA_PORT_1 PORTD
#define LCD_DATA_DDR_1 DDRD
#define LCD_DATA_PIN_1 PD4
#define LCD_DATA_PORT_2 PORTD
#define LCD_DATA_DDR_2 DDRD
#define LCD_DATA_PIN_2 PD3
#define LCD_DATA_PORT_3 PORTD
#define LCD_DATA_DDR_3 DDRD
#define LCD_DATA_PIN_3 PD2
Millis¶
Timer providing a count in milliseconds and delays in milliseconds or microseconds.
The AVR clock speed (F_CPU
) must be specified in config.h
as this is needed for correct timing.
For the tinyAVR 1-series the clock divider must also be specified. This is
because the TCA timer can be used as the source for other timers.
Functions¶
-
void
millisInit
(void)¶ Initialise the millisecond timer.
-
uint32_t
millis
()¶ Return the number of milliseconds since the box started. Wraps after approx 49 days.
- Return
Number of milliseconds
-
void
delay
(uint16_t ms)¶ Delay a number of milliseconds.
- Parameters
[in] ms
: Number of milliseconds to wait
-
delayMicroseconds
(us)¶ Delay a number of microseconds.
- Parameters
[in] us
: Number of microseconds to wait
// CPU clock speed
// This must match the speed set in the AVR's fuses
#define F_CPU 20000000UL
// Clock divider (for tinyAVR 1-series only)
// This is used by the millisecond clock on TCA0
#define CLOCK_DIV 1
Morse¶
Morse keyer. Supports Iambic A, Iambic B and Ultimatic.
Functions¶
-
void
morseInit
()¶ Initialise the morse module.
-
uint8_t
morseGetWpm
()¶ Get the morse speed in words per minute.
- Return
Morse speed in WPM
-
void
morseSetWpm
(uint8_t wpm)¶ Set the morse speed in words per minute.
- Parameters
[in] wpm
: Morse speed in wpm
-
bool
morseScanPaddles
(void)¶ Scan the dot and dash paddles. Called from the main loop.
The return value can be used to decide whether or not to go into a low power mode - useful for a battery powered keyer without an off switch.
- Return
true if active
- Return
false if idle
-
void
morseSetTuneMode
(bool bTune)¶ Switch in or out of tune mode. i.e. continuous transmission for tuning ATU etc.
- Parameters
[in] bTune
: True to set tune mode, False to leave tune mode
-
bool
morseInTuneMode
()¶ Returns whether or not in tune mode.
- Return
true if in tune mode
-
enum eMorseKeyerMode
morseGetKeyerMode
()¶ Get the keyer mode.
- Return
The keyer mode
-
void
morseSetKeyerMode
(enum eMorseKeyerMode)¶ Set the keyer mode.
- Parameters
[in] eMorseKeyerMode
: The keyer mode
-
enum
eMorseKeyerMode
¶ Morse keyer mode.
Values:
-
enumerator
morseKeyerIambicA
= 0¶
-
enumerator
morseKeyerIambicB
¶
-
enumerator
morseKeyerUltimatic
¶
-
enumerator
MORSE_NUM_KEYER_MODES
¶
-
enumerator
void ioInit()
{
// Initialise morse output
MORSE_OUTPUT_DDR_REG |= (1<<MORSE_OUTPUT_PIN);
// Paddle dot and dash pins as inputs with pull-ups
MORSE_PADDLE_DOT_PORT_REG |= (1<<MORSE_PADDLE_DOT_PIN);
MORSE_PADDLE_DASH_PIN_REG |= (1<<MORSE_PADDLE_DASH_PIN);
}
// Functions to read morse paddle inputs
bool ioReadDotPaddle()
{
return !(MORSE_PADDLE_DOT_PIN_REG & (1<<MORSE_PADDLE_DOT_PIN));
}
bool ioReadDashPaddle()
{
return !(MORSE_PADDLE_DASH_PIN_REG & (1<<MORSE_PADDLE_DASH_PIN));
}
Required functions to provide in main.c
and main.h
:
-
void
displayMorse
(char *text)¶ Sends the characters being sent by the keyer to the application. Can be displayed on a screen, for example.
- Parameters
text – Pointer to characters to display
-
void
keyDown
(bool bDown)¶ Sends the key state to the application so that the keyer can key the transmitter.
- Parameters
bDown – true if the key is down, false if it is up
Oscillator¶
Oscillator driver. Allows the setting of individual output frequencies and enabling quadrature mode. Currently only supports the Si5351A chip with 3 outputs. CLK0 and CLK1 use PLL A and CLK2 uses PLL B.
Functions¶
-
bool
oscInit
(void)¶ Initialise the oscillator.
- Return
true if successful
- Return
false if unable to talk to it or it doesn’t initialise properly
-
void
oscSetFrequency
(uint8_t clock, uint32_t frequency, int8_t q)¶ Set the clock to the given frequency with optional quadrature.
quadrature is only used for clock 1 - it is ignored for the others.
+ve is CLK1 leads CLK0 by 90 degrees -ve is CLK1 lags CLK0 by 90 degrees
0 is no quadrature i.e. set the frequency as normal.
When quadrature is set for clock 1 then it is set to the same frequency as clock 0.
- Parameters
[in] clock
: Clock output to set[in] frequency
: Frequency in hertz[in] q
: Quadrature mode
-
void
oscClockEnable
(uint8_t clock, bool bEnable)¶ Enable/disable a clock output.
- Parameters
[in] clock
: Clock output to control[in] bEnable
: true to enable, false to disable
-
void
oscSetXtalFrequency
(uint32_t xtal_freq)¶ Set the crystal frequency.
- Parameters
[in] xtal_freq
: Crystal frequency (in hertz)
// Oscillator chip definitions
// I2C address
#define SI5351A_I2C_ADDRESS 0x60
// Transmit and receive clocks. Receive uses 2 clocks for quadrature.
#define NUM_CLOCKS 3
#define RX_CLOCK_A 0
#define RX_CLOCK_B 1
#define TX_CLOCK 2
// The minimum and maximum crystal frequencies in the setting menu
// Have to allow for adjusting above or below actual valid crystal range
#define MIN_XTAL_FREQUENCY 24000000
#define MAX_XTAL_FREQUENCY 28000000
// The si5351a default crystal frequency and load capacitance
#define DEFAULT_XTAL_FREQ 27000000
#define SI_XTAL_LOAD_CAP SI_XTAL_LOAD_10PF
Pushbutton¶
Pushbutton driver. Debounces the button detecting short and long presses.
Functions¶
Debounce a pushbutton
- Parameters
[in] bDown
: True if the pushbutton is down as read from the hardware[out] pbShortPress
: Pointer to boolean set to true if the pushbutton has been pressed for a short time[out] pbLongPress
: Pointer to boolean set to true if the pushbutton has been pressed for a long time or null if not used[in] debounceTime
: Minimum time pushbutton must be down for it to be a short press[in] longPressTime
: Minimum time pushbutton must be down for it to be a long press[in] pDebounceState
: Pointer to structure holding state machine state
// Debounce times in ms
#define DEBOUNCE_TIME 200
#define LONG_PRESS_TIME 1000
// Keep track of pushbutton debounce state
static struct sDebounceState debounceState;
// Read and debounce the pushbutton
bool shortPress, longPress;
debouncePushbutton( readButton(), &shortPress, &longPress, DEBOUNCE_TIME, LONG_PRESS_TIME, &debounceState );
bool readButton()
{
return !(BUTTON_PIN_REG & (1<<BUTTON_PIN));
}
// Configure all the I/O we need
void ioInit()
{
BUTTON_PORT_REG |= (1<<BUTTON_PIN);
}
Rotary¶
Rotary driver. Handles a rotary control with pushbutton. Decodes the quadrature signal for rotation and debounces the button detecting short and long presses.
Requires the pushbutton driver.
Functions¶
-
void
readRotary
(bool *pbCW, bool *pbCCW, bool *pbShortPress, bool *pbLongPress)¶ Read the rotary control and its push button
- Parameters
[out] pbCW
: Pointer to boolean set to true if the control has been moved clockwise[out] pbCCW
: Pointer to boolean set to true if the control has been moved counter clockwise[out] pbShortPress
: Pointer to boolean set to true if the pushbutton has been pressed for a short time[out] pbLongPress
: Pointer to boolean set to true if the pushbutton has been pressed for a long time
// Time for debouncing the rotary pushbutton (ms)
#define ROTARY_BUTTON_DEBOUNCE_TIME 100
// Time for the rotary pushbutton press to be a long press (ms)
#define ROTARY_LONG_PRESS_TIME 250
void ioReadRotary( bool *pbA, bool *pbB, bool *pbSw )
{
*pbA = !(ROTARY_ENCODER_A_PIN_REG & (1<<ROTARY_ENCODER_A_PIN));
*pbB = !(ROTARY_ENCODER_B_PIN_REG & (1<<ROTARY_ENCODER_B_PIN));
*pbSw = !(ROTARY_ENCODER_SW_PIN_REG & (1<<ROTARY_ENCODER_SW_PIN));
}
// Configure all the I/O we need
void ioInit()
{
ROTARY_ENCODER_A_PORT_REG |= (1<<ROTARY_ENCODER_A_PIN);
ROTARY_ENCODER_B_PORT_REG |= (1<<ROTARY_ENCODER_B_PIN);
ROTARY_ENCODER_SW_PORT_REG |= (1<<ROTARY_ENCODER_SW_PIN);
}
Serial¶
Serial driver for chips, such as the ATMega328 or ATtiny817, that have a USART. Interrupt driven with transmit and receive buffers.
Functions¶
-
void
serialInit
(uint32_t baud)¶ Initialise the serial driver.
- Parameters
[in] baud
: Baud rate
-
void
serialTransmit
(uint8_t data)¶ Transmit a byte over the serial line.
- Parameters
[in] data
: The byte to send
-
void
serialTXString
(char *string)¶ Transmit a null terminated string over the serial line.
- Parameters
[in] string
: Pointer to the string to send
-
uint8_t
serialReceive
(void)¶ Receive a byte from the serial line.
If the receive buffer is empty then NUL is returned.
- Return
The byte received, or NUL.
// // Serial port buffer lengths
// Lengths should be a power of 2 for efficiency
#define SERIAL_RX_BUF_LEN 32
#define SERIAL_TX_BUF_LEN 64