初始化提交

This commit is contained in:
王立帮
2024-07-20 22:09:06 +08:00
commit c247dd07a6
6876 changed files with 2743096 additions and 0 deletions

View File

@@ -0,0 +1,279 @@
/*
Timer2ServoPwm.cpp
Interrupt driven Servo library for Arduino using 8 bit timer2. V1.0.0
Copyright (c) 2020, Straka.
All rights reserved.
MIT License.
The servo driver function is compatible with official servo library.
The main point of this library is to use timer2 as servo driver timer
interrupt source when timer1 has been used by other libraries.
It provide a more accurate servo drive pulse control, and can control 7
servos at a time. Besides, this library provides a not very accurate pwm
function on any pin about 32Hz and 4 max pwm pins at a time.
*/
#include "Timer2ServoPwm.h"
#include <avr/interrupt.h>
//#define __DEBUG
// compensation ticks to trim adjust for digitalWrite delays
#define TRIM_PULSE_TICK 3
// trim to prevent OCRA interruption miss
#define TRIM_TICKS 32
// cycles revise for period of 20ms
#define PERIOD_REVISE_CYCLES 9
// trim to prevent OCRA interruption miss for period revise
#define PERIOD_REVISE_TRIM 16
// ticks revise for period of 20ms
#define PERIOD_REVISE_TICKS 10
// trim to not miss the first servo interruption of the next period
#define TCNT2_TRIM 5
typedef struct{
uint8_t pin=0;
volatile uint8_t cycles=0;
volatile uint8_t startTicks=0;
volatile uint8_t endTicks=0;
bool activated=false;
}servo_t;
typedef struct{
uint8_t pin=0;
volatile uint8_t start=0;
volatile uint8_t end=0;
}pwm_t;
static bool inited = false;
static servo_t servos[MAX_SERVOS + 1];
static uint8_t servoCount=0;
static volatile uint8_t COMPACtn;
static volatile uint8_t curChan;
static pwm_t pwms[MAX_PWM];
static uint8_t pwmCount=0;
static uint8_t curPwm=0;
#ifdef __DEBUG
static uint32_t ovf_times;
static uint32_t compa_times;
#endif
static void initISR(){
servos[MAX_SERVOS].activated = false;
servos[MAX_SERVOS].cycles = PERIOD_REVISE_CYCLES;
servos[MAX_SERVOS].startTicks = PERIOD_REVISE_TRIM;
servos[MAX_SERVOS].endTicks = PERIOD_REVISE_TICKS+PERIOD_REVISE_TRIM;
COMPACtn = 0;
curChan = 0;
#ifdef __DEBUG
ovf_times = 0;
compa_times = 0;
Serial.begin(9600);
#endif
TIMSK2 = 0; // disable interrupts
TCCR2A = _BV(WGM21) | _BV(WGM20); // fast PWM mode, top 0xFF
TCCR2B = _BV(CS21); // prescaler 8
TCNT2 = 0;
TIFR2 = _BV(TOV2) | _BV(OCF2A);
#ifdef PWM_ENABLE
TIMSK2 = _BV(TOIE2) | _BV(OCIE2A);
#else
TIMSK2 = _BV(OCIE2A);
#endif // !PWM_ENABLE
inited = true;
}
// Handle overflow interrupt to provide pwm
ISR(TIMER2_OVF_vect)
{
#ifdef __DEBUG
++ovf_times;
#endif
curPwm++;
for(uint8_t i=0;i<pwmCount;i++){
if(curPwm == pwms[i].start){
digitalWrite(pwms[i].pin, HIGH);
}else if(curPwm == pwms[i].end){
digitalWrite(pwms[i].pin, LOW);
}
}
}
// Handle compare A register to provider servo driver
ISR(TIMER2_COMPA_vect){
#ifdef __DEBUG
++compa_times;
#endif
++COMPACtn;
if(COMPACtn == 1){
OCR2A = servos[curChan].endTicks;
if(servos[curChan].activated){
digitalWrite( servos[curChan].pin, HIGH);
}
}else if(curChan >= MAX_SERVOS && COMPACtn > PERIOD_REVISE_CYCLES){
// also trim to adjust period, not too close to 255 encase miss the next
// interruption. TCNT2_TRIM + PERIOD_REVISE_TICKS is the actual revise.
TCNT2 = 255 - TCNT2_TRIM;
COMPACtn = 0;
curChan = 0;
OCR2A = servos[0].startTicks;
}
if(curChan < MAX_SERVOS && COMPACtn > servos[curChan].cycles){
// a bit larger than 0 to ensure not miss the next interruption
OCR2A = TRIM_TICKS;
if(servos[curChan].activated){
digitalWrite(servos[curChan].pin, LOW);
}
}
if(curChan < MAX_SERVOS && COMPACtn > CYCLES_PER_SERVO){
++curChan;
OCR2A = servos[curChan].startTicks;
COMPACtn = 0;
}
}
Timer2Servo::Timer2Servo(){
if(servoCount>=MAX_SERVOS){
servoChan_=INVALID_SERVO;
return;
}
servoChan_=servoCount++;
}
uint8_t Timer2Servo::attach(uint8_t pin){
if(!inited){
initISR();
}
return attach(pin, MIN_PULSE_WIDTH, 2500);
}
uint8_t Timer2Servo::attach(uint8_t pin, uint16_t min, uint16_t max){
if(this->servoChan_ < MAX_SERVOS){
pinMode(pin, OUTPUT);
servos[this->servoChan_].pin = pin;
servos[this->servoChan_].activated = true;
min_ = min;
max_ = max;
}
return this->servoChan_;
}
void Timer2Servo::detach(){
servos[this->servoChan_].activated = false;
}
void Timer2Servo::write(uint16_t value){
if(value < MIN_PULSE_WIDTH){
if(value > 180) value = 180;
value = map(value, 0, 180, min_, max_);
}
this->writeMicroseconds(value);
}
void Timer2Servo::writeMicroseconds(uint16_t value){
if(servoChan_ >= MAX_SERVOS){
return;
}
if(value < MIN_PULSE_WIDTH){
value = MIN_PULSE_WIDTH;
}
if(value > MAX_PULSE_WIDTH){
value = MAX_PULSE_WIDTH;
}
value = value * TICKS_PER_MICROSECOND - TRIM_PULSE_TICK;
// TODO: call cli() sei() to ensure... // Reverse these codes for future use.
// cli();
servos[servoChan_].cycles = value / TICKS_PER_CYCLE;
uint8_t ticks = value % TICKS_PER_CYCLE;
if(ticks>=256-2*TRIM_TICKS){
servos[servoChan_].cycles++;
servos[servoChan_].startTicks=3*TRIM_TICKS;
servos[servoChan_].endTicks=ticks+3*TRIM_TICKS;
}else{
servos[servoChan_].startTicks=TRIM_TICKS;
servos[servoChan_].endTicks=ticks+TRIM_TICKS;
}
// sei();
}
uint8_t Timer2Servo::read(){
return map(readMicroseconds(), min_, max_, 0, 180);
}
uint16_t Timer2Servo::readMicroseconds(){
if(servoChan_ >= MAX_SERVOS){
return 0;
}
return (servos[servoChan_].cycles * TICKS_PER_CYCLE +
servos[servoChan_].endTicks-servos[servoChan_].startTicks) /
TICKS_PER_MICROSECOND;
}
bool Timer2Servo::attached(){
return servos[servoChan_].activated;
}
void Timer2Servo::debug(){
#ifdef __DEBUG
for(uint8_t i=0;i<servoCount;i++){
Serial.print("pin:");Serial.println(servos[i].pin);
Serial.print("cycles:");Serial.println(servos[i].cycles);
Serial.print("start_ticks:");Serial.println(servos[i].startTicks);
Serial.print("end_ticks:");Serial.println(servos[i].endTicks);
Serial.print("activated:");Serial.println(servos[i].activated);
}
Serial.print("ovf_times:"); Serial.println(ovf_times);
#endif
}
#ifdef PWM_ENABLE
Timer2Pwm::Timer2Pwm(){
if(pwmCount>=MAX_PWM){
pwmChan_=INVALID_PWM;
return;
}
pwmChan_=pwmCount++;
}
uint8_t Timer2Pwm::attach(uint8_t pin){
if(!inited){
initISR();
}
if(pwmChan_ < MAX_PWM){
pinMode(pin, OUTPUT);
pwms[pwmChan_].pin = pin;
}
return pwmChan_;
}
void Timer2Pwm::write(uint8_t pwm){
// uint8_t oldSREG = SREG; // Reverse these codes for future use.
// cli();
pwms[pwmChan_].start = pwmChan_;
pwms[pwmChan_].end = pwm + pwmChan_;
// SREG = oldSREG;
}
uint8_t Timer2Pwm::read(){
return pwms[pwmChan_].end-pwms[pwmChan_].start;
}
void Timer2Pwm::debug(){
#ifdef __DEBUG
for(uint8_t i=0;i<pwmCount;i++){
Serial.print("pin:");Serial.println(pwms[i].pin);
Serial.print("start:");Serial.println(pwms[i].start);
Serial.print("end:");Serial.println(pwms[i].end);
}
Serial.print("compa_times:"); Serial.println(compa_times);
#endif
}
#endif // !PWM_ENABLE

