Newer
Older
PIDMotorControl Example includes code for DAC, ADC, PWM, Timer Interrupts,
PID, Current Control, Encoders w/ interrupts, pins, clock frequency setup, and low pass filters
DAC()<< example in this code is meant only to use DACB due to poor construction
Used to convert digital signals to analog signals - really useful for debugging on scopes
DAC
CTRLA - enable a channel on the DAC, and enable DAC
CTRLB - set DAC to single channel use or dual channel use
CTRLC - Set the reference voltage
DAC Channel
DACB.STATUS - used to check if its ready to convert again.
DACx.CHyDATA - 12 bit value to output
ADC(ADC, ADC_CH, ADC_CH_MUXPOS);
Pass in the ADC you want to use. channel, and the Pin you want to sue for POS side(NEG is gnd here)
Used to convert Analog signals to digital signals for processing. (i.e. - current sensing)
ADC can be setup in multiple ways- first divide being between single or differential mode
In the example, it is setup in differential mode W gain.
CTRLA enable ADC
CTRLB ADC Resolution
REFCTRL Ref voltage
PRESCALER ADC clock prescaler - ADC runs on its own clock- you want to make sure to run it down, otherwise you run into many problems.
ADC Channel
CTRL- channel input mode(DIFFWGAIN in this case) and the gain value, and to start a scan(1<<7)
remember to check if scan is done being computed before doing again. Value 1<<7 is turned off
when scan is done
MUXCTRL - setting the positive and negative pins.
PWM(Timer, Prescalar, pwm channel, base frequency, desired frequency)
For PWM, you have to setup multiple things. You are using the Timer on the chip,
so you have to setup its period(compare capture value with MAX 65536), its prescalar,
its mode, along with the channel at which to output when the timer hits the compare value.
Necessary Registers to run minimal PWM
Timer
CTRLA - prescalar - use the minimum amount in order to preserve as much
resolution as possible
CTRLB - Timer mode (Singleslope for PWM-> TC_WGMODE_SINGLESLOPE_gc)
and the pwm channel on which to output given as TCX_CCXEN_bm type
PERBUFL & PERBUFH - Hi and lo period values for timer. Timer counts up to
this value and then restarts back to 0.
Timer Channel
CCXBUFH & CCXBUFL - compare value. When the Timer hits this value on its
count up, it will change the output to low.
Also uses Timer (should be obvious given by name ;) )
To set up, make sure that interrupts are enabled by including <avr/interrupt.h>,
and calling sei(); and setting the PMIC CTRL pins to each level wanted
(high, medium, and/or low level interrupts)
Remember to set the pin you want as an output using PORTX.DIRSET register
Timer
CTRLA - setup prescalar
PERBUFL & PERBUFH - setup period same as PWM above
INTCTRLA - initiate the interrupt
then, add a method without return type(not even void) named ISR(TCCX_OVF_vect),
where X is your timer number with the contents that you want to loop over.
PID(P,I,D, dt, CONT, MIN & MAX IN & OUT, K)
Typical PID sequence, with some added features. enable CONT in order to have a loop
for a system that is circular (i.e. a 360 degree arm yes vs a 1d elevator no)
Setup mininum and maximum outputs, with integral portion not allowed to go over
the MAX& MIN Outs. Since the derivative portion can be quite noisy run at a very
fast frequency, a low pass filter is automatically applied to the D value,
with K being a constant used to change the low pass filter's response time. See
low pass filter for details on that. dt is the period of each loop.(1second /frequency)
crunch(input) logic
current error = setpoint - current value<<<<<< this is changed if it is CONT. If CONT, find
the shortest distance to setpoint is the current error.
total error+= current error;
derivate error = filter(current error - previous error)
output = P* current error + I* total Error + D* derivative error
onTarget() logic
if on target according to acceptable range for more than a
certain amount of time, return true
setAcceptableRange() logic
set the acceptable range +- where range is sepoint+-acceptable range
setSetpoint()
Current Control(P, I, dt, MAX&MIN IN&OUT)
Basic PI loop. I is used much more than normal, P being used for the initial ramp up.
Same setup as PID loop, but without the derivative term
crunch(input) logic
current error = setpoint - current value
the shortest distance to setpoint is the current error.
total error+= current error;
output = P* current error + I* total Error
onTarget() logic
if on target according to acceptable range for more than a
certain amount of time, return true
setAcceptableRange() logic
set the acceptable range +- where range is sepoint+-acceptable range
setSetpoint()
Encoder w/ interrupts/ATKEncoder
Encoders that automatically update based on interrupts running on a certain port.
PORTX_DIRSET &= ~(PINx_bm | PINy_bm) - set the direction of the two input pins
PORTX.INTCTRL - set the level of the interrupt(high/med/low)
PORTX.INTzMASK= set the interrupt of the port towards a certain pin
PORTX.PINxCTRL = set Pin to trigger on rising or falling edge
(make sure one interrupt is set on falling and other on rising edge)
ISR(PORTX_INTz_vect) logic ( 1 for each pin used)
if pins are different values, add to counter value, if pins are the same, subtract
This is due to the nature of encoders and how they count. If running clockwise,
the values line up, and if counter clockwise they dont line up(the values are 90 degrees away
from each other)
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
Basic Pin I/O
PORTX_DIRSET - set the direction of each port- 0 for input 1 for output
PORTX_DIRTGL - toggle direction
PORTX_DIRSET/CLR - set or clear direction of each port
PORTX_OUTSET/TGL/CLR- set/toggle/clear output to high or low
clock frequency setup
Don't really understand this, just copy paste. Used to set clock to 48 Mhz
OSC.XOSCCTRL = OSC_XOSCSEL_XTAL_256CLK_gc | OSC_FRQRANGE_12TO16_gc; // select external source
OSC.CTRL = OSC_XOSCEN_bm; // enable external source
while(!(OSC.STATUS & OSC_XOSCRDY_bm)); // wait for external
OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | OSC_PLLFAC0_bm | OSC_PLLFAC1_bm; // select external osc for pll, do pll = source * 3
//OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | OSC_PLLFAC1_bm; // pll = source * 2 for 32MHz std clock
OSC.CTRL |= OSC_PLLEN_bm; // enable PLL
while (!(OSC.STATUS & OSC_PLLRDY_bm)); // wait for PLL to be ready
CCP = CCP_IOREG_gc; // enable protected register change
CLK.CTRL = CLK_SCLKSEL_PLL_gc; // switch to PLL for main clock
low pass filters(K)
Used to clean a noisy signal. meant to be run at a fast frequency, since (1-2^-k)takes many
clock counts to settle.
Algorithm- output = y(n) = (1-2^-k)(y(n-1)) + (2^-k)(x(n)) << y(n) output, x(n)new input, n current clock count
filter(input)
give in new input, and returns in output
Currently two examples of working code using Atmel Studio
for the Automota Kit Breadboard board
DACExample
Outputs a sine wave out of the PB2 port using DACB
PWMControl
Outputs a pwm signal ranging from 0-100% duty cycle at 20khz