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