Created
May 17, 2022 10:54
-
-
Save SteelPh0enix/4b2d6fd626afd7dc037c9493aeb10bcc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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