parent
161b2ce88f
commit
5f9aaa0c1b
@ -0,0 +1,169 @@ |
|||||||
|
#include <avr/io.h> |
||||||
|
#include <util/delay.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
#include "pins.h" |
||||||
|
#include "sonar.h" |
||||||
|
|
||||||
|
// Currently measured sonar
|
||||||
|
volatile sonar_t *_sonar_active_so; |
||||||
|
|
||||||
|
// Flag that measurement is in progress
|
||||||
|
volatile bool _busy; |
||||||
|
|
||||||
|
// Result of last measurement, in millimeters
|
||||||
|
volatile int16_t _result = 0xFFFF; |
||||||
|
|
||||||
|
|
||||||
|
void _sonar_init_do(sonar_t* so, PORT_P port, uint8_t ntx, PORT_P pin, uint8_t nrx) |
||||||
|
{ |
||||||
|
so->port = port; |
||||||
|
so->ntx = ntx; |
||||||
|
so->pin = pin; |
||||||
|
so->nrx = nrx; |
||||||
|
|
||||||
|
switch((const uint16_t) pin) { |
||||||
|
case (const uint16_t)&PINB: |
||||||
|
so->bank = 0; |
||||||
|
break; |
||||||
|
case (const uint16_t)&PINC: |
||||||
|
so->bank = 1; |
||||||
|
break; |
||||||
|
case (const uint16_t)&PIND: |
||||||
|
so->bank = 2; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start sonar measurement |
||||||
|
* Interrupts must be enabled |
||||||
|
* TIMER 1 will be used for the async measurement |
||||||
|
* Timer 1 overflow and Pin Change interrupts must invoke Sonar handlers. |
||||||
|
*/ |
||||||
|
bool sonar_start(sonar_t* so) |
||||||
|
{ |
||||||
|
if (_busy) return false; |
||||||
|
|
||||||
|
_sonar_active_so = so; |
||||||
|
|
||||||
|
_busy = true; |
||||||
|
|
||||||
|
// make sure the timer is stopped (set clock to NONE)
|
||||||
|
TCCR1B = 0; |
||||||
|
|
||||||
|
// Timer overflow interrupt enable
|
||||||
|
// We'll stop measuring on overflow
|
||||||
|
TIMSK1 |= (1 << TOIE1); |
||||||
|
|
||||||
|
// Clear the timer value
|
||||||
|
TCNT1 = 0; |
||||||
|
|
||||||
|
// Set up pin change interrupt mask for the RX pin
|
||||||
|
switch(so->bank) { |
||||||
|
case 0: |
||||||
|
PCMSK0 |= (1 << (so->nrx)); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
PCMSK1 |= (1 << (so->nrx)); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
PCMSK2 |= (1 << (so->nrx)); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// send positive pulse
|
||||||
|
*(so->port) |= (1 << so->ntx); |
||||||
|
_delay_us(_SNR_TRIG_TIME); |
||||||
|
*(so->port) &= ~(1 << so->ntx); |
||||||
|
|
||||||
|
// Wait for start of response
|
||||||
|
while ( (*(so->pin) & (1 << so->nrx)) == 0 ); |
||||||
|
|
||||||
|
// Set timer clock source: F_CPU / 8 (0.5 us resolution)
|
||||||
|
TCCR1B = (0b010 << CS10); |
||||||
|
|
||||||
|
// Enable pin change interrupt
|
||||||
|
PCICR |= (1 << (so->bank)); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Stop the timer */ |
||||||
|
void _sonar_stop() |
||||||
|
{ |
||||||
|
// stop timer
|
||||||
|
TCCR1B = 0; |
||||||
|
|
||||||
|
// Disable RX pin interrupt mask
|
||||||
|
switch(_sonar_active_so->bank) { |
||||||
|
case 0: |
||||||
|
PCMSK0 &= ~(1 << (_sonar_active_so->nrx)); |
||||||
|
break; |
||||||
|
case 1: |
||||||
|
PCMSK1 &= ~(1 << (_sonar_active_so->nrx)); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
PCMSK2 &= ~(1 << (_sonar_active_so->nrx)); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Disable timer1 overflow interrupt
|
||||||
|
TIMSK1 &= ~(1 << TOIE1); |
||||||
|
|
||||||
|
_busy = false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Handle TIMER1_OVF (returns true if consumed) */ |
||||||
|
bool sonar_handle_t1ovf() |
||||||
|
{ |
||||||
|
if (!_busy) return false; // nothing
|
||||||
|
|
||||||
|
_result = -1; |
||||||
|
_sonar_stop(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Handle pin change interrupt (returns true if consumed) */ |
||||||
|
bool sonar_handle_pci() |
||||||
|
{ |
||||||
|
if (!_busy) { |
||||||
|
return false; // nothing
|
||||||
|
} |
||||||
|
|
||||||
|
if (*(_sonar_active_so->pin) & (1 << _sonar_active_so->nrx)) { |
||||||
|
// rx is high, not our pin change event
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t x = TCNT1; |
||||||
|
x /= _SNR_DIV_CONST; |
||||||
|
x *= 100000000L; |
||||||
|
x /= F_CPU; |
||||||
|
_result = (int16_t) x; |
||||||
|
|
||||||
|
// no obstacle
|
||||||
|
if (_result > _SNR_MAX_DIST) _result = -1; |
||||||
|
|
||||||
|
_sonar_stop(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool sonar_busy() |
||||||
|
{ |
||||||
|
return _busy; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int16_t sonar_result() |
||||||
|
{ |
||||||
|
return _result; |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
//
|
||||||
|
// Utilities for working with the HC-SR04 ultrasonic sensor
|
||||||
|
// Can be easily modified to work with other similar modules
|
||||||
|
//
|
||||||
|
// It's required that you call the sonar_handle_* functions from your ISRs
|
||||||
|
// See example program for more info.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
#include "lib/pins.h" |
||||||
|
|
||||||
|
// Calib constant for the module
|
||||||
|
// CM = uS / _DIV_CONST
|
||||||
|
#define _SNR_DIV_CONST 58 |
||||||
|
|
||||||
|
// Max module distance in MM
|
||||||
|
#define _SNR_MAX_DIST 4000 |
||||||
|
|
||||||
|
// Trigger time in uS
|
||||||
|
#define _SNR_TRIG_TIME 10 |
||||||
|
|
||||||
|
|
||||||
|
// Sonar data object
|
||||||
|
typedef struct { |
||||||
|
PORT_P port; // Tx PORT
|
||||||
|
uint8_t ntx; // Tx bit number
|
||||||
|
PORT_P pin; // Rx PIN
|
||||||
|
uint8_t nrx; // Rx bit number
|
||||||
|
uint8_t bank; // Rx PCINT bank
|
||||||
|
} sonar_t; |
||||||
|
|
||||||
|
|
||||||
|
// Create a Sonar port
|
||||||
|
// Args: sonar_t* so, Trig pin, Echo pin
|
||||||
|
#define sonar_init(so, trig, echo) do { \ |
||||||
|
as_output(io_pack(trig)); \
|
||||||
|
as_input_pu(io_pack(echo)); \
|
||||||
|
_sonar_init_do(so, &io2port(io_pack(trig)), io2n(io_pack(trig)), &io2pin(io_pack(echo)), io2n(io_pack(echo))); \
|
||||||
|
} while(0) |
||||||
|
|
||||||
|
// private, in header because of the macro.
|
||||||
|
void _sonar_init_do(sonar_t* so, PORT_P port, uint8_t ntx, PORT_P pin, uint8_t nrx); |
||||||
|
|
||||||
|
|
||||||
|
/** Check if sonar is busy */ |
||||||
|
bool sonar_busy(); |
||||||
|
|
||||||
|
|
||||||
|
/** Get result of last measurement, in millimeters. Returns -1 if no obstacle detected */ |
||||||
|
int16_t sonar_result(); |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start sonar measurement |
||||||
|
* Interrupts must be enabled |
||||||
|
* TIMER 1 will be used for the async measurement |
||||||
|
*/ |
||||||
|
bool sonar_start(sonar_t* so); |
||||||
|
|
||||||
|
|
||||||
|
/** Handle TIMER1_OVF (returns true if consumed) */ |
||||||
|
bool sonar_handle_t1ovf(); |
||||||
|
|
||||||
|
|
||||||
|
/** Handle pin change interrupt (returns true if consumed) */ |
||||||
|
bool sonar_handle_pci(); |
Loading…
Reference in new issue