Skip to content

Instantly share code, notes, and snippets.

@SteelPh0enix
Created May 17, 2022 10:54
Show Gist options
  • Save SteelPh0enix/4b2d6fd626afd7dc037c9493aeb10bcc to your computer and use it in GitHub Desktop.
Save SteelPh0enix/4b2d6fd626afd7dc037c9493aeb10bcc to your computer and use it in GitHub Desktop.
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#define _NOP() \
do { \
__asm__ __volatile__("nop"); \
} while (0)
static constexpr uint32_t UART_BAUD_RATE{1000000};
/// LED-related functions ///
void LEDSetup() {
// Set PB7 to output
DDRB |= _BV(DDB7);
}
void LEDSetState(bool state) {
if (state) {
// Set PB7 to HIGH
PORTB |= _BV(PB7);
} else {
// Set PB7 to LOW
PORTB &= ~(_BV(PB7));
}
}
void LEDToggle() {
// Check PB7 state
if (PINB & _BV(PB7)) {
LEDSetState(false);
} else {
LEDSetState(true);
}
}
/// UART-related functions ///
void UARTSetup(uint32_t baudrate) {
// UART settings:
// * 8 data bits
// * No parity bits (default)
// * 1 stop bit (default)
// * Asynchronous Normal mode (default)
uint32_t const baseClockFrequency = F_CPU;
uint32_t const uartClockDivider = 16;
uint16_t const UBRRValue = static_cast<uint16_t>(
(baseClockFrequency / (uartClockDivider * baudrate)) - 1);
// Setup TX0 pin as output (PE1)
DDRE |= _BV(PE1);
// Set baudrate
UBRR0 = UBRRValue;
// Enable transmitter
UCSR0B |= _BV(TXEN0);
// Set 8-bit frame format
UCSR0C |= _BV(UCSZ01) | _BV(UCSZ00);
}
void UARTSendByte(uint8_t value) {
// Wait until the previous transmission has ended
while (!(UCSR0A & _BV(UDRE0)))
;
// Put the data into buffer
UDR0 = value;
}
void UARTSendBytes(uint8_t const* data, size_t length) {
for (uint8_t const* ptr = data; ptr != (data + length); ptr++) {
UARTSendByte(*ptr);
}
}
void UARTSendString(char const* string) {
UARTSendBytes(reinterpret_cast<uint8_t const*>(string), strlen(string));
}
template <typename T>
void UARTSendValue(T value) {
UARTSendBytes((uint8_t const*)&value, sizeof(value));
}
/// Timer-related functions ///
void ICTimerInit() {
// Use Timer4, because only ICP4 and ICP5 are accessible on Arduino Mega
// (which is bullshit)
// Set capture on rising edge, and prescaler to 1 (timer should be clocked at
// F_CLK)
TCCR4B |= _BV(ICES4) | _BV(CS40);
// Enable input capture and overflow interrupts
TIMSK4 |= _BV(ICIE4) | _BV(TOIE4);
}
/// General functions ///
// This function configures Timer 1 Channel A (OC1A) as PWM
// with maximum frequency, so you can connect an oscilloscope
// and check the real timer clock
void enableClockTestOutput() {
// Enable Fast PWM mode with OCR1A as TOP for TIM1
// Enable pin output on PB5 (OC1A pin)
DDRB |= _BV(PB5);
// Toggle OC1A on compare match, for 50% PWM.
// Also, Fast PWM mode bits.
TCCR1A |= _BV(COM1A0) | _BV(WGM11) | _BV(WGM10);
// Fast PWM mode bits, also prescaler disabled
TCCR1B |= _BV(WGM12) | _BV(WGM13) | _BV(CS10);
// Set OCR1A to 0, so the PWM will have maximum frequency
// Measure the time of high or low signal part to check the frequency.
OCR1A = 0x0;
}
void resetSystemTimerPrescaler() {
// Make sure that there's no prescaler on Fclk
// To change prescaler, set CLKPCE bit in CLKPR register to 1,
// while simultanously setting the prescaler bits - here, we
// set them to 0 because we don't want any prescaler
CLKPR = _BV(CLKPCE);
// Wait 4 cycles to make sure the change is applied before proceeding
_NOP();
_NOP();
_NOP();
_NOP();
}
// Input-capture event
ISR(TIMER4_CAPT_vect) {
// Send the value over UART
UARTSendValue(static_cast<uint16_t>(ICR4));
}
// Overflow event
ISR(TIMER4_OVF_vect) {
// Add the max value of ICR register
// to indicate that overflow happened
// timeBetweenCaptures += 0xFFFF;
}
int main() {
cli(); // disable interrupts
resetSystemTimerPrescaler();
LEDSetup();
UARTSetup(UART_BAUD_RATE);
ICTimerInit();
// enableClockTestOutput();
sei(); // enable interrupts
while (true)
;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment