Code Snippets Submitted by texane
bitbang (software) SPI implementation
#include <stdint.h>
#include <avr/io.h>
/* default pins */
#define SOFTSPI_CLK_DDR DDRD
#define SOFTSPI_CLK_PORT PORTD
#define SOFTSPI_CLK_MASK (1 << 3)
#define SOFTSPI_MOSI_DDR DDRD
#define SOFTSPI_MOSI_PORT PORTD
#define SOFTSPI_MOSI_MASK (1 << 4)
#ifndef SOFTSPI_DONT_USE_MISO
#define SOFTSPI_DONT_USE_MISO 0
#endif
#if (SOFTSPI_DONT_USE_MISO == 0)
#define SOFTSPI_MISO_DDR DDRD
#define SOFTSPI_MISO_PIN PIND
#define SOFTSPI_MISO_MASK (1 << 5)
#endif
static void softspi_setup_master(void)
{
SOFTSPI_CLK_DDR |= SOFTSPI_CLK_MASK;
SOFTSPI_MOSI_DDR |= SOFTSPI_MOSI_MASK;
#if (SOFTSPI_DONT_USE_MISO == 0)
SOFTSPI_MISO_DDR |= SOFTSPI_MISO_MASK;
#endif
}
static inline void softspi_clk_low(void)
{
SOFTSPI_CLK_PORT &= ~SOFTSPI_CLK_MASK;
}
static inline void softspi_clk_high(void)
{
SOFTSPI_CLK_PORT |= SOFTSPI_CLK_MASK;
}
static inline void softspi_mosi_low(void)
{
SOFTSPI_MOSI_PORT &= ~SOFTSPI_MOSI_MASK;
}
static inline void softspi_mosi_high(void)
{
SOFTSPI_MOSI_PORT |= SOFTSPI_MOSI_MASK;
}
static inline void softspi_write_bit(uint8_t x, uint8_t m)
{
/* dac7554 samples at clock falling edge */
/* 5 insns per bit */
softspi_clk_high();
if (x & m) softspi_mosi_high(); else softspi_mosi_low();
softspi_clk_low();
}
static void softspi_write_uint8(uint8_t x)
{
/* transmit msb first, sample at clock falling edge */
softspi_write_bit(x, (1 << 7));
softspi_write_bit(x, (1 << 6));
softspi_write_bit(x, (1 << 5));
softspi_write_bit(x, (1 << 4));
softspi_write_bit(x, (1 << 3));
softspi_write_bit(x, (1 << 2));
softspi_write_bit(x, (1 << 1));
softspi_write_bit(x, (1 << 0));
}
static inline void softspi_write_uint16(uint16_t x)
{
softspi_write_uint8((uint8_t)(x >> 8));
softspi_write_uint8((uint8_t)(x & 0xff));
}
#if (SOFTSPI_DONT_USE_MISO == 0)
static inline void softspi_read_bit(uint8_t* x, uint8_t i)
{
/* read at falling edge */
softspi_clk_high();
#if 0
/* no need, atmega328p clock below 50mhz */
/* softspi_wait_clk(); */
#endif
softspi_clk_low();
if (SOFTSPI_MISO_PIN & SOFTSPI_MISO_MASK) *x |= 1 << i;
}
static uint8_t softspi_read_uint8(void)
{
/* receive msb first, sample at clock falling edge */
/* must be initialized to 0 */
uint8_t x = 0;
softspi_read_bit(&x, 7);
softspi_read_bit(&x, 6);
softspi_read_bit(&x, 5);
softspi_read_bit(&x, 4);
softspi_read_bit(&x, 3);
softspi_read_bit(&x, 2);
softspi_read_bit(&x, 1);
softspi_read_bit(&x, 0);
return x;
}
static inline uint16_t softspi_read_uint16(void)
{
/* msB ordering */
const uint8_t x = softspi_read_uint8();
return ((uint16_t)x << 8) | (uint16_t)softspi_read_uint8();
}
#endif /* SOFTSPI_DONT_USE_MISO == 0 */
high resolution frequency counter implementation
/* high resolution frequency counter implementation
*/
/* timer2 interrupt handler. timer1 is an extended
32 bits register (16 bits hard + 16 softs)
incremented once per:
1 / (fcpu / prescal) <=> prescal / fcpu
thus, it will overflow at:
2^16 * prescal / fcpu
on tim2 overflow, the interrupt handler is called
and stores the tim1 current value in tim1_cur_counter.
thus, the tim1 value integrated over the whole
tim1 period is:
(tim1_ovf_counter * 2^16) + tim1_cur_counter.
tim2_is_ovf is set to notify the application.
*/
static volatile uint8_t tim2_ovf_counter;
static volatile uint8_t tim2_is_ovf;
static volatile uint16_t tim1_cur_counter;
ISR(TIMER2_OVF_vect)
{
if ((tim2_ovf_counter--) == 0)
{
/* disable tim1 before reading */
TCCR1B = 0;
tim1_cur_counter = TCNT1;
/* disable tim2 */
TCCR2B = 0;
tim2_is_ovf = 1;
}
}
/* timer2 interrupt handler. timer2 is a 8 bits counter
incremented by the input signal rising edges. since
8 bits are not enough to integrate, an auxiliary
register (tim2_ovf_counter) is updated on overflow.
tim2_ovf_counter is an 8 bits register, and will
overflow without any notice past 0xff.
*/
static volatile uint8_t tim1_ovf_counter;
ISR(TIMER1_OVF_vect)
{
++tim1_ovf_counter;
}
static void hfc_start(void)
{
/* resolution: 1.907349 hz per tick */
/* fmax: 500 khz */
/* acquisition time: 0.524288 seconds */
/* disable interrupts */
TIMSK1 = 0;
TIMSK2 = 0;
/* reset stuff */
tim1_ovf_counter = 0;
tim1_cur_counter = 0;
tim2_is_ovf = 0;
/* 0x100 overflows make 16 bits */
tim2_ovf_counter = 0xff;
/* configure tim2
normal operation
prescaler 128
enable interrupt on overflow
*/
TCNT2 = 0;
TIMSK2 = 1 << 0;
TCCR2A = 0;
TCCR2B = 0;
/* configure tim1
t1 pin (pd5) rising edge as external clock
*/
DDRD &= ~(1 << 5);
TCNT1 = 0;
TIMSK1 = 1 << 0;
TCCR1A = 0;
TCCR1B = 0;
/* start tim1, tim2 */
TCCR1B = 7 << 0;
TCCR2B = 5 << 0;
}
static uint8_t hfc_poll(void)
{
return tim2_is_ovf;
}
static uint32_t hfc_wait(void)
{
/* busy wait for tim1 to overflow. returns the resulting
16 bits counter, to be multiplied by the frequency
resolution (refer to hfc_start) to get the actual
frequency.
*/
/* force inline, do not use hfc_poll */
while (tim2_is_ovf == 0) ;
return ((uint32_t)tim1_ovf_counter << 16) | (uint32_t)tim1_cur_counter;
}
static inline uint32_t hfc_start_wait(void)
{
hfc_start();
return hfc_wait();
}
static inline double hfc_to_hz(uint32_t counter)
{
return 1.907349 * (double)counter;
}
LCD, 4 bit data mode
/* note: lcd model MC21605A6W */
/* note: DB0:3 and RW must be grounded */
/* note: see https://github.com/texane/lcmeter for usage */
#include <stdint.h>
#include <avr/io.h>
#define LCD_POS_DB 0x02
#define LCD_PORT_DB PORTD
#define LCD_DIR_DB DDRD
#define LCD_MASK_DB (0x0f << LCD_POS_DB)
#define LCD_POS_EN 0x06
#define LCD_PORT_EN PORTD
#define LCD_DIR_EN DDRD
#define LCD_MASK_EN (0x01 << LCD_POS_EN)
#define LCD_POS_RS 0x07
#define LCD_PORT_RS PORTD
#define LCD_DIR_RS DDRD
#define LCD_MASK_RS (0x01 << LCD_POS_RS)
static inline void wait_50_ns(void)
{
__asm__ __volatile__ ("nop\n\t");
}
static inline void wait_500_ns(void)
{
/* 8 cycles at 16mhz */
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
}
static inline void wait_50_us(void)
{
/* 800 cycles at 16mhz */
uint8_t x;
for (x = 0; x < 100; ++x) wait_500_ns();
}
static inline void wait_2_ms(void)
{
wait_50_us();
wait_50_us();
wait_50_us();
wait_50_us();
}
static inline void wait_50_ms(void)
{
/* FIXME: was _delay_ms(50), but not working */
uint8_t x;
for (x = 0; x < 25; ++x) wait_2_ms();
}
static inline void lcd_pulse_en(void)
{
/* assume EN low */
LCD_PORT_EN |= LCD_MASK_EN;
wait_50_us();
LCD_PORT_EN &= ~LCD_MASK_EN;
wait_2_ms();
}
static void lcd_write_db4(uint8_t x)
{
/* configured in 4 bits mode */
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x >> 4) << LCD_POS_DB;
lcd_pulse_en();
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x & 0xf) << LCD_POS_DB;
lcd_pulse_en();
}
static void lcd_write_db8(uint8_t x)
{
/* configured in 8 bits mode */
/* only hi nibble transmitted, (0:3) grounded */
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x >> 4) << LCD_POS_DB;
lcd_pulse_en();
}
/* exported interface */
void lcd_setup(void)
{
LCD_DIR_DB |= LCD_MASK_DB;
LCD_DIR_RS |= LCD_MASK_RS;
LCD_DIR_EN |= LCD_MASK_EN;
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_RS &= ~LCD_MASK_RS;
LCD_PORT_EN &= ~LCD_MASK_EN;
/* small delay for the lcd to boot */
wait_50_ms();
/* datasheet init sequence */
#define LCD_MODE_BLINK (1 << 0)
#define LCD_MODE_CURSOR (1 << 1)
#define LCD_MODE_DISPLAY (1 << 2)
lcd_write_db8(0x30);
wait_2_ms();
wait_2_ms();
wait_500_ns();
lcd_write_db8(0x30);
wait_2_ms();
lcd_write_db4(0x32);
wait_2_ms();
lcd_write_db4(0x28);
wait_2_ms();
lcd_write_db4((1 << 3) | LCD_MODE_DISPLAY);
wait_2_ms();
lcd_write_db4(0x01);
wait_2_ms();
lcd_write_db4(0x0f);
wait_2_ms();
}
void lcd_clear(void)
{
/* clear lcd */
lcd_write_db4(0x01);
wait_2_ms();
}
void lcd_home(void)
{
/* set cursor to home */
lcd_write_db4(0x02);
wait_2_ms();
}
void lcd_set_ddram(uint8_t addr)
{
lcd_write_db4((1 << 7) | addr);
wait_2_ms();
}
void lcd_goto_xy(uint8_t x, uint8_t y)
{
/* assume 0 <= x < 8 */
/* assume 0 <= y < 2 */
/* from datasheet: */
/* first line is 0x00 to 0x27 */
/* second line is 0x40 to 0x67 */
static const uint8_t row[] = { 0x00, 0x40 };
lcd_set_ddram(row[y] | x);
}
void lcd_write(const uint8_t* s, unsigned int n)
{
wait_50_ns();
LCD_PORT_RS |= LCD_MASK_RS;
for (; n; --n, ++s)
{
lcd_write_db4(*s);
wait_2_ms();
}
LCD_PORT_RS &= ~LCD_MASK_RS;
}
LCD, 4 bit data mode
/* note: lcd model MC21605A6W */
/* note: DB0:3 and RW must be grounded */
/* note: see https://github.com/texane/lcmeter for usage */
#include <stdint.h>
#include <avr/io.h>
#define LCD_POS_DB 0x02
#define LCD_PORT_DB PORTD
#define LCD_DIR_DB DDRD
#define LCD_MASK_DB (0x0f << LCD_POS_DB)
#define LCD_POS_EN 0x06
#define LCD_PORT_EN PORTD
#define LCD_DIR_EN DDRD
#define LCD_MASK_EN (0x01 << LCD_POS_EN)
#define LCD_POS_RS 0x07
#define LCD_PORT_RS PORTD
#define LCD_DIR_RS DDRD
#define LCD_MASK_RS (0x01 << LCD_POS_RS)
static inline void wait_50_ns(void)
{
__asm__ __volatile__ ("nop\n\t");
}
static inline void wait_500_ns(void)
{
/* 8 cycles at 16mhz */
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
__asm__ __volatile__ ("nop\n\t");
}
static inline void wait_50_us(void)
{
/* 800 cycles at 16mhz */
uint8_t x;
for (x = 0; x < 100; ++x) wait_500_ns();
}
static inline void wait_2_ms(void)
{
wait_50_us();
wait_50_us();
wait_50_us();
wait_50_us();
}
static inline void wait_50_ms(void)
{
/* FIXME: was _delay_ms(50), but not working */
uint8_t x;
for (x = 0; x < 25; ++x) wait_2_ms();
}
static inline void lcd_pulse_en(void)
{
/* assume EN low */
LCD_PORT_EN |= LCD_MASK_EN;
wait_50_us();
LCD_PORT_EN &= ~LCD_MASK_EN;
wait_2_ms();
}
static void lcd_write_db4(uint8_t x)
{
/* configured in 4 bits mode */
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x >> 4) << LCD_POS_DB;
lcd_pulse_en();
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x & 0xf) << LCD_POS_DB;
lcd_pulse_en();
}
static void lcd_write_db8(uint8_t x)
{
/* configured in 8 bits mode */
/* only hi nibble transmitted, (0:3) grounded */
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_DB |= (x >> 4) << LCD_POS_DB;
lcd_pulse_en();
}
/* exported interface */
void lcd_setup(void)
{
LCD_DIR_DB |= LCD_MASK_DB;
LCD_DIR_RS |= LCD_MASK_RS;
LCD_DIR_EN |= LCD_MASK_EN;
LCD_PORT_DB &= ~LCD_MASK_DB;
LCD_PORT_RS &= ~LCD_MASK_RS;
LCD_PORT_EN &= ~LCD_MASK_EN;
/* small delay for the lcd to boot */
wait_50_ms();
/* datasheet init sequence */
#define LCD_MODE_BLINK (1 << 0)
#define LCD_MODE_CURSOR (1 << 1)
#define LCD_MODE_DISPLAY (1 << 2)
lcd_write_db8(0x30);
wait_2_ms();
wait_2_ms();
wait_500_ns();
lcd_write_db8(0x30);
wait_2_ms();
lcd_write_db4(0x32);
wait_2_ms();
lcd_write_db4(0x28);
wait_2_ms();
lcd_write_db4((1 << 3) | LCD_MODE_DISPLAY);
wait_2_ms();
lcd_write_db4(0x01);
wait_2_ms();
lcd_write_db4(0x0f);
wait_2_ms();
}
void lcd_clear(void)
{
/* clear lcd */
lcd_write_db4(0x01);
wait_2_ms();
}
void lcd_home(void)
{
/* set cursor to home */
lcd_write_db4(0x02);
wait_2_ms();
}
void lcd_set_ddram(uint8_t addr)
{
lcd_write_db4((1 << 7) | addr);
wait_2_ms();
}
void lcd_goto_xy(uint8_t x, uint8_t y)
{
/* assume 0 <= x < 8 */
/* assume 0 <= y < 2 */
/* from datasheet: */
/* first line is 0x00 to 0x27 */
/* second line is 0x40 to 0x67 */
static const uint8_t row[] = { 0x00, 0x40 };
lcd_set_ddram(row[y] | x);
}
void lcd_write(const uint8_t* s, unsigned int n)
{
wait_50_ns();
LCD_PORT_RS |= LCD_MASK_RS;
for (; n; --n, ++s)
{
lcd_write_db4(*s);
wait_2_ms();
}
LCD_PORT_RS &= ~LCD_MASK_RS;
}
bitbang (software) SPI implementation
#include <stdint.h>
#include <avr/io.h>
/* default pins */
#define SOFTSPI_CLK_DDR DDRD
#define SOFTSPI_CLK_PORT PORTD
#define SOFTSPI_CLK_MASK (1 << 3)
#define SOFTSPI_MOSI_DDR DDRD
#define SOFTSPI_MOSI_PORT PORTD
#define SOFTSPI_MOSI_MASK (1 << 4)
#ifndef SOFTSPI_DONT_USE_MISO
#define SOFTSPI_DONT_USE_MISO 0
#endif
#if (SOFTSPI_DONT_USE_MISO == 0)
#define SOFTSPI_MISO_DDR DDRD
#define SOFTSPI_MISO_PIN PIND
#define SOFTSPI_MISO_MASK (1 << 5)
#endif
static void softspi_setup_master(void)
{
SOFTSPI_CLK_DDR |= SOFTSPI_CLK_MASK;
SOFTSPI_MOSI_DDR |= SOFTSPI_MOSI_MASK;
#if (SOFTSPI_DONT_USE_MISO == 0)
SOFTSPI_MISO_DDR |= SOFTSPI_MISO_MASK;
#endif
}
static inline void softspi_clk_low(void)
{
SOFTSPI_CLK_PORT &= ~SOFTSPI_CLK_MASK;
}
static inline void softspi_clk_high(void)
{
SOFTSPI_CLK_PORT |= SOFTSPI_CLK_MASK;
}
static inline void softspi_mosi_low(void)
{
SOFTSPI_MOSI_PORT &= ~SOFTSPI_MOSI_MASK;
}
static inline void softspi_mosi_high(void)
{
SOFTSPI_MOSI_PORT |= SOFTSPI_MOSI_MASK;
}
static inline void softspi_write_bit(uint8_t x, uint8_t m)
{
/* dac7554 samples at clock falling edge */
/* 5 insns per bit */
softspi_clk_high();
if (x & m) softspi_mosi_high(); else softspi_mosi_low();
softspi_clk_low();
}
static void softspi_write_uint8(uint8_t x)
{
/* transmit msb first, sample at clock falling edge */
softspi_write_bit(x, (1 << 7));
softspi_write_bit(x, (1 << 6));
softspi_write_bit(x, (1 << 5));
softspi_write_bit(x, (1 << 4));
softspi_write_bit(x, (1 << 3));
softspi_write_bit(x, (1 << 2));
softspi_write_bit(x, (1 << 1));
softspi_write_bit(x, (1 << 0));
}
static inline void softspi_write_uint16(uint16_t x)
{
softspi_write_uint8((uint8_t)(x >> 8));
softspi_write_uint8((uint8_t)(x & 0xff));
}
#if (SOFTSPI_DONT_USE_MISO == 0)
static inline void softspi_read_bit(uint8_t* x, uint8_t i)
{
/* read at falling edge */
softspi_clk_high();
#if 0
/* no need, atmega328p clock below 50mhz */
/* softspi_wait_clk(); */
#endif
softspi_clk_low();
if (SOFTSPI_MISO_PIN & SOFTSPI_MISO_MASK) *x |= 1 << i;
}
static uint8_t softspi_read_uint8(void)
{
/* receive msb first, sample at clock falling edge */
/* must be initialized to 0 */
uint8_t x = 0;
softspi_read_bit(&x, 7);
softspi_read_bit(&x, 6);
softspi_read_bit(&x, 5);
softspi_read_bit(&x, 4);
softspi_read_bit(&x, 3);
softspi_read_bit(&x, 2);
softspi_read_bit(&x, 1);
softspi_read_bit(&x, 0);
return x;
}
static inline uint16_t softspi_read_uint16(void)
{
/* msB ordering */
const uint8_t x = softspi_read_uint8();
return ((uint16_t)x << 8) | (uint16_t)softspi_read_uint8();
}
#endif /* SOFTSPI_DONT_USE_MISO == 0 */
high resolution frequency counter implementation
/* high resolution frequency counter implementation
*/
/* timer2 interrupt handler. timer1 is an extended
32 bits register (16 bits hard + 16 softs)
incremented once per:
1 / (fcpu / prescal) <=> prescal / fcpu
thus, it will overflow at:
2^16 * prescal / fcpu
on tim2 overflow, the interrupt handler is called
and stores the tim1 current value in tim1_cur_counter.
thus, the tim1 value integrated over the whole
tim1 period is:
(tim1_ovf_counter * 2^16) + tim1_cur_counter.
tim2_is_ovf is set to notify the application.
*/
static volatile uint8_t tim2_ovf_counter;
static volatile uint8_t tim2_is_ovf;
static volatile uint16_t tim1_cur_counter;
ISR(TIMER2_OVF_vect)
{
if ((tim2_ovf_counter--) == 0)
{
/* disable tim1 before reading */
TCCR1B = 0;
tim1_cur_counter = TCNT1;
/* disable tim2 */
TCCR2B = 0;
tim2_is_ovf = 1;
}
}
/* timer2 interrupt handler. timer2 is a 8 bits counter
incremented by the input signal rising edges. since
8 bits are not enough to integrate, an auxiliary
register (tim2_ovf_counter) is updated on overflow.
tim2_ovf_counter is an 8 bits register, and will
overflow without any notice past 0xff.
*/
static volatile uint8_t tim1_ovf_counter;
ISR(TIMER1_OVF_vect)
{
++tim1_ovf_counter;
}
static void hfc_start(void)
{
/* resolution: 1.907349 hz per tick */
/* fmax: 500 khz */
/* acquisition time: 0.524288 seconds */
/* disable interrupts */
TIMSK1 = 0;
TIMSK2 = 0;
/* reset stuff */
tim1_ovf_counter = 0;
tim1_cur_counter = 0;
tim2_is_ovf = 0;
/* 0x100 overflows make 16 bits */
tim2_ovf_counter = 0xff;
/* configure tim2
normal operation
prescaler 128
enable interrupt on overflow
*/
TCNT2 = 0;
TIMSK2 = 1 << 0;
TCCR2A = 0;
TCCR2B = 0;
/* configure tim1
t1 pin (pd5) rising edge as external clock
*/
DDRD &= ~(1 << 5);
TCNT1 = 0;
TIMSK1 = 1 << 0;
TCCR1A = 0;
TCCR1B = 0;
/* start tim1, tim2 */
TCCR1B = 7 << 0;
TCCR2B = 5 << 0;
}
static uint8_t hfc_poll(void)
{
return tim2_is_ovf;
}
static uint32_t hfc_wait(void)
{
/* busy wait for tim1 to overflow. returns the resulting
16 bits counter, to be multiplied by the frequency
resolution (refer to hfc_start) to get the actual
frequency.
*/
/* force inline, do not use hfc_poll */
while (tim2_is_ovf == 0) ;
return ((uint32_t)tim1_ovf_counter << 16) | (uint32_t)tim1_cur_counter;
}
static inline uint32_t hfc_start_wait(void)
{
hfc_start();
return hfc_wait();
}
static inline double hfc_to_hz(uint32_t counter)
{
return 1.907349 * (double)counter;
}