View File

@@ -0,0 +1,90 @@
/*
Timer2ServoPwm.h
Interrupt driven Servo library for Arduino using 8 bit timer2. V1.0.0
Copyright (c) 2020, Straka.
All rights reserved.
MIT License.
The servo driver function is compatible with official servo library.
The main point of this library is to use timer2 as servo driver timer
interrupt source when timer1 has been used by other libraries.
It provide a more accurate servo drive pulse control, and can control 7
servos at a time. Besides, this library provides a not very accurate pwm
function on any pin and 4 max pwm pins at a time.
*/
#ifndef __TIMER2_SERVO_PWM_H__
#define __TIMER2_SERVO_PWM_H__
#include <Arduino.h>
#include <inttypes.h>
#define PWM_ENABLE
#define MIN_PULSE_WIDTH 500
#define MAX_PULSE_WIDTH 2500 // unit:us
#define PERIOD 20 // unit: ms
#define PRESCALE_FACTOR 8
#define CRYSTAL_FREQ 16000000 // 16M
#define TICKS_PER_CYCLE 256
#define MAX_SERVOS 7 // 20ms / 2.5ms = 8
#define INVALID_SERVO 255
// (CRYSTAL_FREQ / 1000 / TICKS_PER_CYCLE * PERIOD / PRESCALE_FACTOR )
#define CYCLES_PER_PERIOD 156 // 16M: 156.25
// (CYCLES_PER_PERIOD / MAX_SERVOS)
#define CYCLES_PER_SERVO 20
// (1000 * 1000 / CRYSTAL_FREQ * PRESCALE_FACTOR * TICKS_PER_CYCLE)
#define TIME_PER_CYCLE 128 // unit: us 16
// (CRYSTAL_FREQ / 1000 / 1000 / PRESCALE_FACTOR)
#define TICKS_PER_MICROSECOND 2
#ifdef PWM_ENABLE
#define MAX_PWM 4
#define INVALID_PWM 255
#endif // !PWM_ENABLE
class Timer2Servo{
public:
Timer2Servo();
// Attach the pin to the free servo channel, sets pinMode,
// it will return channel number or INVALID_SERVO if failure.
// Caller should ensure the pin is legal.
uint8_t attach(uint8_t pin);
// Similar as attach(int pin), but also sets min and max pluse width in
// microseconds.
uint8_t attach(uint8_t pin, uint16_t min, uint16_t max);
void detach();
// if value is < 200 its treated as an angle, otherwise as pulse width in
// microseconds
void write(uint16_t value);
// Write pulse width in microseconds
void writeMicroseconds(uint16_t value);
// returns current pulse width as an angle between 0 and 180 degrees
uint8_t read();
// returns current pulse width in microseconds for this servo (was read_us()
// in first release)
uint16_t readMicroseconds();
bool attached();
void debug();
private:
uint8_t servoChan_;
uint16_t min_;
uint16_t max_;
};
#ifdef PWM_ENABLE
class Timer2Pwm{
public:
Timer2Pwm();
uint8_t attach(uint8_t pin);
void write(uint8_t pwm);
uint8_t read();
void debug();
private:
uint8_t pwmChan_;
};
#endif // !PWM_ENABLE
#endif // !__TIMER2_SERVO_PWM_H__