/* Parts of this code were modified from * http://www.d.umn.edu/~cprince/PubRes/Hardware/SPI/ * examples * * Fully interrupt driven SPI slave ADC for OPI * */ /** \file slaveo.c * BMCboard SPI DAQ slave for the Orange PI and RPi */ #include #include "slaveo.h" volatile bool failure = false; volatile uint8_t in_buf1 = 0x19, in_buf2 = 0x57, in_buf3 = 0x07; static uint8_t *tmp_buf4 = (void *) &ha_daq_calib.scaler4; static uint8_t *tmp_buf5 = (void *) &ha_daq_calib.scaler5; volatile bool r_string_ready = false, bmc_string_ready = false, update_bmc_string = false; static void clear_slaveo_flags(void); static void clear_slaveo_flags(void) { serial_buffer_ss.adc_value = false; serial_buffer_ss.dmake_value = false; serial_buffer_ss.dac_value = false; serial_buffer_ss.dget_value = false; serial_buffer_ss.cfg_value = false; serial_buffer_ss.cmake_value = false; serial_buffer_ss.cget_value = false; } /* * setup the interrupt call backs and data structures * For SPI2 using MODE 3, DON'T USE MODE 0 with Orange PI SPI links */ void init_slaveo(void) { uint8_t val = CHECKBYTE; SPI2CON0bits.EN = 0; SPI2_SetRxInterruptHandler(slaveo_rx_isr); SPI2_SetInterruptHandler(slaveo_spi_isr); while (!SPI2STATUSbits.RXRE) { // clear the FIFO of data val = SPI2RXB; } serial_buffer_ss.data[BMC_D1] = val; SPI2STATUSbits.RXRE = 0; SPI2CON2bits.TXR = 1; // FIFO required for transmit SPI2CON0bits.EN = 1; ADC_SelectContext(CONTEXT_1); } /* * DAQ_BMC command processor task */ void slaveo_rx_isr(void) { uint8_t command = 0; uint8_t tmp_buf = 0; /* we only get this when the master wants data, the slave never generates one */ // SPI port #2 SLAVE receiver #ifdef SLAVE_TRACE TP1_SetHigh(); #endif DLED_SetHigh(); #ifdef SLAVE_TIME if (TMR4 == ISR_TIMEMARK) { // ISR cpu usage counter start flag TMR4 = 0; // reset ISR task time counter, 250ns per count T4CONbits.TMR4ON = 1; } #endif report_stat_ss.slave_int_count++; #ifdef SLAVE_DEBUG if (SPI2INTFbits.RXOIF) { spi_stat_ss.rxof_bit++; } #endif data_in2 = SPI2RXB; // store SPI data received for later processing serial_buffer_ss.data[serial_buffer_ss.raw_index] = data_in2; // buffer SPI into protocol data-frame // CHAR_GO_BYTES // use r_string array to buffer ascii data if (serial_buffer_ss.cmake_value) { if (serial_buffer_ss.raw_index == CHAR_GO_BYTES) { BM.spi_reset = 0; if ((serial_buffer_ss.data[BMC_D1] & 0x07) < BMC_EM540_DATA) { // [0..3] UART1 if (!r_string_ready) { if (serial_buffer_ss.data[BMC_D0] == STX) { // character sync serial_buffer_ss.r_string_index = 0; serial_buffer_ss.r_string[serial_buffer_ss.r_string_index] = 0; } else { serial_buffer_ss.r_string[serial_buffer_ss.r_string_index++] = serial_buffer_ss.data[BMC_D0]; serial_buffer_ss.r_string_chan = serial_buffer_ss.data[BMC_D1] & 0x03; if (serial_buffer_ss.r_string_index >= MAX_STRLEN) { serial_buffer_ss.r_string[serial_buffer_ss.r_string_index] = 0; serial_buffer_ss.r_string_index = 0; r_string_ready = true; } } } } else { // [4..7] MEMORY device, sync character for type of data requested, only using channel 4 for comedi link to data switch (serial_buffer_ss.data[BMC_D0]) { case STX: case DC1_CMD: case DC2_CMD: case DC3_CMD: case DC4_CMD: // reset the ASCII CSV buffer serial_buffer_ss.r_string_index = 0; serial_buffer_ss.r_string[serial_buffer_ss.r_string_index] = 0; update_bmc_string = true; // print to log_buffer bmc_string_ready = true; // display bmc data on first line r_string_ready = true; break; default: // does nothing break; } } serial_buffer_ss.cmake_value = false; serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txdone_bit++; // number of completed packets } data_in2 = 0; } // PORT_GO_BYTES if (serial_buffer_ss.dmake_value) { if (serial_buffer_ss.raw_index == PORT_GO_BYTES) { V.bmc_do = serial_buffer_ss.data[BMC_D0]; V.bmc_do += (uint32_t) (serial_buffer_ss.data[BMC_D1] << 8u)&0x0000ff00; data_in2 = 0; serial_buffer_ss.dmake_value = false; serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txdone_bit++; // number of completed packets } data_in2 = 0; } // DAC_GO_BYTES if (serial_buffer_ss.dac_value) { if (serial_buffer_ss.raw_index == DAC_GO_BYTES) { V.bmc_ao = serial_buffer_ss.data[BMC_D0]; serial_buffer_ss.dac_value = false; serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txdone_bit++; // number of completed packets } data_in2 = 0; } // CHAR_GET_BYTES // use r_string array to buffer ascii data if (serial_buffer_ss.cget_value) { if (serial_buffer_ss.raw_index == CHAR_GET_BYTES) { if ((serial_buffer_ss.data[BMC_D1] & 0x07) < BMC_EM540_DATA) { // [0..3] tmp_buf = 0; SPI2TXB = tmp_buf; } else { // [4..7] tmp_buf = 0x57; if (update_bmc_string == true) { // log_buffer has been updated tmp_buf = log_buffer[BMC4.pos++]; } SPI2TXB = tmp_buf; spi_stat_ss.bmc_counts++; } serial_buffer_ss.cget_value = false; serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txdone_bit++; // number of completed packets } else { if ((serial_buffer_ss.data[BMC_D1] & 0x07) < BMC_EM540_DATA) { // [0..3] if (serial_buffer_ss.raw_index == BMC_D0) { tmp_buf = 0x00; } } else { // MEMORY device tmp_buf = log_buffer[BMC4.pos]; } SPI2TXB = tmp_buf; } data_in2 = 0; } // PORT_GET_BYTES if (serial_buffer_ss.dget_value) { if (serial_buffer_ss.raw_index == PORT_GET_BYTES) { tmp_buf = (uint8_t) in_buf3; SPI2TXB = tmp_buf; serial_buffer_ss.dget_value = false; serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txdone_bit++; // number of completed packets } else { if (serial_buffer_ss.raw_index == BMC_D0) { tmp_buf = (uint8_t) in_buf1 | 0b00000001; } else { tmp_buf = (uint8_t) in_buf2; } SPI2TXB = tmp_buf; } data_in2 = 0; } // ADC_GET_BYTES if (serial_buffer_ss.adc_value) { if (serial_buffer_ss.raw_index == ADC_GET_BYTES) { SPI2TXB = spi_stat_ss.daq_conf; // send PCB configuration code serial_buffer_ss.adc_value = false; serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txdone_bit++; // number of completed packets } else { if (serial_buffer_ss.raw_index == BMC_D0) { SPI2TXB = (adc_buffer[channel] &0x00ff); } else { SPI2TXB = ((adc_buffer[channel] >> 8)&0x00ff); } } data_in2 = 0; } // GET_CFG_BYTES if (serial_buffer_ss.cfg_value) { if (serial_buffer_ss.raw_index == CFG_GET_BYTES) { SPI2TXB = CHECKBYTE; serial_buffer_ss.cfg_value = false; serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txdone_bit++; // number of completed packets } else { switch (channel) { case ADC_HV0: // 32-bit float switch (serial_buffer_ss.raw_index) { case BMC_D0: SPI2TXB = (uint8_t) tmp_buf4[0]; break; case BMC_D1: SPI2TXB = (uint8_t) tmp_buf4[1]; break; case BMC_D2: SPI2TXB = (uint8_t) tmp_buf4[2]; break; case BMC_D3: SPI2TXB = (uint8_t) tmp_buf4[3]; break; default: SPI2TXB = CHECKBYTE; break; } break; case ADC_HV1: switch (serial_buffer_ss.raw_index) { case BMC_D0: SPI2TXB = (uint8_t) tmp_buf5[0]; break; case BMC_D1: SPI2TXB = (uint8_t) tmp_buf5[1]; break; case BMC_D2: SPI2TXB = (uint8_t) tmp_buf5[2]; break; case BMC_D3: SPI2TXB = (uint8_t) tmp_buf5[3]; break; default: SPI2TXB = CHECKBYTE; break; } break; case GET_MUI: // 32-bit integer switch (serial_buffer_ss.raw_index) { case BMC_D0: SPI2TXB = (uint8_t) spi_stat_ss.mui; break; case BMC_D1: SPI2TXB = (uint8_t) (spi_stat_ss.mui >> 8); break; case BMC_D2: SPI2TXB = (uint8_t) (spi_stat_ss.mui >> 16); break; case BMC_D3: SPI2TXB = (uint8_t) (spi_stat_ss.mui >> 24); break; default: SPI2TXB = CHECKBYTE; break; } break; case 0x0C: // do ADC calibration functions SPI2TXB = CHECKBYTE; switch (serial_buffer_ss.raw_index) { case BMC_DUMMY: // on the last byte trigger a update cal data and a cal data write ha_daq_calib.c_zero_cal = false; ha_daq_calib.c_scale_cal = false; ha_daq_calib.c_do_cal = true; break; } break; case 0x0: default: switch (serial_buffer_ss.raw_index) { case BMC_D0: SPI2TXB = CHECKBYTE; break; default: SPI2TXB = spi_stat_ss.daq_conf; // respond with DAQ configuration bits break; } break; } } data_in2 = 0; } if (++serial_buffer_ss.raw_index > SPI_BUFFER_LEN - 1) { serial_buffer_ss.raw_index = BMC_CMD; spi_stat_ss.txuf_bit++; // buffer high watermark cleared } if (serial_buffer_ss.dac_value || serial_buffer_ss.dget_value || serial_buffer_ss.dmake_value || serial_buffer_ss.dac_value || serial_buffer_ss.cfg_value || serial_buffer_ss.cget_value || serial_buffer_ss.cmake_value) { TMR0_Reload(); // restart master activity timer counter to prevent system restart goto isr_end; } command = data_in2 & HI_NIBBLE; // Linux bmc_daq driver commands switch (command) { case CMD_DAC_GO: serial_buffer_ss.raw_index = BMC_D0; serial_buffer_ss.dac_value = true; break; case CMD_ADC_GO: channel = data_in2 & LO_NIBBLE; // only 16 possible channels so higher numbers needs to be munged if (channel > AI_CHAN_FIX) { switch (channel) { case 0x6: channel = channel_ANC6; break; case 0x7: channel = channel_ANC7; break; case 0x8: channel = channel_AND5; break; case 0x9: channel = channel_VSS; break; case 0xa: channel = channel_Temp; break; case 0xb: channel = channel_DAC1; break; case 0xc: channel = channel_FVR_Buffer1; break; case 0xd: channel = channel_FVR_Buffer2; break; case 0xe: // SPI bads count reset command channel = channel_BADS; RESET(); break; case 0xf: SPI1CON0bits.EN = 0; MLED_SetHigh(); serial_buffer_ss.raw_index = BMC_CMD; clear_slaveo_flags(); MLED_SetLow(); SPI1CON0bits.EN = 1; channel = channel_ANA0; break; default: channel = channel_ANA0; break; } } serial_buffer_ss.raw_index = BMC_D0; serial_buffer_ss.adc_value = true; break; case CMD_PORT_GET: serial_buffer_ss.raw_index = BMC_D0; serial_buffer_ss.dget_value = true; break; case CMD_PORT_GO: serial_buffer_ss.raw_index = BMC_D0; serial_buffer_ss.dmake_value = true; break; case CMD_CHAR_GET: channel = data_in2 & LO_NIBBLE; // only 16 possible channels serial_buffer_ss.raw_index = BMC_D0; serial_buffer_ss.cget_value = true; break; case CMD_CHAR_GO: channel = data_in2 & LO_NIBBLE; // only 16 possible channels serial_buffer_ss.raw_index = BMC_D0; serial_buffer_ss.cmake_value = true; break; case CMD_DUMMY_CFG: serial_buffer_ss.raw_index = BMC_D0; serial_buffer_ss.cfg_value = true; channel = data_in2 & LO_NIBBLE; // only 16 possible channels so higher numbers needs to be munged tmp_buf4 = (void *) &ha_daq_calib.scaler4; tmp_buf5 = (void *) &ha_daq_calib.scaler5; if (channel == 0x7) { if (!serial_buffer_ss.adc_value && !serial_buffer_ss.dac_value && !serial_buffer_ss.dmake_value && !serial_buffer_ss.dget_value&& !serial_buffer_ss.cmake_value && !serial_buffer_ss.cget_value) { MLED_SetHigh(); clear_slaveo_flags(); MCZ_PWM_SetLow(); MLED_SetLow(); } } break; case CMD_ZERO: default: { static uint8_t val_zero = CHECKBYTE; while (!SPI2STATUSbits.RXRE) { // clear the FIFO of data val_zero = SPI2RXB; } SPI2STATUSbits.RXRE = 0; } break; } TMR0_Reload(); // restart master activity timer counter to prevent system restart isr_end: #ifdef SLAVE_TIME T4CONbits.TMR4ON = 0; // ISR cpu usage counter stop flag #endif DLED_SetLow(); #ifdef SLAVE_TRACE TP1_SetLow(); #endif } void slaveo_spi_isr(void) { spi_stat_ss.spi_error_count++; SPI2INTF = 0; } void slaveo_time_isr(void) { SPI2CON0bits.EN = 0; // reset OPi SPI link module SPI2CON0bits.EN = 1; if (SPI2STATUSbits.TXWE || SPI2STATUSbits.RXRE) { // check for overruns/collisions spi_stat_ss.spi_resets++; MM_ERROR_S; } }