Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define F_CPU 16000000UL
- #define BAUD 57600 // NB not good for 115200, possibly need U2X high precision mode. Use setbaud.h features?
- //#include <util/setbaud.h>
- #include <util/delay.h>
- #include <inttypes.h>
- #include "nRF24L01.h"
- // ******************************************
- //
- // UART stuff - minimal logging
- //
- // ******************************************
- #define UART_MAX_ECHOABLE_ARRAY_LEN 50 // 5 byte array of unit8_t plus commas = 20 chars string length plus terminating 0
- void uart_init() {
- UBRR0 = F_CPU/16/BAUD-1; // NB this will set the 0L and the 0H. The BAUD-1 part is from the AVR datasheet, presumably kills an OBO error.
- UCSR0B = (1<<RXEN0)|(1<<TXEN0); // Enable receive and transmit
- UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); // 8 data, 2 stop?
- }
- void uart_putchar(char c) {
- loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
- UDR0 = c;
- }
- char uart_getchar(void) {
- loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
- return UDR0;
- }
- void uart_send(const char *txt){
- uint8_t index = 0;
- while(txt[index] != 0){
- uart_putchar(txt[index]);
- index++;
- }
- }
- void uart_send(int num){
- char chars[7] = {0}; // Maximum length for decimal string representation of an an int is 7 chars, as in "-32767\0"
- uart_int_to_decimal(num, chars);
- uart_send(chars);
- }
- void uart_send_hex(uint8_t num){
- char chars[3] = {0}; // Maximum length for hexadecimal string representation of an a uint8_t is 3 chars, as in "FF\0"
- uart_int_to_hex(num, chars);
- uart_send(chars);
- }
- void uart_sendl(const char *txt){
- uart_send(txt);
- uart_send("\r\n");
- }
- void uart_sendl(int num){
- uart_send(num);
- uart_send("\r\n");
- }
- void uart_send(uint8_t* array, uint8_t len){
- char txt[UART_MAX_ECHOABLE_ARRAY_LEN] = {0};
- uart_array_to_hex(array, len, txt);
- uart_send(txt);
- }
- void uart_array_to_hex(uint8_t* array, uint8_t len, char *txt){
- for(uint8_t i = 0; i < len; i++){
- char arrayMember[3] = {0}; // Max len
- uart_int_to_hex(array[i], arrayMember);
- uint8_t memberIdx = 0;
- int foo = 0;
- while(arrayMember[memberIdx] != 0){
- txt[0] = arrayMember[memberIdx];
- memberIdx ++;
- txt++;
- foo++;
- }
- if(i < len-1){
- txt[0] = ',';
- txt++;
- }
- }
- };
- void uart_int_to_decimal(int value, char *ptr){
- int temp;
- if(value==0){
- *ptr++='0';
- *ptr='\0';
- return;
- }
- if(value<0){
- value*=(-1);
- *ptr++='-';
- }
- for(temp=value;temp>0;temp/=10,ptr++);
- *ptr='\0';
- for(temp=value;temp>0;temp/=10){
- *--ptr=temp%10+'0';
- }
- }
- char hexchars[]="0123456789ABCDEF";
- void uart_int_to_hex(uint8_t value, char *ptr){
- ptr[0] = (hexchars[(value>>4)&0xf]);
- ptr[1] = (hexchars[value&0xf]);
- }
- // ******************************************
- //
- // Nordic Semiconductor nRF24L01+
- //
- // ******************************************
- uint8_t nrf_master_address[5] = {0xE1,0xE2,0xE3,0xE4,0xE5};
- uint8_t nrf_slave_address[5] = {0xD1,0xD2,0xD3,0xD4,0xD5};
- // CE is green; D9 = PB5
- // CSN is D10 = PB2
- // Where is the hardware connected?
- #define SPI_DEVICE_NRF 0 // See further info about this spi device under "spi stuff" below
- #define NRF_PORT PORTB
- #define NRF_PORT_DIR DDRB
- #define NRF_CSN_PIN 2
- #define NRF_CE_PIN 1
- #define NRF_USE_INTX true // Use INT0 or INT1 to signal nRF activity. Generally this will be used on receive.
- #define NRF_WHICH_INT 0 // INT0 or INT1. You need to define ISR (INTx_vect){...} as appropriate.
- // Options
- #define NRF_DEFAULT_FREQUENCY 2478 // 2478 is above wifi and legal in the USA.
- #define NRF_DEFAULT_PAYLOAD_WIDTH 3 // in bytes. Used to set RX pipe width.
- #define NRF_ROLE_TX true // This is default
- #define NRF_ROLE_RX false
- #define NRF_FCC_COMPLIANT true // Limits frequencies to < 2.482GHz for US regulatory compliance
- #define NRF_DEFAULT_RETRIES 3 // Up to 15
- #define NRF_DEFAULT_RETRY_DELAY 0 // Sets retry delay according to the formula (n+1)*250uS up to 15=4000us.
- #define NRF_USE_AUTO_ACKNOWLEDGE true
- #define NRF_TEST_ROLE NRF_ROLE_TX
- // Some nRF24L01 resources suggest a 10us delay after some ops, particularly sending SPI commands
- // and before sending data associated with that command. Often this seems unnecessary so it's
- // selectable here.
- #define USE_NRF_DELAY 0
- #if USE_NRF_DELAY == 1
- #define NRF_COURTESY_DELAY _delay_us(10);
- #else
- #define NRF_COURTESY_DELAY
- #endif
- // Caution: these extra defines, not in nRF24L01.h, represent
- // features which are possibly nRF24L01+ only.
- #define FEATURE 0x1D // This is a register
- #define EN_DPL 0x02 // Enable dynamic payload
- #define EN_ACK_PAY 0x01
- #define EN_DYN_ACK 0x00
- #define CONT_WAVE 0x07
- #define RF_DR_LOW 0x05
- #define RF_DR_HIGH 0x03
- #define RPD 0x00
- #define W_TX_PAYLOAD_NOACK 0xB0 // Command which was missing from the header
- // Select and deselect the nRF chip. NB that CS on the nRF is active-low.
- // This all now handled with spi_select etc.
- // FIXME: spi_select does not support NRF_COURTESY_DELAY, though that seems unnecessary.
- // Be careful in case this causes future problems.
- /*
- void nrf_select(){
- spi_select(SPI_DEVICE_NRF);
- //uart_sendl(NRF_PORT);
- //NRF_PORT &= ~(1<<NRF_CSN_PIN);
- NRF_COURTESY_DELAY
- }
- void //nrf_deselect(){
- spi_deselect();
- //NRF_PORT |= (1<<NRF_CSN_PIN);
- NRF_COURTESY_DELAY
- }
- */
- // Enable and disable the nRF chip. This is active-high.
- void nrf_enable(){
- NRF_PORT |= (1<<NRF_CE_PIN);
- NRF_COURTESY_DELAY
- }
- void nrf_disable(){
- NRF_PORT &= ~(1<<NRF_CE_PIN);
- NRF_COURTESY_DELAY
- }
- // Get a single-byte registerM
- uint8_t nrf_getreg(uint8_t reg){
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- reg = spi_transact(reg); // Should really be R_REGISTER | reg, but R_REGISTER is 0.
- NRF_COURTESY_DELAY
- uint8_t val = spi_transact(0b00000000); // Dummy byte so the SPI sends zeroes
- NRF_COURTESY_DELAY
- //nrf_deselect();
- spi_deselect();
- return val;
- }
- // Get a multi-byte register
- void nrf_getreg(uint8_t reg, uint8_t len, uint8_t* val){
- int i;
- NRF_COURTESY_DELAY
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- reg = spi_transact(reg); // Should really be R_REGISTER | reg, but R_REGISTER is 0.
- NRF_COURTESY_DELAY
- for(i = 0; i < len; i++){
- val[i] = spi_transact(0x00); // Dummy byte so the SPI sends zeroes
- NRF_COURTESY_DELAY
- }
- //nrf_deselect();
- spi_deselect();
- }
- // Set a single-byte register
- void nrf_setreg(uint8_t reg, uint8_t data){
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- reg = spi_transact(W_REGISTER | reg); // Set which register to write
- NRF_COURTESY_DELAY
- reg = spi_transact(data); // Write data
- NRF_COURTESY_DELAY
- //nrf_deselect();
- spi_deselect();
- }
- // Set a multi-byte register
- void nrf_setreg(uint8_t reg, uint8_t *data, uint8_t len){
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- reg = spi_transact(W_REGISTER | reg); // Set which register to write
- NRF_COURTESY_DELAY
- for(uint8_t i = 0; i < len; i++){
- reg = spi_transact(data[i]); // Write data
- NRF_COURTESY_DELAY
- }
- //nrf_deselect();
- spi_deselect();
- }
- // Powerup and powerdown the nRF chip.
- // It takes 1.5ms with internel osc, or 150us with external osc (which we think
- // we have) to get from powerdown to standby, so we don't want to do that too
- // often - consider leaving powered up.
- void nrf_powerup(){
- uint8_t cfreg = nrf_getreg(CONFIG);
- cfreg = cfreg | (1<<PWR_UP);
- nrf_setreg(CONFIG, cfreg);
- _delay_ms(5); // Theoretically 1.5ms to reach stby while CE is low; 150us if we're on external clock which we think we are.
- }
- void nrf_powerdown(){
- uint8_t cfreg = nrf_getreg(CONFIG);
- cfreg &= ~(1<<PWR_UP);
- nrf_setreg(CONFIG, cfreg);
- _delay_ms(5); // FIXME: It doesn't really say how long it takes to power down. This is a courtesy delay and is possibly way, way too long, or unnecessary.
- }
- // Send the payload to the nRF. The command W_TX_PAYLOAD is followed by the bytes to be sent.
- // The delays are possibly unnecessary, or at least other than after setting CSN
- void nrf_tx_immediate(uint8_t* payload, uint8_t len){
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- if(NRF_USE_AUTO_ACKNOWLEDGE){
- spi_transact(W_TX_PAYLOAD);
- } else {
- spi_transact(W_TX_PAYLOAD_NOACK);
- }
- NRF_COURTESY_DELAY
- for(uint8_t i = 0; i < len; i++){
- spi_transact(payload[i]);
- NRF_COURTESY_DELAY
- }
- //nrf_deselect();
- spi_deselect();
- // Pulse CE to actually transmit the data
- //nrf_select();
- //NRF_COURTESY_DELAY // In some implementations we needed a 10 millisecond (millisecond!) delay here, but it seems OK without.
- nrf_enable();
- _delay_us(15); // This actually is required per the datasheet; apparently minimum CE high is 10us.
- nrf_disable();
- //NRF_COURTESY_DELAY
- ////nrf_deselect();
- _delay_us(2000); // FIXME: Ensure we've had time for all the retries and so on. With 250us and 3 retries set, 1000 should be enough, but wasn't.
- // Possibly this is due to PLL settling delay?
- // Anyway, assuming we're asynchronously sending it won't matter and we can probably take this out
- // Hard reset all the IRQ flags. This appears to be essential.
- nrf_clear_irqs();
- }
- // Read received data
- // This reads everything from the three buffers on the nRF into these three arrays.
- // Usually, just ensure we get each one as it comes in; if nrf_getata() != 0, there's
- // at least something in nrf_received[0].
- // Don't call this unless you're happy to have all of nrf_received overwritten
- uint8_t nrf_received[3][NRF_DEFAULT_PAYLOAD_WIDTH];
- bool nrf_pendingPkts = false;
- uint8_t nrf_pollfordata(){
- uint8_t msgcount = 0;
- while(~nrf_getreg(STATUS) & RX_DR){
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- spi_transact(R_RX_PAYLOAD);
- NRF_COURTESY_DELAY
- for(uint8_t i = 0; i < NRF_DEFAULT_PAYLOAD_WIDTH; i++){
- nrf_received[msgcount][i] = spi_transact(0x00);
- }
- /*uart_send("Receive:");
- uart_send(nrf_received[msgcount],NRF_DEFAULT_PAYLOAD_WIDTH);
- uart_sendl("");*/
- msgcount++;
- //nrf_deselect();
- spi_deselect();
- }
- nrf_pendingPkts = false;
- nrf_clear_irqs();
- return msgcount;
- }
- // Clear any set IRQ-type-indicating flags. Active low!
- void nrf_clear_irqs(){
- /*
- // RX_DR "received data ready" there is something to receive
- // TX_DS "transmit data sent" (dependent on successful acknowledgement if selected)
- // MAX_RT "maximum number of retries" has been reached.
- uint8_t statusReg = nrf_getreg(STATUS);
- statusReg |= (1<<RX_DR | 1<<TX_DS | 1<<MAX_RT);
- nrf_setreg(STATUS, statusReg);
- */
- /*
- // Fastest possible fully-inlined IRQ reset
- NRF_PORT &= ~(1<<NRF_CSN_PIN); // Select nRF chip
- SPDR = W_REGISTER | STATUS;
- while(!(SPSR & (1<<SPIF)));
- // Set all IRQ flags inactive-high
- SPDR = 0x70; // Flag high all the writable bits in the register
- while(!(SPSR & (1<<SPIF)));
- NRF_PORT |= (1<<NRF_CSN_PIN);
- */
- // Because the only writable bits in STATUS are the three IRQs, and because their value ORs to 0x70
- // this basically boils down to:
- nrf_setreg(STATUS, 0x70);
- }
- // Flush any leftover stuff in the transmit buffers.
- void nrf_flush_tx(){
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- spi_transact(FLUSH_TX); // FLUSH_TX is a command not a register so it gets sent directly
- NRF_COURTESY_DELAY
- //nrf_deselect();
- spi_deselect();
- }
- // Flush any leftover stuff in the receive buffers.
- void nrf_flush_rx(){
- //nrf_select();
- spi_select(SPI_DEVICE_NRF);
- spi_transact(FLUSH_RX); // FLUSH_RX is a command not a register so it gets sent directly
- NRF_COURTESY_DELAY
- //nrf_deselect();
- spi_deselect();
- }
- // Be what the datasheet calls a PTX or PRX (primarily transmitter or receiver)
- void nrf_setrole(bool isTx){
- uint8_t cfreg = nrf_getreg(CONFIG);
- if(isTx){
- cfreg &= ~(1<<PRIM_RX); // Be a transmitter
- } else {
- cfreg |= (1<<PRIM_RX); // Be a receiver
- }
- nrf_setreg(CONFIG, cfreg);
- }
- // Set radio freqency. The hardware will do 125 channels, which must be 2+MHz apart at 2Mbps
- // Not all channels are legal everywhere.
- bool nrf_setfreq(int freq){
- if(freq < 2400 || freq > 2525){
- nrf_err("Requested frequency not available");
- return false;
- }
- if(NRF_FCC_COMPLIANT && freq > 2482){
- nrf_err("Cannot set frequency over 2.482GHz with bandwidth <=2MHz in FCC controlled locale");
- return false;
- }
- nrf_setreg(RF_CH, NRF_DEFAULT_FREQUENCY - 2400);
- return true;
- }
- // Set retry count and delay. Not really in use at the moment.
- // Args: retries, number of retries <16
- // dly, delay according to (dly + 1) * 250us, for a maximum of 15=4000us
- void nrf_setretries(uint8_t retries, uint8_t dly){
- nrf_setreg(SETUP_RETR, retries | (dly << ARD)); // ARD is 4, to get the upper 4 bytes.
- }
- // Set the width of the receive pipe's payload.
- // This sets all the pipes, though we really only care about pipe 0 since it's used for auto-ack
- // This implementation of the nRF24L01 doesn't deal with multiple pipe operation.
- void nrf_set_rx_payload_width(uint8_t width){
- for(uint8_t i = 0; i < 6; i++){
- nrf_setreg(RX_PW_P0 + i, width); // Register addresses are sequential, so we can add the incrementing variable
- }
- }
- // Set whether to use auto-acknowledgement
- void nrf_set_use_auto_ack(bool autoAck){
- if(autoAck){
- nrf_setreg(EN_AA, ENAA_P5 | ENAA_P4 | ENAA_P3 | ENAA_P2 | ENAA_P1 | ENAA_P0);
- } else {
- nrf_setreg(EN_AA,0x00);
- }
- // W_TX_PAYLOAD_NOACK command is always enabled in init();
- }
- // Log some info about the nRF24L01 setup. Usually just alias to a usart send function.
- void nrf_log(const char* str){
- uart_sendl(str);
- }
- // Send an error message. Really just adds a prefix. Again, alias to a usart send function.
- void nrf_err(const char* str){
- uart_send("NRF24L01: Error: ");
- uart_sendl(str);
- }
- // Example ISR for packet arrival. All this does (with the uart_sends commented
- // out) is set a flag, but critical timing states could also be stored here.
- // Uses nrf_pendingPkts bool to signal stuff available
- // NB change defines to point the code to the right interrupt.
- ISR(INT0_vect) {
- nrf_pendingPkts = true;
- }
- ISR(BADISR_vect){
- uart_sendl("Error in ISR setup.");
- }
- void nrf_disable_interrupts(bool receivedData, bool transmitComplete, bool transmitFailed){
- uint8_t val = nrf_getreg(CONFIG);
- if(receivedData){
- val |= (1<<MASK_RX_DR);
- } else {
- val &= ~(1<<MASK_RX_DR);
- }
- if(transmitComplete){
- val |= (1<<MASK_TX_DS);
- } else {
- val &= ~(1<<MASK_TX_DS);
- }
- if(transmitFailed){
- val |= (1<<MASK_MAX_RT);
- } else {
- val &= ~(1<<MASK_MAX_RT);
- }
- nrf_setreg(CONFIG, val);
- }
- // Set up the nRF chip as from cold start.
- // This will clear everything and leave the chip in transmit mode but powered down, deselected and disabled.
- // It may take a few milliseconds to run, if the nRF chip needs a long timeout to reach standby.
- void nrf_init(){
- // Set CE and CSN pins to be outputs
- // CE is green wire, arduino pin D9/PB1
- // CSN pin is blue wire, arduino pin D10/PB2, but is now dealt with by the SPI stuff
- //NRF_PORT_DIR |= (1<<NRF_CE_PIN) | (1<NRF_CSN_PIN);
- NRF_PORT_DIR |= (1<NRF_CSN_PIN);
- // Set up RX interrupts using INTx pins
- // NB - this is set up for the ATmega328. 168 is possibly identical.
- if(NRF_USE_INTX){
- if(NRF_WHICH_INT == 0){
- DDRD &= ~(1 << DDD2); // Make the relevant pin an input
- EICRA |= (1 << ISC01); // Trigger on falling edges only.
- EIMSK |= (1 << INT0); // Turns on interrupts for that pin
- } else {
- DDRD &= ~(1 << DDD3); // Make the relevant pin an input
- EICRA |= (1 << ISC11); // Trigger on falling edges only.
- EIMSK |= (1 << INT1); // Turns on interrupts for that pin
- }
- sei();
- }
- //nrf_deselect();
- spi_deselect(); // CSN high
- nrf_disable(); // CE low
- // Set up nRF24L01 chip.
- nrf_powerdown();
- nrf_setrole(NRF_ROLE_TX);
- nrf_setfreq(NRF_DEFAULT_FREQUENCY);
- nrf_setretries(NRF_DEFAULT_RETRIES, NRF_DEFAULT_RETRY_DELAY); // Number of retries <16; Delay (n+1)*250us, max 15=4000us
- nrf_set_rx_payload_width(NRF_DEFAULT_PAYLOAD_WIDTH);
- nrf_flush_tx();
- nrf_flush_rx();
- nrf_clear_irqs();
- nrf_disable_interrupts(false, false, false); // Don't disable any interrupts (probably get modified later anyway)
- // Enable sending packets without auto acknowledgement (Always enable this,
- // just don't actually use W_TX_PAYLOAD_NOACK unless we're asked to.)
- uint8_t featureReg = nrf_getreg(FEATURE);
- featureReg |= (1 << EN_DYN_ACK); // This equates to 0x01, since EN_DYN_ACK == 0.
- nrf_setreg(FEATURE, featureReg);
- // Addresses
- // Set P0 so that basic auto-ack will work, but we aren't really supporting any other pipes in this simple framework
- nrf_setreg(TX_ADDR, nrf_master_address, 5); // Set transmission address
- nrf_setreg(RX_ADDR_P0, nrf_master_address, 5); // Pipe-0 receive addr should equal transmission address for automatic acknowledgement
- // Finished setup. Send some debug.
- //nrf_log("nRF24L01: Post-reset. Status now:");
- //nrf_read_status(); // Reads a bunch of registers and sends out over uart in a sort of human readable format.
- }
- // ******************************************
- //
- // SMPTE-12M Linear Timecode Reader
- //
- // ******************************************
- #define LTC_REQUIRED_FRAMES 30
- #define LTC_AVG_FRAMES 16
- uint8_t ltc_magic_number_A = 0b00111111; // 3F
- uint8_t ltc_magic_number_B = 0b11111101; // FD
- uint8_t ltc_magic_number_A_rev = 0b11111100; // FC
- uint8_t ltc_magic_number_B_rev = 0b10111111; // BF
- uint8_t ltc_bitperiod_threshold = 75; // For clk/64, this should work for most frame rates.
- uint8_t ltc_buf[11]; // 11 so we can shift easily
- uint8_t ltc_buf2[11]; // 11 so we can shift easily
- uint8_t *rptr;
- uint8_t *wptr;
- volatile bool ltc_short_wait_flag = false;
- volatile bool ltc_frame_ready = false;
- // Log some info about the LTC reader setup. Usually just alias to a usart send function.
- void ltc_log(const char* str){
- uart_send("LTC Reader: ");
- uart_sendl(str);
- }
- void ltc_init(){
- ltc_log("Set up...");
- // Signal: PD7 AIN1 neg - arduino digital 7, ATmega328 pin 13
- // Signal ground: PD6 AINO pos - arduino digital 6, ATmega328 pin 12
- // Set up ports
- DDRD &= ~((1<<7) | (1<<6)); // Set PD6 and PD7 as inputs TODO: Break pin assigns out into settings, if they can be changed at all
- PORTD &= ~((1<<7) | (1<<6)); // with pullup off
- // Set up ADC/AC hardware
- ADCSRA &= ~(1<<ADEN); // Disable the ADC
- ADCSRB &= ~(1<<ACME); // Disable analog comparator input multiplexer so we always read AIN0 and AIN1
- ADCSRB &= ~((1<<ADTS2) | (1<<ADTS1)); // Clear two MSB of ADTSn, which will
- ADCSRB |= 1<<ADTS0; // in concert with this select "analog comparator" in ADC status register B.
- DIDR1 = (1<<AIN1D) | (1<AIN0D); // Disable the digital input buffer on the AINx pins to reduce current consumption
- ACSR = (0<<ACIE) | (1<<ACIC); // Bits 3 and 2 - enable interrupt.
- // Otherwise, most of what we want is zeroes:
- // ACD (7) = Analog Comparator Disable
- // ACBG (6) = use bandgap reference instead of positive input.
- // ACO (5) Read-only comparator output state
- // ACI (4) Interrupt flag. Can be cleared manually, so clear it here to avoid shenanigans.
- // ACIS[1:0] Zeroed - comparator interrupt on output toggle.
- //ACSR |= ((1<<ACIS1) | (1<<ACIS0));
- ACSR &= ~((1<<ACIS1) | (0<<ACIS0));
- // Set up timer
- TCCR1A = 0x00; // Don't want any output compare pins toggling, nor any waveform generator functions
- TCCR1B = 0x00; // Explicitly zero the register to avoid shenanigans
- TCCR1B |= ((0<<ICNC1) | // Enable noise canceler. Requires four sequential identical samples to trip (operates on both anacomp and input capture events)
- (1<<ICES1) | // Trip on rising edge first. This gets swapped a lot later.
- (1<<CS10) | // Clock prescaler selection 12:10. 101 = clk/1024, 100 = /256, 011 = /64, 010 = /8, 0x1 = /1. 110 = ext clock on T1, falling edge. 111 = rising edge
- (1<<CS11) |
- (0<<CS12)
- );
- TIMSK1 = 0x00; // Explicitly zero the register to avoid shenanigans
- TIMSK1 |= (1<<ICIE1); // Enable input capture interrupt, which is the one used by the AC.
- for(uint8_t i = 0; i < 11; i++){
- ltc_buf[i] = ltc_buf2[i] = 0;
- }
- wptr = ltc_buf;
- rptr = ltc_buf2;
- }
- ISR(ANALOG_COMP_vect){ // Currently disabled; see line ACSR = (0<<ACIE) | (1<<ACIC); // above
- uart_sendl("Foo");
- /*TCNT1=0
- if(ACSR & (1<<ACIS0)){ // We just saw a falling output edge
- ACSR |= 1<<ACIS0;
- } else { // We just saw a rising output edge
- ACSR &= ~(1<<ACIS0);
- } */
- }
- volatile uint32_t ltc_frametime = 0;
- volatile uint32_t ltc_frametime_wkg = 0; // This is used to signal a frame decoded when it's nonzero.
- //uint8_t ltc_avgframes = 0;
- uint8_t lastTime = 0;
- //uint8_t ltc_transitions = 0;
- bool ltc_signal = false;
- //const uint8_t TCCR_ICES1 = (1 << ICES1); // IRC adds
- ISR(TIMER1_CAPT_vect){
- //lastTime = ICR1; // Input capture register. Will have captured the counter value at the moment of the interrupt
- ltc_frametime += ICR1; // Update the total time for this frame
- TCNT1=0; // Zero the timer for this bitperiod
- // Flip looking for rising or falling edges
- /*if(TCCR1B & (1<<ICES1)){
- TCCR1B &= ~(1<<ICES1);
- } else {
- TCCR1B |= (1<<ICES1);
- }*/
- TCCR1B = TCCR1B == 67 ? 3 : 67; // Abbreviates the above
- //TCCR1B ^= TCCR_ICES1; // IRC adds
- // Figure out whether this was a long or short bitperiod
- // and if it was short, figure out if it's the second short
- // one in a row.
- if(ICR1>ltc_bitperiod_threshold){
- // It's a zero. Zero the last bit in the buffer.
- wptr[9] &= ~(0x80);
- //ltc_short_wait_flag = false; // Possibly prevents shenanigans
- } else {
- // It's possibly part of a 1
- if(ltc_short_wait_flag){
- // It's a 1
- wptr[9] |= 0x80; // Set the last bit in the buffer.
- ltc_short_wait_flag = false;
- } else {
- // It's presmably the beginning of a 1
- ltc_short_wait_flag = true;
- return;
- }
- }
- // Check to see if we've found a full frame
- if(wptr[8] == ltc_magic_number_A_rev && wptr[9] == ltc_magic_number_B_rev){
- if(!ltc_signal){
- ltc_log("Detected signal.");
- ltc_signal = true;
- }
- ltc_frametime_wkg = ltc_frametime; // Set this nonzero to indicate a frame decoded.
- ltc_frametime = 0;
- // Swap buffers
- if (rptr == ltc_buf){
- rptr = ltc_buf2;
- wptr = ltc_buf;
- }else{
- rptr = ltc_buf;
- wptr = ltc_buf2;
- }
- }
- // Shuffle the buffer
- // TODO: Optimise with asm? Investigate srac instruction - shift right with carry
- for(uint8_t i = 0; i < 10; i++){ // Remember we made an extra byte on the end we don't need?
- wptr[i] = (wptr[i] >> 1) | (wptr[i+1] << 7);
- }
- }
- uint32_t ltc_timeavg = 0;
- uint8_t ltc_avgcount = 0;
- uint8_t ltc_maxavg = 32;
- uint16_t ltc_frametime_avg;
- typedef enum ltc_framerate{
- UNKNOWN,
- R23976,
- R24,
- R25,
- R2997,
- R30
- };
- const char* ltc_framerate_names[] = {
- "Resolving...",
- "23.976",
- "24",
- "25",
- "29.970",
- "30"
- };
- uint8_t ltc_framerate_maxframes[] = {
- 0, 23, 23, 24, 29, 29
- };
- ltc_framerate ltc_framerate_now = UNKNOWN;
- ltc_framerate ltc_framerate_then = UNKNOWN;
- uint8_t ltc_hh = 0;
- uint8_t ltc_mm = 0;
- uint8_t ltc_ss = 0;
- uint8_t ltc_ff = 0;
- bool ltc_df = false;
- /*
- uint8_t ltc_prev_hh = 0;
- uint8_t ltc_prev_mm = 0;
- uint8_t ltc_prev_ss = 0;
- uint8_t ltc_prev_ff = 0;
- bool prev_ltc_df = false;
- */
- void ltc_decode_frame(){
- /*ltc_prev_hh = ltc_hh;
- ltc_prev_mm = ltc_mm;
- ltc_prev_ss = ltc_ss;
- ltc_prev_ff = ltc_ff;*/
- uint8_t nums[8];
- nums[0] = rptr[7] & 0b00000011;
- nums[1] = rptr[6] & 0b00001111;
- nums[2] = rptr[5] & 0b00000111;
- nums[3] = rptr[4] & 0b00001111;
- nums[4] = (rptr[3] & 0b00000111);
- nums[5] = (rptr[2] & 0b00001111);
- nums[6] = (rptr[1] & 0b00000011);
- nums[7] = (rptr[0] & 0b00001111);
- uart_send(nums, 8);
- uart_send("\r\n");
- display_show(nums);
- /*
- ltc_hh = (10*(rptr[7] & 0b00000011)) + (rptr[6] & 0b00001111); // Other bits in hrs tens are reserved and binary group flag
- ltc_mm = (10*(rptr[5] & 0b00000111)) + (rptr[4] & 0b00001111); // Other bit in mins tens is binary group flag
- ltc_ss = (10*(rptr[3] & 0b00000111)) + (rptr[2] & 0b00001111); // Other bit in secs tens is biphase mark correction bit
- ltc_ff = (10*(rptr[1] & 0b00000011)) + (rptr[0] & 0b00001111); // Other bits in frames tens are drop frame and color frame flags
- ltc_df = (rptr[1] & 0b00000100) == 4;
- */
- }
- // ******************************************
- //
- // 7-segment display
- //
- // ******************************************
- /*
- * This was the version for the plugboard
- uint8_t display_font[] = {
- // cdebafg dp
- 0b11111100,
- 0b10010000,
- 0b01111010,
- 0b11011010,
- 0b10010110,
- 0b11001110,
- 0b11101110,
- 0b10011000,
- 0b11111110,
- 0b11011110,
- };
- */
- uint8_t display_font[] = {
- // gfedcb.a
- 0b01111101, // 0
- 0b00001100, // 1
- 0b10110101, // 2
- 0b10011101, // 3
- 0b11001100, // 4
- 0b11011001, // 5
- 0b11111001, // 6
- 0b00001101, // 7
- 0b11111101, // 8
- 0b11011101, // 9
- };
- // Where is the hardware connected?
- #define SPI_DEVICE_DSPLY 1
- #define DSPLY_PORT PORTB
- #define DSPLY_PORT_DIR DDRB
- #define DSPLY_CS_PIN 0
- #define DSPLY_LATCH_PORT PORTD
- #define DSPLY_LATCH_PORT_DIR DDRD
- #define DSPLY_LATCH_PIN 5
- void display_init(){
- DSPLY_LATCH_PORT_DIR |= (1 << DSPLY_LATCH_PIN);
- DSPLY_LATCH_PORT &= ~(1 << DSPLY_LATCH_PIN);
- display_test();
- }
- void display_latch(){
- DSPLY_LATCH_PORT |= (1 << DSPLY_LATCH_PIN);
- _delay_ms(1);
- DSPLY_LATCH_PORT &= ~(1 << DSPLY_LATCH_PIN);
- }
- void display_test(){
- uint8_t eights[] = {8,8,8,8,8,8,8,8};
- display_show(eights);
- _delay_ms(500);
- display_clear();
- }
- void display_show(uint8_t nums[8]){
- spi_select(SPI_DEVICE_DSPLY);
- uint8_t i = 7;
- do{
- uint8_t dp = 0;
- if(i==1 || i == 3 || i == 5) dp = 0b00000010;
- spi_transact(display_font[nums[i]] | dp);
- } while (i--);
- display_latch();
- spi_deselect();
- }
- void display_clear(){
- spi_select(SPI_DEVICE_DSPLY);
- for(uint8_t i = 0; i < 8; i++){
- spi_transact(0);
- }
- display_latch();
- spi_deselect();
- }
- // ******************************************
- //
- // SPI stuff
- //
- // ******************************************
- typedef struct{
- uint8_t pin;
- volatile uint8_t *port;
- volatile uint8_t *ddr;
- bool activeHigh;
- }spi_device;
- // Declare SPI devices for this project:
- // Syntax: pin on port, port, active hi/low
- // Each SPI device also needs a uint8_t called something like SPI_DEVICE_WHATEVER identifying the order
- // in which it appears here.
- // Where is the hardware connected?
- spi_device spi_devices[] = {
- {
- .pin = NRF_CSN_PIN,
- .port = &NRF_PORT,
- .ddr = &NRF_PORT_DIR,
- .activeHigh = false
- },
- {
- .pin = DSPLY_CS_PIN,
- .port = &DSPLY_PORT,
- .ddr = &DSPLY_PORT_DIR,
- .activeHigh = true
- }
- };
- uint8_t spi_currently_selected = 255;
- void spi_init(){
- // Set /SS, MOSI and SCK as output
- // /SS is PB2, MOSI is PB3, SCK is PB5
- // NB most applications will also need at least a chip select set as output
- DDRB |= (1<<DDB5) | (1<<DDB3) | (1<<DDB2);
- // Set SPE (SPI Enable) and MSTR (use clock rate/16) of the SPCR register
- SPCR |= (1<<SPE) | (1<<MSTR) | (1<<SPR0);
- // Do not enable SPI interrupt, since we're only supporting master mode operation
- // Prepare chip select pins and ports for the required IO work
- for(uint8_t i = 0; i < sizeof(spi_devices) / sizeof(spi_device); i++){
- spi_devices[i].pin = (1<<spi_devices[i].pin); // Convert it to a bitmask as that's how we'll always use it.
- *spi_devices[i].ddr |= spi_devices[i].pin;
- }
- }
- uint8_t spi_transact(uint8_t data){ // Single bytes
- SPDR = data;
- while(!(SPSR & (1<<SPIF)));
- return SPDR;
- }
- void spi_transact(uint8_t * data, uint8_t len, uint8_t* response){ // Multi bytes
- for(uint8_t i = 0; i < len; i++){
- response[i] = spi_transact(data[i]);
- }
- }
- void spi_select(uint8_t dev){
- if(spi_currently_selected != 255){
- uart_sendl("Glitch. Attempted to select SPI device when already selected.");
- return;
- }
- if(spi_devices[dev].activeHigh){
- *spi_devices[dev].port |= spi_devices[dev].pin;
- } else {
- *spi_devices[dev].port &= ~spi_devices[dev].pin;
- }
- spi_currently_selected = dev;
- }
- void spi_deselect(){
- if(spi_currently_selected == 255){
- uart_sendl("Glitch. Attempted to deselect SPI device when none selected.");
- return;
- }
- if(spi_devices[spi_currently_selected].activeHigh){
- *spi_devices[spi_currently_selected].port &= ~spi_devices[spi_currently_selected].pin;
- } else {
- *spi_devices[spi_currently_selected].port |= spi_devices[spi_currently_selected].pin;
- }
- spi_currently_selected = 255;
- }
- uint8_t ltc_expectedframe = 0;
- uint8_t ltc_goodframes = 0;
- bool ltc_codegood = false;
- int main(void){
- uart_init();
- spi_init();
- nrf_init();
- sei();
- ltc_init();
- nrf_read_status();
- display_init();
- //return 0;
- while(1){
- if(ltc_frametime_wkg>0){ // Nonzero working frame time indicates a frame decoded
- // Get times and DF flag from decoded data
- ltc_decode_frame();
- // If we know what rate we're doing, it's possible to work out whether we have good code
- if(ltc_framerate_now != UNKNOWN){
- if(ltc_ff != ltc_expectedframe){
- // If this wasn't the expected frame, zero the good-frames counter and reset the rate to unknown, as we may have been wrong
- ltc_goodframes = 0;
- ltc_framerate_now = UNKNOWN;
- } else {
- // If this was the expected frame but we aren't yet confident that we have good code, increase the good-frames counter
- if(!ltc_codegood) ltc_goodframes++;
- }
- if(ltc_goodframes >= LTC_REQUIRED_FRAMES){
- // If we've had enough good frames, we now have good timecode. If we hadn't already decided that, flag it up.
- if(!ltc_codegood){
- ltc_log("Good timecode. ");
- ltc_codegood = true;
- }
- } else {
- // If we haven't had enough good frames, but we already thought we had good timecode, declare that we now don't have good timecode.
- if(ltc_codegood){
- ltc_log("Bad timecode.");
- ltc_codegood = false;
- ltc_framerate_now = UNKNOWN;
- ltc_signal = false; // TODO: Really this should get zeroed after some time has passed without transitions
- }
- }
- }
- /*
- ltc_expectedframe = ltc_get_next_frame();
- ltc_timeavg += ltc_frametime_wkg;
- ltc_avgcount ++;
- if(ltc_avgcount > ltc_maxavg){
- ltc_frametime_avg = ltc_timeavg / ltc_maxavg;
- ltc_timeavg = 0;
- ltc_avgcount = 0;
- }
- */
- /*
- Notes - approx clk/64 periods per frame based on signal from Fostex FR-2TC board.
- 23.976 - around 10708-10710 (Ideal: 10427.0833)
- 24 - around 10697-10699 (Ideal: 10416.667)
- 25 - 10267-10269 (Ideal: 10000)
- 2997 - 8551-8554 (Ideal: 8341.667)
- 30 - 8543-8547 (Ideal: 8333.33)
- 23.976 10648 10652
- 24 10637 10640
- 25 10210 10211
- 29.970 8493 8497
- 30 8483 8486
- */
- // ((f_cpu/64)*1001)/30000
- // TODO: rates of 50, 5994 and 60 exist in recent versions of SMPTE-12M.
- /*
- ltc_framerate_now = UNKNOWN;
- if(ltc_frametime_avg > 10644){
- ltc_framerate_now = R23976;
- } else if(ltc_frametime_avg < 10644 && ltc_frametime_avg > 10635){
- ltc_framerate_now = R24;
- } else if (ltc_frametime_avg < 10220 && ltc_frametime_avg > 10200){ // can be fairly wide on this
- ltc_framerate_now = R25;
- } else if (ltc_frametime_avg < 8500 && ltc_frametime_avg > 8490){
- ltc_framerate_now = R2997;
- } else if (ltc_frametime_avg < 8490){
- ltc_framerate_now = R30;
- }*/
- /*
- if(ltc_framerate_then != ltc_framerate_now){
- uart_send("LTC Reader: Frame rate changed to ");
- uart_sendl(ltc_framerate_names[ltc_framerate_now]);
- ltc_framerate_then = ltc_framerate_now;
- ltc_codegood = false;
- }
- ltc_codegood = true;
- if(true){
- uart_send(ltc_ff);
- uart_send(" ");
- uart_send(ltc_frametime_avg);
- uart_send(" ");
- uart_send(ltc_framerate_names[ltc_framerate_now]);
- uart_send(" ");
- uart_sendl(ltc_df ? "DF" : "NDF");
- }
- */
- ltc_frametime_wkg = 0; // Must zero this after handling a frame.
- }
- }
- }
- uint8_t ltc_get_next_frame(){
- if(ltc_ff == ltc_framerate_maxframes[ltc_framerate_now]){
- if(ltc_df &&
- ltc_ss == 59 &&
- ltc_mm != 9 &&
- ltc_mm != 19 &&
- ltc_mm != 29 &&
- ltc_mm != 39 &&
- ltc_mm != 49 &&
- ltc_mm != 59
- ){
- return 2;
- } else {
- return 0;
- }
- } else {
- return ltc_ff + 1;
- }
- }
- /*
- void ltc_get_framerate_ui_name(ltc_framerate rate, char* name){
- switch(rate){
- case UNKNOWN:
- name = "Wait..";
- break;
- case R23976:
- name = "23.976";
- break;
- case R24:
- name = "24.000";
- break;
- case R25:
- name = "25.000";
- break;
- case R2997:
- name = "29.970";
- break;
- case R30:
- name = "30.000";
- break;
- }
- }
- */
- // Prints out a load of status information about the nRF24L01 attached.
- void nrf_read_status(){
- uint8_t gpreg; // General-purpose variable to hold registers we won't need twice
- uart_sendl("nRF24L01 configuration\r\n");
- uart_sendl(" Radio setup:");
- uart_send(" Freq: ");
- gpreg = nrf_getreg(RF_CH);
- int freq = gpreg + 2400;
- uart_send(freq);
- uart_send("MHz; ");
- uint8_t configRegister = nrf_getreg(CONFIG);
- gpreg = nrf_getreg(RF_SETUP);
- uart_send("; RF output: ");
- uart_send(18 - (((gpreg & 0x06) >> 1) * 6)); // 00 = -18, 01 = -12, 10 = 06, 11 = 0.
- uart_send("dBm; ");
- uart_send("Mode: ");
- uart_send(configRegister & (1 << PRIM_RX) ? "RX; " : "TX; ");
- uart_send("\r\n TX addr: ");
- uint8_t txaddr[5];
- nrf_getreg(TX_ADDR, 5, txaddr);
- uart_send(txaddr, 5);
- uart_send("\r\n RX addr P0: ");
- nrf_getreg(RX_ADDR_P0, 5, txaddr);
- uart_send(txaddr, 5);
- uart_send("\r\n (XP) RF ambient >-64dBm: ");
- uart_send(nrf_getreg(RPD) & 0x1); // RPD will be 1 if there's >-64dBm of signal at this freq
- uart_send("; Radio power-up: ");
- uart_send(configRegister & 1<<PWR_UP ? 1 : 0);
- uart_send("; Bitrate: ");
- if(gpreg & 1 << RF_DR_LOW){
- uart_send("250Kbps");
- } else {
- if(gpreg & 1 << RF_DR_HIGH){
- uart_send("2Mbps");
- } else {
- uart_send("1Mbps");
- }
- }
- uart_send("; TX carrier?: ");
- uart_send(gpreg & 1 << CONT_WAVE ? 1 : 0);
- uart_sendl("");
- gpreg = nrf_getreg(OBSERVE_TX);
- uart_send(" Total lost pkts: ");
- uart_send((int)(gpreg >> 4));// Get the upper 4 bits, 7:4
- uart_send("; Retransmitted this pkt: ");
- uart_send((int)(gpreg & 0xF));// Get the lower 4 bits, 3:0
- uart_sendl("\r\n\r\n Packet configuration:");
- uart_send(" Use CRC: ");
- uart_send(configRegister & 1<<PWR_UP ? 1 : 0);
- uart_send("; CRC len: ");
- uart_send(configRegister & 1 << CRCO ? "2 " : "1 ");
- uart_send("; IRQ masked? RX_DR ");
- uart_send(configRegister & 1<<MASK_RX_DR ? 1 : 0);
- uart_send("; TX_DS ");
- uart_send(configRegister & 1<<MASK_TX_DS ? 1 : 0);
- uart_send("; MAX_RT: ");
- uart_send(configRegister & 1<<MASK_MAX_RT ? 1 : 0);
- uart_sendl("");
- gpreg = nrf_getreg(SETUP_RETR);
- uart_send(" Retry delay: ");
- uart_send((int)(((gpreg >> 4)+1) * 250));// Get the upper 4 bits, 7:4 in nordic nomenclature; 250ms per code value
- uart_send("us; max retries: ");
- uart_send(gpreg & 0xF); // Get the lower 4 bits; this represents the 0-16 retry count directly.
- uart_send("; Addr len: ");
- uart_send(nrf_getreg(SETUP_AW) + 2);
- uart_send(" bytes; ");
- gpreg = nrf_getreg(FEATURE);
- uart_send("\r\n Enable dyn payload len: ");
- uart_send(gpreg & 1<<EN_DPL ? 1 : 0);
- uart_send("; Enable acknowledge payload: ");
- uart_send(gpreg & 1<<EN_ACK_PAY ? 1 : 0);
- uart_send("; Enable tx of no-ack pkts: ");
- uart_send(gpreg & 1<<EN_DYN_ACK ? 1 : 0);
- uart_sendl("\r\n");
- uart_sendl(" Current status:");
- gpreg = nrf_getreg(STATUS);
- uart_send(" RX Data Rdy: ");
- uart_send(gpreg & 1<<RX_DR ? 1 : 0);
- uart_send("; Pkt Ackn'd: ");
- uart_send(gpreg & 1<<TX_DS ? 1 : 0);
- uart_send("; TX fifo full: ");
- uart_send(gpreg & 1<<TX_FULL ? 1 : 0);
- uart_send("\r\n Data avail on pipe: ");
- uint8_t rxPVal = (gpreg & 0xE)>>1;
- if(rxPVal > 6){
- uart_sendl("None");
- } else {
- uart_sendl(rxPVal);
- }
- gpreg = nrf_getreg(FIFO_STATUS);
- uart_send(" Reuse TX payload: ");
- uart_send(gpreg & 1<<TX_REUSE ? 1 : 0);
- uart_send("; TX fifo full: ");
- uart_send(gpreg & 1<<FIFO_FULL ? 1 : 0);
- uart_send("; TX fifo empty: ");
- uart_send(gpreg & 1<<TX_EMPTY ? 1 : 0);
- uart_send("; RX fifo full: ");
- uart_send(gpreg & 1<<RX_FULL ? 1 : 0);
- uart_send("; RX fifo empty: ");
- uart_send(gpreg & 1<<RX_EMPTY ? 1 : 0);
- uart_sendl("");
- uint8_t enaa = nrf_getreg(EN_AA);
- uint8_t enrxaddr = nrf_getreg(EN_RXADDR);
- uint8_t enDynPlLen = nrf_getreg(DYNPD);
- uart_sendl("\r\n Per-pipe configuration:\r\n\tRX_ADDR\tEN_AA\tERX\tDPL\tRX_PW");
- for(uint8_t i = 0; i < 6; i++){
- uart_send(" ");
- uart_send(i);
- uart_send(":\t");
- uart_send_hex(nrf_getreg(RX_ADDR_P0 + i));
- uart_send("\t");
- uart_send(enaa & 1<<i ? 1 : 0);
- uart_send("\t");
- uart_send(enrxaddr & 1<<i ? 1 : 0);
- uart_send("\t");
- uart_send(enDynPlLen & 1<<i ? 1 : 0);
- uart_send("\t");
- uart_sendl(nrf_getreg(RX_PW_P0 + i));
- }
- uart_sendl("\r\nEnd of nRF24L01 configuration\r\n");
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement