< async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"">

Sunday, May 5, 2019

A tiny avr library for Si5351 [less than 1kb flash]

Si5351 is a popular solution for generating a custom clock signal and is very popular among the home brewers. It makes the job of building a vfo a lot simpler.

A tiny sdr prototype based on FST3253 mixer  driven by a quadrature signal from Si5351 and controlled by  Attiny13

Setting up the correct values in the devices registers are needed to output a required frequency by this device and most of the currently available libraries uses at least  a few kilobytes. After seeing an interesting post from Ram (vu3xvr),  where he uses attiny13 with limited flash size, thought of doing the same with avr-gcc and building a simple library for rapid prototyping. I borrowed the math routines for division and multiplications available among the avr-communities in assembly for
doing 32bit math on the 8bit avr. In addition parts of i2c library by Peter Fleury is used to communicate with the chip.

Hans Summers from qrplab has several examples and details on the pll settings (esp, the application note needs reading in betwen the lines). Jason (NT7S) has an excellent library which is recommended if you have enough flash size to spare as my library has some limitations at the expense of  reduced flash usage.

The wiring for Si5351 is very simple and is mentioned else where (See here)

  • simple library to use with avr / arduino to set frequency on Si5351 
  • uses a mix of assembly and c to achieve the register settings
  • takes less than 1000 bytes, so useful with attiny13
  • If you want to use Arduino platform, just install a microcore for attiny with link time optimisation (LTO)  Link
  • It use a bitbang i2c, So use PB1 (SCL) & PB2 (SDA)on attiny13 (can be changed to any pin in i2cmaster.S file in the library)
  • There is an example sketch (tinypll - takes around 960 bytes of flash), just upload it via an isp programmer (usbasp, or use an arduino uno as isp) and it generates a signal at 10MHZ and can be adjusted a few kHz upward by applying a voltage (0-vcc) on ADC3 of the attiny13
  • For testing it on an arduino uno, you can use the same test sketch and see the enclosed pinout diagram to locate PB1 (Digital pin 9) , PB2  (pin 10) and ADC3  (A3) on the uno.
  • For sdr application, to generate a quadrature output and to reduce the jitter, i used a different approach (slightly larger code size-200bytes more) where PLL frequency is changed and  an integral divider is used in the multi-synth (will update later )

  The entire code is available for download on github
  If interested only in the avr-gcc version - download

To vary the frequency in the test program, connect a potentiometer  (wiper)to the ADC input and connect the the other ends to the vcc and ground to get a variable voltage from 0-vcc


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <avr/io.h>
#include "tiny5351.h"
void InitADC()
{
 ADMUX |= (1<<REFS0);
 //set prescaller to 128 and enable ADC 
 ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);    
}
#if defined(__AVR_ATmega328P__) 

uint16_t ReadADC(uint8_t ADCchannel)
{
 //select ADC channel with safety mask
 ADMUX = (ADMUX & 0xF0) | (ADCchannel & 0x0F);
 //single conversion mode
 ADCSRA |= (1<<ADSC);
 // wait until ADC conversion is complete
 while( ADCSRA & (1<<ADSC) );
 return ADC;
}
#else
//ADC on ATTINY13
#define REF_AVCC (0<<REFS0) // reference = AVCC
// select the corresponding channel 0~N and set reference
uint16_t ReadADC(uint8_t ADCchannel) 
{
   ADMUX = REF_AVCC | ADCchannel;  // set reference and channel
   ADMUX |= _BV(ADLAR); // left adjust of ADC result
   ADCSRA |= (1<<ADSC);         // start conversion  
   while(ADCSRA & (1<<ADSC)){}  // wait for conversion complete  
   return ADC;
}
#endif

void setup() {
  // put your setup code here, to run once: 
  #if defined(__AVR_ATmega328P__)  
  //set adc input pin
  DDRC &=~(1<<PC3); //input onPC3  ie A3 on Arduino Uno with Atmega328p
  #else
  DDRB &=~(1<<PB3); // For attiny it is PB3 for adc input
  #endif
  
  uint32_t frequency=10000000UL;
  uint16_t t;
  uint16_t prv_t = 0;
  InitADC();
  sei();
}
void loop() {
  // put your main code here, to run repeatedly:
    
 t = ReadADC(3);
    if (prv_t != t) {
      si5351_freq(frequency+(t<<7), 0); //multiply adc value with 128 0-1023 becomes 0-(1023*128)
      prv_t = t;
      }
}

For avr-gcc users, there is a zip file in the package with make files
Just edit to change your programmer and the microcontroller. For e.g to test with an arduino uno,
just use the Makefile.uno (make -f  Makefile.uno & make flash)

To use with arduino ide, just download and keep the folder inside the libraries folder and there is an example sketch in the tinypll

The library has routine to set the frequency which is very minimal and uses a fixed pll frequency and available divider to get the target frequency.  To improve the phase noise /jitter ,  an alternative routine can be made where  the      divider can be kept as  a fixed integer and vary the pll frequency (multiplier)

I2C pins are controlled by bitbanging and can be changed by editing the assembly files (inside the library). By default PB1 and PB2 is used.