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
- #define UART_BUFFER_LEN 150
- 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_old(const char *txt){
- uint8_t index = 0;
- while(txt[index] != 0){
- uart_putchar(txt[index]);
- index++;
- }
- }
- volatile char uart_buffer[UART_BUFFER_LEN] = {0,};
- volatile uint8_t uart_buffer_next = 0;
- volatile uint8_t uart_buffer_len = 0;
- volatile uint8_t fill = 0; // FIXME: rename.
- bool uart_poller_started = false;
- void uart_send(const char *txt){
- uint8_t charIndex = 0;
- if(!uart_poller_started){
- uart_send_old(txt);
- return;
- }
- while(txt[charIndex] != 0){
- fill++;
- if(fill>UART_BUFFER_LEN){
- uart_send_old("UART buffer overflow.\r\n");
- uart_buffer_len = 0;
- uart_buffer_next = 0;
- fill = 0;
- }
- uart_buffer[uart_buffer_len] = txt[charIndex];
- uart_buffer_len++;
- charIndex++;
- if(uart_buffer_len==UART_BUFFER_LEN)uart_buffer_len = 0;
- }
- }
- void uart_async_send_poll(){
- if(!uart_poller_started)uart_sendl("UART Send: Polling...");
- uart_poller_started = true;
- if(uart_buffer_next == uart_buffer_len) return; // Nothing to send
- if(!(UCSR0A & (1<<UDRE0))) return; // Busy sending
- fill--;
- UDR0 = uart_buffer[uart_buffer_next]; // Send character
- uart_buffer_next ++; // Increment the next character to send
- if(uart_buffer_next==UART_BUFFER_LEN)uart_buffer_next=0;
- }
- 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);
- //if(num<10) uart_send("0");
- uart_send(chars);
- }
- void uart_send(uint32_t num){
- char chars[11] = {0}; // Maximum length for decimal string representation of an a uint32_t is 11 chars, as in "4294967296\0"
- uart_int32_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_sendl(uint32_t 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';
- *ptr='\0';
- return;
- }
- if(value<0){
- value*=(-1);
- *ptr++='-';
- }
- if(value<10){
- *ptr++='0'; // This works fine and ensures we get "08" not "8"
- }
- for(temp=value;temp>0;temp/=10,ptr++);
- *ptr='\0';
- for(temp=value;temp>0;temp/=10){
- *--ptr=temp%10+'0';
- }
- }
- void uart_int32_to_decimal(uint32_t value, char *ptr){
- uint32_t 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 uart_hexchars[]="0123456789ABCDEF";
- void uart_int_to_hex(uint8_t value, char *ptr){
- ptr[0] = (uart_hexchars[(value>>4)&0xf]);
- ptr[1] = (uart_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 false // 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.
- // FIXME: Make sure this isn't actually active so as not to screw up timecode timing.
- /*
- 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(); // TODO FIXME: Dangerous!
- }
- //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.
- }
- // ******************************************
- //
- // 7-segment display
- //
- // ******************************************
- const uint8_t PROGMEM display_alpha[] = {
- // gfedcb.a
- 0b01111101, // 48 0
- 0b00001100, // 49 1
- 0b10110101, // 50 2
- 0b10011101, // 51 3
- 0b11001100, // 52 4
- 0b11011001, // 53 5
- 0b11111001, // 54 6
- 0b00001101, // 55 7
- 0b11111101, // 56 8
- 0b11011101, // 57 9
- // Pad the table for a few chars we're not handling
- // Costs us a few bytes of flash, but makes the code cleaner
- 0, // :
- 0, // ;
- 0, // <
- 0, // =
- 0, // >
- 0, // ?
- 0, // @
- 0b11101101, // 65 A
- 0b11111000, // 66 b
- 0b01110001, // 67 C
- 0b10111100, // 68 d
- 0b11110001, // 69 E
- 0b11100001, // 70 F
- 0b01111001, // 71 G
- 0b11101000, // 72 h (we use a lowercase h to differentiate it from K/X)
- 0b01100000, // 73 I
- 0b00011100, // 74 J
- 0b11101100, // 75 K (may be confused with X or H)
- 0b01110000, // 76 L
- 0b00101001, // 77 M (not really doable, implemented as segs A, C and E, inverted W)
- 0b01101101, // 78 N
- 0b01111101, // 79 O
- 0b11100101, // 80 P
- 0b11001101, // 81 q (possibly confusable with the 9 produced by some display drivers, but this number font illuminates segment D as well)
- 0b10100000, // 82 r
- 0b11011001, // 83 S (may be confused with number 5)
- 0b11100000, // 84 t (not that great)
- 0b01111100, // 85 U (may be confused with V)
- 0b01111100, // 86 V (may be confused with U)
- 0b01010100, // 87 W (not really doable, implemented as segs B, D and F, inverted M)
- 0b11101100, // 88 X (may be confused with K or H)
- 0b11011100, // 89 Y
- 0b10110101, // 90 Z (may be confused with number 2)
- };
- // 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 4
- 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(){
- char eights[] = "88888888";
- display_show(eights);
- _delay_ms(500);
- display_clear();
- }
- void display_show(const char *txt){
- display_load_text(txt);
- display_latch();
- }
- void display_load_text(const char *txt){
- spi_select(SPI_DEVICE_DSPLY);
- uint8_t i = 7;
- do{
- //uart_send("chr$: ");
- //uart_sendl(i);
- if(txt[i] >= '0' && txt[i] <= 'Z'){ // 0 = char 48
- spi_transact(pgm_read_byte_near(display_alpha + (txt[i] - '0')));
- } else {
- spi_transact(0); // Ensures that space, and anything not otherwise handled, becomes a space.
- }
- } while (i--);
- spi_deselect();
- }
- void display_load_raw(uint8_t * displayData){
- spi_select(SPI_DEVICE_DSPLY);
- uint8_t i = 7;
- do{
- spi_transact(displayData[i]);
- } while (i--);
- 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();
- }
- // ******************************************
- //
- // SMPTE-12M Timecode
- //
- // ******************************************
- typedef enum {
- UNKNOWN,
- R23976,
- R24,
- R25,
- R2997,
- R30,
- R5994,
- R50,
- R60
- } smpte_framerate;
- const char* smpte_framerate_names[] = {
- "Resolving...",
- "23.976",
- "24",
- "25",
- "29.970",
- "30",
- "50",
- "59.940",
- "60"
- };
- /*
- * struct _Object { int (*methodptr)(...); }
- * int myimpl(...) {;} o.methodptr=&myimpl;
- * https://en.cppreference.com/w/c/variadic
- * https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work
- */
- struct smpte_timecode{
- uint8_t hours;
- uint8_t mins;
- uint8_t secs;
- uint8_t frames;
- bool dropframe;
- uint8_t ubits[4];
- bool fwd;
- smpte_framerate framerate;
- uint32_t frametime;
- };
- uint8_t smpte_framerate_maxframes[] = {
- 0, 23, 23, 24, 29, 29, 49, 59, 59
- };
- bool smpte_compare(struct smpte_timecode *code1, struct smpte_timecode *code2){ // FIXME: Needs "struct" due to Arduino strangeness
- return
- code1->hours == code2->hours &&
- code1->mins == code2->mins &&
- code1->secs == code2->secs &&
- code1->frames == code2->frames &&
- code1->dropframe == code2->dropframe &&
- code1->framerate == code2->framerate;
- }
- void smpte_ubits_tostring(struct smpte_timecode *code, char *chars){ // FIXME: Needs "struct" due to Arduino strangeness
- uart_array_to_hex(code->ubits, 8, chars);
- }
- void smpte_tostring(struct smpte_timecode *code, char *chars){ // FIXME: Needs "struct" due to Arduino strangeness
- // 01:02:03:04 1A2B3C4D 29.97 D\0 == 30 chars
- for(uint8_t i = 0; i < 20; i++){
- chars[i] = 0;
- }
- uart_int_to_decimal(code->hours, chars);
- chars[2] = ':';
- uart_int_to_decimal(code->mins, chars+3);
- chars[5] = ':';
- uart_int_to_decimal(code->secs, chars+6);
- chars[17] = ' ';
- if(code->dropframe){
- chars[8] = ';';
- chars[18] = 'D';
- } else {
- chars[8] = ':';
- chars[18] = ' ';
- }
- uart_int_to_decimal(code->frames, chars+9);
- chars[11] = ' ';
- chars[14] = '.';
- chars[19] = '\0';
- switch(code->framerate){
- case R23976:
- chars[12] = '2';
- chars[13] = '3';
- chars[15] = '9';
- chars[16] = '7';
- break;
- case R24:
- chars[12] = '2';
- chars[13] = '4';
- chars[15] = '0';
- chars[16] = '0';
- break;
- case R25:
- chars[12] = '2';
- chars[13] = '5';
- chars[15] = '0';
- chars[16] = '0';
- break;
- case R2997:
- chars[12] = '2';
- chars[13] = '9';
- chars[15] = '9';
- chars[16] = '7';
- break;
- case R30:
- chars[12] = '3';
- chars[13] = '0';
- chars[15] = '0';
- chars[16] = '0';
- break;
- case R50:
- chars[12] = '5';
- chars[13] = '0';
- chars[15] = '0';
- chars[16] = '0';
- break;
- case R5994:
- chars[12] = '5';
- chars[13] = '9';
- chars[15] = '9';
- chars[16] = '4';
- break;
- case R60:
- chars[12] = '6';
- chars[13] = '0';
- chars[15] = '0';
- chars[16] = '0';
- break;
- default:
- chars[12] = 'W';
- chars[13] = 'I';
- chars[14] = 'L';
- chars[15] = 'D';
- chars[16] = ' ';
- break;
- }
- }
- void smpte_increment(struct smpte_timecode *code){ // FIXME: Needs "struct" due to Arduino strangeness
- if(code->framerate == UNKNOWN){
- uart_sendl("ERROR: Can't increment, unknown rate.");
- return;
- }
- if(code->frames == smpte_framerate_maxframes[code->framerate]){
- if(code->dropframe && code->secs == 59 && ((code->mins +1) % 10 != 0)){
- if(code->framerate == R2997 || code->framerate == R30){
- code->frames = 2; // 29.97 or 30.00 drop
- } else {
- code->frames = 4; // 59.94 or 60.00 drop
- }
- } else {
- code->frames = 0;
- }
- if(code->secs == 59){
- code->secs = 0;
- if(code->mins == 59){
- code->mins = 0;
- if(code->hours == 23){
- code->hours = 0;
- } else {
- code->hours ++;
- }
- } else {
- code->mins ++;
- }
- } else {
- code->secs ++;
- }
- } else {
- code->frames ++;
- }
- }
- // ******************************************
- //
- // SMPTE-12M Linear Timecode Reader
- //
- // ******************************************
- // NB wiggle may have to be very significantly larger to make analogue tape work at all.
- // Wiggle room is in F_CPU clocks plus or minus, 5 = +/- 5 clocks
- #include <util/atomic.h>
- // Constants
- #define LTC_MAGIC_A_REV 0b00111111
- #define LTC_MAGIC_B_REV 0b11111101
- #define LTC_MAGIC_A 0b11111100
- #define LTC_MAGIC_B 0b10111111
- #define LTC_BITS_PER_FRAME 80UL
- // Settings
- // 6 = sync over 64 frames, etc
- #define LTC_JAM_FRAMES_SHIFTFACTOR 6
- #define LTC_BITPERIOD_AVG_LEN 64
- #define LTC_BITPERIOD_AVG_SPACING 28
- #define LTC_BITPERIOD_AVG_ADJUST 1.25
- #define LTC_BITPERIOD_TIMEOUT_SAFETY_FACTOR 1.2
- uint8_t ltc_frame_duration_wiggle = 20;
- uint8_t ltc_legal_period_wiggle = 10;
- bool ltc_monotonic = true;
- bool ltc_display_ubits = false;
- uint8_t ltc_required_sequence = 3;
- bool ltc_use_biphase_mark_flag = true;
- // Decoder internal state
- uint16_t ltc_bitperiods[LTC_BITPERIOD_AVG_LEN];
- uint8_t ltc_bitperiod_avgcounter = 0;
- uint8_t ltc_bitperiod_avgindex = 0;
- uint32_t ltc_bitperiod_sum = 0;
- uint8_t ltc_buf[11] = {0,}; // 11 so we can shift easily
- uint8_t ltc_buf2[11] = {0,}; // 11 so we can shift easily
- uint8_t *ltc_readbuffer_ptr = ltc_buf;
- uint8_t *ltc_writebuffer_ptr = ltc_buf2;
- uint8_t ltc_goodframes = 0;
- volatile bool ltc_short_wait_flag;
- // Generator
- uint8_t ltc_gen_bitidx;
- uint16_t ltc_gen_zero_length;
- uint16_t ltc_gen_one_length_1;
- uint16_t ltc_gen_one_length_2;
- uint16_t ltc_gen_dither_qty;
- uint16_t ltc_gen_dither_counter;
- uint8_t ltc_gen_dither_adds;
- uint8_t ltc_gen_buf_1[11];
- uint8_t ltc_gen_buf_2[11];
- uint8_t *ltc_gen_buf_rptr = ltc_gen_buf_1;
- uint8_t *ltc_gen_buf_wptr = ltc_gen_buf_2;
- bool ltc_gen_short_wait_flag = false;
- smpte_timecode ltc_gen_tc_working;
- bool ltc_gen_needs_framedata = false;
- uint8_t ltc_gen_dither_accumulator;
- uint16_t ltc_gen_group_count;
- // Timing
- volatile uint16_t ltc_frame_start_time = 0;
- volatile uint16_t ltc_last_edge_time = 0;
- volatile uint16_t ltc_frame_timer_overflows;
- volatile uint32_t ltc_frametime_now = 0;
- volatile smpte_framerate ltc_framerate_now;
- volatile uint16_t ltc_max_bp;
- volatile uint16_t ltc_min_bp;
- volatile uint16_t ltc_bitperiod_threshold;
- volatile uint16_t ltc_interrupt_delay;
- smpte_timecode ltc_timecode_previous;
- smpte_timecode ltc_timecode_current;
- uint32_t ltc_jam_timer;
- uint32_t ltc_jam_timer_overflows;
- bool ltc_jamming = true;
- uint16_t ltc_jam_framecount;
- uint16_t ltc_jam_last_check;
- uint16_t ltc_jam_start_time;
- uint16_t ltc_frame_timeout;
- // Calibrated pseudo-constants
- uint16_t ltc_bitperiod_longest;
- uint16_t ltc_bitperiod_shortest;
- uint32_t ltc_fr_23976_min;
- uint32_t ltc_fr_23976_max;
- uint32_t ltc_fr_24_min;
- uint32_t ltc_fr_24_max;
- uint32_t ltc_fr_25_min;
- uint32_t ltc_fr_25_max;
- uint32_t ltc_fr_2997_min;
- uint32_t ltc_fr_2997_max;
- uint32_t ltc_fr_30_min;
- uint32_t ltc_fr_30_max;
- uint32_t ltc_fr_50_min;
- uint32_t ltc_fr_50_max;
- uint32_t ltc_fr_5994_min;
- uint32_t ltc_fr_5994_max;
- uint32_t ltc_fr_60_min;
- uint32_t ltc_fr_60_max;
- #define LTC_REV_BITS(v) \
- ({ \
- uint8_t val = v; \
- uint8_t res; \
- __asm__ \
- ( \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- "rol %0" "\n\t" \
- "ror %1" "\n\t" \
- : "=&d" (val), "=r" (res) \
- : "0" (val) \
- ); \
- res; \
- })
- // 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: ");
- uart_sendl(str);
- }
- void ltc_decoder_init(){
- ltc_bitperiod_threshold = ltc_bitperiod_longest - (ltc_bitperiod_shortest >> 1);
- ltc_max_bp = 0;
- ltc_min_bp = 0xFFFFUL;
- ltc_framerate_now = UNKNOWN;
- ltc_frametime_now = 0;
- ltc_short_wait_flag = false;
- ltc_goodframes = 0;
- ltc_gen_begin_jam(); // Basically just resets a bunch of stuff so we'll start jamming on good code.
- display_show("NO CODE");
- }
- void ltc_init(){
- ltc_log("Set up...");
- if(ltc_monotonic){
- ltc_log("Decoder: Monotonic");
- } else {
- ltc_log("Decoder: Wild");
- }
- ltc_init_calibrated(16000000);
- // 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
- // Stuff for LTC output. Set PD5 (OC0B) as output so that it can be toggled on compare match.
- DDRD |= (1 << 5);
- // Set up ADC/AC hardware
- ADCSRA &= ~(1<<ADEN); // Disable the ADC. All else is zeros. You'd write a 1 to ADSC to start an ADC conversion. Other bits enable conversion-complete interrupts etc.
- DIDR1 = (1<<AIN1D) | (1<AIN0D); // Disable the digital input buffer on the AIN1/0 pins to reduce current consumption
- // Analog comparator control and status register
- ACSR = (1<<ACIC); // Bits 3 and 2 - enable interrupt. ACIE - AC interrupt enable. ACIC - AC input capture enable. This is absolutely required for LTC decode.
- // 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.
- // ACIE (3) Interrupt enable. Don't actually need this; we go on the timer input capture interrupt, not the AC interrupt.
- // ACIS[1:0] Zeroed - comparator interrupt on output toggle. Don't use this as we rely on the timer input capture interrupt not the AC interrupt.
- // Set up timer 1
- TCCR1A = 0x00; // Explicitly zero the register to avoid shenanigans
- TCCR1A |= (1 << COM0B0); // Bits 5 and 4 of TCCR1A are COM0Bn, "compare output mode for channel B." Toggle OC0B on compare match = 0b01.
- // The below needs to stay off until we want to output jammed code.
- //TCCR1A |= (1 << WGM01); // Bits 0 and 1 (WGM00, WGM01) set CTC mode. WGM02 is involved in other timer modes, but it's in TCCR1B and we don't need it.
- OCR1A = 1000000;
- TIMSK1 |= (1 << 1);
- // This is stuff used to decode LTC in EXT mode
- TCCR1B = 0x00; // Explicitly zero the register to avoid shenanigans
- TCCR1B |= ((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
- (0<<CS11) | // Flip this to 1 for /64
- (0<<CS12)); // NB these are in reverse order.
- TIMSK1 = 0x00; // Explicitly zero the register to avoid shenanigans
- TIMSK1 |= (1<<ICIE1); // Enable input capture interrupt, which is the one used by the AC.
- TIMSK1 |= (1<<1);
- ltc_decoder_init();
- }
- void ltc_init_calibrated(uint32_t calibratedClockSpeed){
- ltc_bitperiod_longest = ((((calibratedClockSpeed/1000L)*1001L)/24L)/LTC_BITS_PER_FRAME) + ltc_legal_period_wiggle;
- ltc_bitperiod_shortest = ((calibratedClockSpeed/60L)/160L) - ltc_legal_period_wiggle;
- ltc_fr_23976_min = (((calibratedClockSpeed/1000L)*1001L)/24L) - (uint32_t)ltc_frame_duration_wiggle;
- ltc_fr_23976_max = (((calibratedClockSpeed/1000L)*1001L)/24L) + (uint32_t)ltc_frame_duration_wiggle;
- ltc_fr_24_min = (calibratedClockSpeed/24) - ltc_frame_duration_wiggle;
- ltc_fr_24_max = (calibratedClockSpeed/24) + ltc_frame_duration_wiggle;
- ltc_fr_25_min = (calibratedClockSpeed/25) - ltc_frame_duration_wiggle;
- ltc_fr_25_max = (calibratedClockSpeed/25) + ltc_frame_duration_wiggle;
- ltc_fr_2997_min = (((calibratedClockSpeed/1000L)*1001L)/30L) - (uint32_t)ltc_frame_duration_wiggle;
- ltc_fr_2997_max = (((calibratedClockSpeed/1000L)*1001L)/30L) + (uint32_t)ltc_frame_duration_wiggle;
- ltc_fr_30_min = (calibratedClockSpeed/30) - ltc_frame_duration_wiggle;
- ltc_fr_30_max = (calibratedClockSpeed/30) + ltc_frame_duration_wiggle;
- ltc_fr_50_min = (calibratedClockSpeed/50) - ltc_frame_duration_wiggle;
- ltc_fr_50_max = (calibratedClockSpeed/50) + ltc_frame_duration_wiggle;
- ltc_fr_5994_min = (((calibratedClockSpeed/1000L)*1001L)/60L) - (uint32_t)ltc_frame_duration_wiggle;
- ltc_fr_5994_max = (((calibratedClockSpeed/1000L)*1001L)/60L) + (uint32_t)ltc_frame_duration_wiggle;
- ltc_fr_60_min = (calibratedClockSpeed/60) - ltc_frame_duration_wiggle;
- ltc_fr_60_max = (calibratedClockSpeed/60) + ltc_frame_duration_wiggle;
- ltc_frame_timeout = ((calibratedClockSpeed/24)/LTC_BITS_PER_FRAME) * LTC_BITPERIOD_TIMEOUT_SAFETY_FACTOR;
- }
- smpte_framerate ltc_get_framerate(uint32_t frame_duration); // Prototype. Arduino IDE screws with enums for some reason
- smpte_framerate ltc_get_framerate(uint32_t frame_duration){
- // FIXME: Should this deal with the concept of a user-selected framerate,
- // and showing some sort of error if the incoming code isn't at that rate?
- // TODO: Averaging for flaky signals?
- if(frame_duration > ltc_fr_23976_min && frame_duration < ltc_fr_23976_max){
- return R23976;
- } else if(frame_duration > ltc_fr_24_min && frame_duration < ltc_fr_24_max){
- return R24;
- } else if(frame_duration > ltc_fr_25_min && frame_duration < ltc_fr_25_max){
- return R25;
- } else if(frame_duration > ltc_fr_2997_min && frame_duration < ltc_fr_2997_max){
- return R2997;
- } else if(frame_duration > ltc_fr_30_min && frame_duration < ltc_fr_30_max){
- return R30;
- } else if(frame_duration > ltc_fr_50_min && frame_duration < ltc_fr_50_max){
- return R50;
- } else if(frame_duration > ltc_fr_5994_min && frame_duration < ltc_fr_5994_max){
- return R5994;
- } else if(frame_duration > ltc_fr_60_min && frame_duration < ltc_fr_60_max){
- return R60;
- } else {
- return UNKNOWN;
- }
- }
- void ltc_event_poll(){
- if(ltc_gen_needs_framedata){
- ltc_gen_setup_next_frame(); // This is required to kick things off
- smpte_log_code(<c_gen_tc_working);
- }
- if(ltc_frametime_now > 0){
- ltc_new_frame();
- ltc_frametime_now = 0;
- }
- if(ltc_goodframes < ltc_required_sequence) return;
- uint16_t duration;
- uint16_t last;
- ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
- last = ltc_last_edge_time;
- }
- uint16_t now = TCNT1;
- if(now > last){
- duration = now - last;
- } else {
- duration = now + (0xFFFFUL - last);
- }
- if(duration > ltc_frame_timeout){
- ltc_desync();
- }
- }
- // *******
- // Reader
- // *******
- void ltc_set_bitperiod_threshold(uint16_t this_edge_duration){
- if(ltc_monotonic){
- // TODO: Reset when changing FR?
- ltc_max_bp = ltc_max_bp > this_edge_duration ? ltc_max_bp : this_edge_duration;
- ltc_min_bp = ltc_min_bp < this_edge_duration ? ltc_min_bp : this_edge_duration;
- ltc_bitperiod_threshold = ltc_max_bp - ((ltc_max_bp - ltc_min_bp) >> 1);
- } else {
- // FIXME: None of this gets reset in init() but it doesn't matter much -
- // it just means it'll take fractionally (and really fractionally) longer
- // to lock up if the averages aren't right for the incoming framerate.
- if(ltc_bitperiod_avgcounter == LTC_BITPERIOD_AVG_SPACING){
- ltc_bitperiod_avgcounter = 0;
- ltc_bitperiods[ltc_bitperiod_avgindex] = this_edge_duration;
- ltc_bitperiod_sum += this_edge_duration;
- ltc_bitperiod_avgindex ++;
- if(ltc_bitperiod_avgindex == LTC_BITPERIOD_AVG_LEN){
- ltc_bitperiod_avgindex = 0;
- }
- ltc_bitperiod_sum -= ltc_bitperiods[ltc_bitperiod_avgindex];
- ltc_bitperiod_threshold = ltc_bitperiod_sum >> 6; // Divide number by value in LTC_BITPERIOD_AVG_LEN
- ltc_bitperiod_threshold *= LTC_BITPERIOD_AVG_ADJUST;
- } else {
- ltc_bitperiod_avgcounter ++;
- }
- }
- }
- ISR (TIMER1_COMPA_vect) {
- OCR1A = ltc_gen_get_next_transition() - 1; // Subtract one because the timers always runs one clock longer than the value sent.
- }
- ISR(TIMER1_CAPT_vect){
- // Flip looking for rising or falling edges
- TCCR1B ^= (1<<ICES1);
- // Figure out time since last edge
- uint16_t this_edge_duration;
- if(ICR1 > ltc_last_edge_time){
- this_edge_duration = ICR1 - ltc_last_edge_time;
- } else {
- ltc_frame_timer_overflows++; // TODO: If this overflows we're horrifically out of time and should bomb out
- this_edge_duration = ICR1 + (0xFFFFUL - ltc_last_edge_time);
- }
- if(ltc_jamming){
- if(ltc_jam_framecount == 0){
- // Begin a new jam
- ltc_jam_start_time = ICR1;
- ltc_jam_last_check = ICR1;
- } else {
- // We're in a jam (ho ho ho)
- if(ICR1 < ltc_jam_last_check){
- ltc_jam_timer_overflows ++;
- }
- }
- }
- ltc_jam_last_check = ICR1;
- if(this_edge_duration > ltc_bitperiod_longest || this_edge_duration < ltc_bitperiod_shortest){ // FIXME: Be more specific about per-framerate legal bit periods.
- // Reject this bit period as not part of a valid monotonic timecode signal (just noise, or a non-TC audio signal)
- ltc_desync();
- return;
- }
- ltc_last_edge_time = ICR1;
- ltc_jam_last_check = ICR1;
- ltc_set_bitperiod_threshold(this_edge_duration);
- // 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. Push the result to the buffer.
- if(this_edge_duration > ltc_bitperiod_threshold){
- // It's a zero. Zero the last bit in the buffer.
- ltc_writebuffer_ptr[9] &= ~(0x80);
- if(ltc_short_wait_flag){
- ltc_short_wait_flag = false; // Should never get a long after just one short
- ltc_desync();
- return;
- }
- } else {
- // It's possibly part of a 1
- if(ltc_short_wait_flag){
- // It's a one. Set the last bit in the buffer.
- ltc_writebuffer_ptr[9] |= 0x80;
- ltc_short_wait_flag = false;
- } else {
- // It's presmably the beginning of a one
- ltc_short_wait_flag = true;
- return;
- }
- }
- // Check to see if we've found a full frame
- if((ltc_writebuffer_ptr[8] == LTC_MAGIC_A && ltc_writebuffer_ptr[9] == LTC_MAGIC_B)
- || (ltc_writebuffer_ptr[0] == LTC_MAGIC_B_REV && ltc_writebuffer_ptr[1] == LTC_MAGIC_A_REV)){
- // Display the next TC as early as possible
- if(ltc_goodframes == ltc_required_sequence) display_latch();
- // Get an idea of how long it takes from the interrupt firing to get to here.
- ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
- ltc_interrupt_delay = TCNT1;
- }
- if(ltc_interrupt_delay > ICR1){
- ltc_interrupt_delay -= ICR1;
- } else {
- ltc_interrupt_delay = ltc_interrupt_delay + (0xFFFFUL - ICR1);
- }
- // Calculate the precision frame duration
- ltc_frametime_now = 0xFFFFUL * ltc_frame_timer_overflows - ltc_frame_start_time + ICR1;
- ltc_frame_timer_overflows = 0;
- ltc_frame_start_time = ICR1;
- // If we've finished a jam, signal completion. If not, and we're jamming, increment the jam frame counter
- /*
- * At an appropriate instant, turn off the analog comparator+timer capture interrupt, turn on CTC mode, turn on output
- * pin toggling, zero the timer and get a new value for it from ltc_gen_get_next_transition. Set that
- * to be the CTC mode trip point, and in a new ISR keep calling ltc_gen_get_next_transition about 110
- * times a frame.
- *
- */
- if(ltc_jam_framecount == (1<<LTC_JAM_FRAMES_SHIFTFACTOR)){
- //TCCR1A |= (1 << WGM01);
- TCCR1B |= (1 << WGM12); // TODO this will need tweaking for proper phasing
- ltc_gen_tc_working = ltc_timecode_current;
- ltc_jamming = false;
- //uart_send("Ovf:");
- //uart_sendl(ltc_jam_timer_overflows);
- // FIXME: the addition of jam_timer_overflows is experimental as it seemed suspiciously close to the required kludge
- // It probably still counts as a kludge as we've no idea why we'd be that far out, but if it works... though we don't know it works yet.
- //ltc_jam_timer = 0xFFFFUL * ltc_jam_timer_overflows - ltc_jam_start_time + ICR1 + ltc_jam_timer_overflows;
- ltc_jam_timer = ((0xFFFFUL * ltc_jam_timer_overflows) - ltc_jam_start_time) + ICR1;
- } else {
- if(ltc_jamming) ltc_jam_framecount++;
- }
- // Swap buffers
- if (ltc_readbuffer_ptr == ltc_buf){
- ltc_readbuffer_ptr = ltc_buf2;
- ltc_writebuffer_ptr = ltc_buf;
- } else {
- ltc_readbuffer_ptr = ltc_buf;
- ltc_writebuffer_ptr = ltc_buf2;
- }
- }
- // Shuffle the buffer
- for(uint8_t i = 0; i < 10; i++){ // Remember we made an extra byte on the end we don't need?
- ltc_writebuffer_ptr[i] = (ltc_writebuffer_ptr[i] >> 1) | (ltc_writebuffer_ptr[i+1] << 7);
- }
- }
- void ltc_desync(){
- if(ltc_goodframes < ltc_required_sequence) return; // can't desync when not synced. This really just guards against multiple hits.
- if(ltc_monotonic) display_show("NO CODE");
- ltc_log("Unlocked");
- ltc_decoder_init();
- // TODO: some sort of function pointer for this "event?"
- }
- void ltc_sync(){
- uart_send("LTC: Lock: ");
- // TODO: some sort of function pointer for this "event?"
- // TODO: Should this be guarded against double calling? It never seems to be a problem.
- uart_send(smpte_framerate_names[ltc_timecode_current.framerate]);
- if(ltc_timecode_current.dropframe == true){
- uart_sendl(" DF");
- } else {
- if(ltc_timecode_current.framerate == R2997 ||
- ltc_timecode_current.framerate == R30 ||
- ltc_timecode_current.framerate == R5994 ||
- ltc_timecode_current.framerate == R60){
- uart_sendl(" ND");
- } else {
- uart_send("\r\n");
- }
- }
- }
- void ltc_reverse_buffer(uint8_t *buf){
- uint8_t revbuffer[10]; // This will hold the reversed data
- for(uint8_t i = 0; i < 10; i++){
- revbuffer[i] = LTC_REV_BITS(buf[9-i]); // NB LTC_REV_BITS is known good
- }
- for(uint8_t j = 0; j < 10; j++){
- buf[j] = revbuffer[j];
- }
- }
- // FIXME: Note that this will always return timecode structs marked
- // with the currently-detected framerate at the time of unpacking. This
- // might easily not represent the framerate at which this specific
- // LTC buffer was captured.
- void ltc_unpack_frame(struct smpte_timecode *code, uint8_t *buf){ // FIXME: Needs "struct" due to Arduino strangeness
- code->hours = (10*(buf[7] & 0b00000011)) + (buf[6] & 0b00001111); // Other bits in hrs tens are reserved and binary group flag
- code->mins = (10*(buf[5] & 0b00000111)) + (buf[4] & 0b00001111); // Other bit in mins tens is binary group flag
- code->secs = (10*(buf[3] & 0b00000111)) + (buf[2] & 0b00001111); // Other bit in secs tens is biphase mark correction bit
- code->frames = (10*(buf[1] & 0b00000011)) + (buf[0] & 0b00001111); // Other bits in frames tens are drop frame and color frame flags
- code->dropframe = (buf[1] & 0b00000100) == 4;
- code->ubits[3] = ((buf[1] & 0b11110000)) | ((buf[0] & 0b11110000)>>4);
- code->ubits[2] = ((buf[3] & 0b11110000)) | ((buf[2] & 0b11110000)>>4);
- code->ubits[1] = ((buf[5] & 0b11110000)) | ((buf[4] & 0b11110000)>>4);
- code->ubits[0] = ((buf[7] & 0b11110000)) | ((buf[6] & 0b11110000)>>4);
- code->framerate = ltc_get_framerate(ltc_frametime_now);
- code->frametime = ltc_frametime_now;
- }
- void ltc_new_frame(){
- if(!ltc_jamming && ltc_jam_framecount == (1<<LTC_JAM_FRAMES_SHIFTFACTOR)){
- ltc_calc_gen_constants();
- ltc_jam_framecount = 0;
- }
- ltc_timecode_current.fwd = ltc_readbuffer_ptr[0] != LTC_MAGIC_B_REV || ltc_readbuffer_ptr[1] != LTC_MAGIC_A_REV;
- if(!ltc_timecode_current.fwd){
- ltc_reverse_buffer(ltc_readbuffer_ptr);
- }
- ltc_unpack_frame(<c_timecode_current, ltc_readbuffer_ptr);
- if(smpte_compare(<c_timecode_current, <c_timecode_previous)){
- if(ltc_goodframes < ltc_required_sequence) {
- ltc_goodframes ++;
- if(ltc_goodframes == ltc_required_sequence){
- ltc_sync();
- }
- }
- } else {
- ltc_desync(); // This will set goodframes to 0
- }
- smpte_increment(<c_timecode_current); // ltc_timecode_current now indicates the frame we're actually in as this function runs
- ltc_timecode_previous = ltc_timecode_current;
- if(ltc_monotonic || ltc_goodframes == ltc_required_sequence){
- // If we're monotonic, or if we're wild and we have synced code, load two-up and wait to latch on the next sync word
- smpte_increment(<c_timecode_current); // Increment again, so that when we get to the end of THIS frame, ltc_timecode_current will indicate the next frame.
- ltc_preload_display(<c_timecode_current);
- } else {
- // If we're wild (not-monotonic) and don't have synced code, display immediately, because it's all a bit up in the air.
- ltc_preload_display(<c_timecode_current);
- display_latch();
- }
- // FIXME: TEST: Every time we get a new frame, read out all the transitions for the one we've buffered in the generator for test.
- /*if(ltc_jam_timer > 0 && ltc_jamming == false){
- uart_sendl("Jam out:");
- smpte_log_code(<c_gen_tc_working);
- uart_send("\r\nTransitions: ");
- uint8_t transcount = 0;
- //uart_sendl(ltc_gen_needs_framedata);
- while(!ltc_gen_needs_framedata){
- ltc_gen_get_next_transition();
- transcount++;
- }
- uart_sendl(transcount); // STARTHERE: there's going to be some issue with swapping buffers, since we get an 80-period frame when we shouldn't have just sent all the 0s.
- }*/
- }
- void smpte_log_code(struct smpte_timecode *code){
- char tc[20];
- smpte_tostring(code,tc);
- uart_sendl(tc);
- }
- void ltc_preload_display(struct smpte_timecode *code){
- uint8_t preload_nums[8] = {0,};
- if(ltc_display_ubits){
- uart_array_to_hex(code->ubits, 4, preload_nums);
- for(uint8_t i = 0; i< 8; i++){
- preload_nums[i] = pgm_read_byte_near(display_alpha + (preload_nums[i] - 48));
- }
- } else {
- preload_nums[0] = pgm_read_byte_near(display_alpha + (code->hours / 10));
- preload_nums[1] = pgm_read_byte_near(display_alpha + (code->hours % 10)) | 0b00000010;
- preload_nums[2] = pgm_read_byte_near(display_alpha + (code->mins / 10));
- preload_nums[3] = pgm_read_byte_near(display_alpha + (code->mins % 10)) | 0b00000010;
- preload_nums[4] = pgm_read_byte_near(display_alpha + (code->secs / 10));
- preload_nums[5] = pgm_read_byte_near(display_alpha + (code->secs % 10)) | 0b00000010;
- preload_nums[6] = pgm_read_byte_near(display_alpha + (code->frames / 10));
- preload_nums[7] = pgm_read_byte_near(display_alpha + (code->frames % 10));
- }
- display_load_raw(preload_nums);
- }
- // **********
- // Generator
- // **********
- // FIXME? ltc_pack_frame does no checking on the hours tens or minutes tens.
- // if there are more than 2 hours tens or more than 5 minutes tens, this will
- // overwrite junk into the bit flags or generically produce drivel.
- // This can be fixed by more correct masking.
- // FIXME?: We set the biphase mark correction bit to 1 if there's an odd number
- // of ones in the buffer, zero otherwise. It will work either way as a way of stopping
- // the phase of the waveform from toggling, but we're not sure if this is actually
- // to spec. Setting it to zero with an odd number of ones, and one otherwise, would
- // have the same effect. It almost certainly doesn't matter.
- void ltc_pack_frame(struct smpte_timecode *code, uint8_t *buf){
- buf[0] = (code->ubits[3] & 0b00001111) << 4 | (code->frames % 10);
- buf[1] = (code->ubits[3] & 0b11110000) | (code->frames / 10);
- buf[2] = (code->ubits[2] & 0b00001111) << 4 | (code->secs % 10);
- buf[3] = (code->ubits[2] & 0b11110000) | (code->secs / 10);
- buf[4] = (code->ubits[1] & 0b00001111) << 4 | (code->mins % 10);
- buf[5] = (code->ubits[1] & 0b11110000) | (code->mins / 10);
- buf[6] = (code->ubits[0] & 0b00001111) << 4 | (code->hours % 10);
- buf[7] = (code->ubits[0] & 0b11110000) | (code->hours / 10);
- buf[8] = LTC_MAGIC_A;
- buf[9] = LTC_MAGIC_B;
- if(code->dropframe) buf[1] |= 0b00000100;
- if(ltc_use_biphase_mark_flag && ltc_get_ones_in_buffer(buf) & 1){
- buf[3] |= 0b00010000;
- }
- // Behaviour as designed: we do not set bits 43 and 59, because
- // these are intended to indicate the character set of user bit
- // data. This is almost never used, and various sources recommend
- // leaving them at zero which means "no user bits format specified."
- // Setting them to 1 may cause problems with bad readers which
- // assume the minutes-tens and hours-tens values, respectively,
- // are 4 bits wide. Values other than 0 and 1 are formally reserved.
- // Also, we do not set bit 11 which indicates whether the timecode
- // is supposed to be synchronised to a colour field sequence, which
- // is obsolete, irrelevant in the context of almost all modern timecode
- // applications, and may interfere with the decoding of frames-tens
- // value with bad readers. It's trivial to add to buf[1] if we need it.
- // Bit 58 is formally reserved.
- // FIXME: It's possible that some of this has changed in the
- // 50- and 60-frame revisions but we don't know that at the moment.
- // In any case, any receiver would be mad not to support the old version.
- }
- uint8_t ltc_get_ones_in_buffer(uint8_t *buf){
- uint8_t count = 0;
- uint8_t n = 0;
- for(uint8_t i = 0; i < 10; i++){
- n = buf[i];
- while (n){
- count += n & 1;
- n >>= 1;
- }
- }
- //uart_sendl(count);
- return count;
- }
- uint16_t ltc_gen_get_next_transition(){
- uint16_t val;
- if(ltc_gen_short_wait_flag){
- val = ltc_gen_one_length_2;
- ltc_gen_short_wait_flag = false;
- ltc_gen_bitidx ++;
- } else {
- if((ltc_gen_buf_rptr[ltc_gen_bitidx >> 3] & (1<<(ltc_gen_bitidx & 7)))){ // 1<< or 128>>
- // One
- val = ltc_gen_one_length_1;
- ltc_gen_short_wait_flag = true;
- //if(ltc_gen_bitidx==79)ltc_gen_bitidx --; // Make sure we actually get called once more to finish off the short ending transition.
- } else {
- // Zero
- val = ltc_gen_zero_length;
- ltc_gen_bitidx ++;
- }
- // Dither goes here because we only put dither once per bit period,
- // so we don't want dither on both halves of a pair of short transitions
- uint8_t last_dither_accu = ltc_gen_dither_accumulator;
- ltc_gen_dither_accumulator += ltc_gen_dither_adds;
- if(ltc_gen_dither_accumulator < last_dither_accu){ // we rolled over
- if(ltc_gen_dither_counter < ltc_gen_dither_qty){
- val++;
- ltc_gen_dither_counter ++;
- }
- }
- // If we've got to the end of this frame, flip the buffers
- // This assumes that the next frame data was packed into
- // the other buffer by ltc_gen_setup_next_frame() while
- // we were sending out this one
- }
- if(ltc_gen_bitidx == LTC_BITS_PER_FRAME){
- //uart_send(LTC_BITS_PER_FRAME);
- ltc_gen_flip_buffers();
- ltc_gen_bitidx = 0;
- ltc_gen_needs_framedata = true;
- }
- return val;
- }
- void ltc_gen_flip_buffers(){
- if(ltc_gen_buf_wptr == ltc_gen_buf_1){
- ltc_gen_buf_wptr = ltc_gen_buf_2;
- ltc_gen_buf_rptr = ltc_gen_buf_1;
- } else {
- ltc_gen_buf_wptr = ltc_gen_buf_1;
- ltc_gen_buf_rptr = ltc_gen_buf_2;
- }
- }
- // NB assumes the frame we want is the frame subsequent to ltc_gen_tc_working
- // and will increment before packing. Writes to wptr while active. Output frame
- // ends up in rptr (for reading by other code) while wptr is freed up for
- // building the next frame.
- void ltc_gen_setup_next_frame(){
- if(ltc_gen_group_count == (1<<LTC_JAM_FRAMES_SHIFTFACTOR)){
- ltc_gen_dither_accumulator = 0;
- ltc_gen_dither_counter = 0;
- ltc_gen_group_count = 0;
- } else {
- ltc_gen_group_count ++;
- }
- smpte_increment(<c_gen_tc_working);
- ltc_pack_frame(<c_gen_tc_working, ltc_gen_buf_wptr);
- ltc_gen_needs_framedata = false;
- ltc_gen_flip_buffers();
- }
- void ltc_calc_gen_constants(){ // gets called once from new_frame.
- uint16_t total_transitions = (1<<LTC_JAM_FRAMES_SHIFTFACTOR)*LTC_BITS_PER_FRAME;
- //ltc_jam_timer += 680; // Now we add on the number of jam timer overflows in the ISR. No idea how great it is. Trying.
- ltc_gen_zero_length = ltc_jam_timer / total_transitions; // ltc_jam_timer is total sysclocks per n frames, something like 34 million over 64 frames at 30fps
- ltc_gen_one_length_1 = ltc_gen_zero_length >> 1;
- ltc_gen_one_length_2 = ltc_gen_one_length_1;
- if(ltc_gen_zero_length % 2 != 0) ltc_gen_one_length_2 ++; // Handles odd-numbered long tick counts
- ltc_gen_group_count = 0;
- ltc_gen_dither_counter = 0;
- ltc_gen_needs_framedata = false;
- ltc_gen_dither_qty = ltc_jam_timer % total_transitions;
- ltc_gen_setup_next_frame();
- // NB version below will always ceil() the resulting value so we don't run out of bitperiods to add dither to
- ltc_gen_dither_adds = (((256UL * ltc_gen_dither_qty) + (total_transitions/2)) / total_transitions); //((256UL * ltc_gen_dither_qty) / total_transitions);
- uart_send("Jammed: ");
- uart_send((uint32_t)ltc_gen_zero_length);
- uart_send("; s: ");
- uart_send((uint32_t)ltc_gen_one_length_1);
- uart_send("/");
- uart_send((uint32_t)ltc_gen_one_length_2);
- uart_send("; d: ");
- uart_send((uint32_t)ltc_gen_dither_qty);
- uart_send("; adds: ");
- uart_sendl((uint32_t)ltc_gen_dither_adds);
- }
- void ltc_gen_begin_jam(){
- ltc_jam_timer = 0;
- ltc_jam_timer_overflows = 0;
- ltc_jamming = true;
- ltc_jam_framecount = 0; // Will already be 0 if we've jammed before
- ltc_jam_last_check = 0;
- ltc_jam_start_time = 0;
- }
- /*
- * To jam LTC:
- *
- * call ltc_gen_begin_jam. This (among other things) sets ltc_jamming to true so the decoder ISR
- * will begin accumulating timing data. NB: This type of background jamming starts
- * happening on reception of good code, because ltc_decoder_init calls ltc_gen_begin_jam, and
- * ltc_desync calls ltc_decoder_init so we're set up to go the second we sync again.
- *
- * When the jam timing is done, ltc_jamming is set to false but ltc_framecount will contain
- * the number of frames in the jam period. When ltc_event_poller sees ltc_frametime_now != 0,
- * it calls new_frame, and the combination of ltc_jamming==false and a full ltc_framecount will
- * be interpreted as indicating the jam timing is complete and generate a subsequent call to
- * ltc_calc_gen_constants.
- *
- * To update the generator constants again, call ltc_gen_begin_jam and wait until framecount ==
- * requested jam period and jamming is false.
- *
- * When you want to actually start outputting jammed code, put the start timecode in
- * ltc_gen_tc_working, possibly something like this:
- *
- * ltc_gen_tc_working = ltc_timecode_current;
- *
- * That TC will be incremented once by setup_next_frame() before it's output, so bear that in mind.
- *
- * At an appropriate instant, turn off the analog comparator+timer capture interrupt, turn on CTC mode, turn on output
- * pin toggling, zero the timer and get a new value for it from ltc_gen_get_next_transition. Set that
- * to be the CTC mode trip point, and in a new ISR keep calling ltc_gen_get_next_transition about 110
- * times a frame.
- *
- * Be careful about the fact that CTC mode seems to go 1 sysclk longer than you'd think.
- *
- * When ltc_gen_get_next_transition hits the end of a frame, it flips wptr/rptr on the assumption
- * that the other buffer, pointed to by wptr, has had new frame data packed into it. It then sets
- * ltc_gen_needs_frame_data to true.
- *
- * ltc_event_poll will read ltc_gen_needs_frame_data and call
- * ltc_gen_setup_next_frame() as required. This works on the wptr, which is not currently
- * being used by anyone.
- *
- * Repeat until wrap.
- *
- */
- // ******************************************
- //
- // SPI stuff
- //
- // ******************************************
- bool spi_started = false; // Just used to squelch the "glitch" message on startup
- 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
- // Go fairly fast. This yields an SPI clock of 1MHz at F_CPU 16MHz.
- // SPCR |= (1<<SPE) | (1<<MSTR) | (1<<SPR0);
- // Go really fast; SPI clock is 8MHz at F_CPU 16MHz. Does this work with the nRF?
- SPCR |= (1<<SPE) | (1<<MSTR);
- SPSR |= (1<<SPI2X);
- // 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;
- }
- }
- // FIXME: Make async? At least optionally, for non timing critical stuff?
- 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 && spi_started == true){
- uart_sendl("Glitch. Attempted to deselect SPI device when none selected.");
- return;
- }
- spi_started = true;
- 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;
- }
- int main(void){
- uart_init();
- spi_init();
- nrf_init();
- //nrf_read_status(); // This uses a crapton of RAM for all the strings
- display_init();
- ltc_init();
- sei();
- while(1){
- uart_async_send_poll();
- ltc_event_poll();
- }
- }
- // 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