/* This function get the calibration data from the production calibration. * * The calibration data is loaded from flash and stored in the calibration * register. The calibration data reduces the gain error in the adc. * * \param adc Pointer to ADC module register section. */ void ADC_CalibrationValues_Set(ADC_t * adc) { if(&ADCA == adc){ // Get ADCCAL0 from byte address 0x20 (Word address 0x10. adc->CAL = SP_ReadCalibrationByte(0x20); }else { // Get ADCCAL0 from byte address 0x24 (Word address 0x12. adc->CAL = SP_ReadCalibrationByte(0x24); } }
/* Read factory calibration from NVM: */ void read_calibration(void){ //Get the factory temperature value, at 85 Deg. C. ad_temp_ref = SP_ReadCalibrationByte(PRODSIGNATURES_ADCACAL0) ; ad_temp_ref |= SP_ReadCalibrationByte(PRODSIGNATURES_ADCACAL1) <<8; }
// Init everything void init(void) { // clock OSC.CTRL |= OSC_RC32MEN_bm; // turn on 32 MHz oscillator while (!(OSC.STATUS & OSC_RC32MRDY_bm)) { }; // wait for it to start CCP = CCP_IOREG_gc; CLK.CTRL = CLK_SCLKSEL_RC32M_gc; // switch osc DFLLRC32M.CTRL = DFLL_ENABLE_bm; // turn on DFLL // disable JTAG CCP = CCP_IOREG_gc; MCU.MCUCR = 1; // Init pins LED_PORT.OUTCLR = LED_USR_0_PIN_bm | LED_USR_1_PIN_bm | LED_USR_2_PIN_bm; LED_PORT.DIRSET = LED_USR_0_PIN_bm | LED_USR_1_PIN_bm | LED_USR_2_PIN_bm; // Init buttons BTN_PORT.DIRCLR = BTN_PIN_bm; // UARTs usart.set_tx_buffer(usart_txbuf, USART_TX_BUF_SIZE); usart.set_rx_buffer(usart_rxbuf, USART_RX_BUF_SIZE); usart.begin(UART_BAUD_RATE); usart.setup_stream(&usart_stream); usart_n0.set_tx_buffer(usart_n0_txbuf, NODE_TX_BUF_SIZE); usart_n0.set_rx_buffer(usart_n0_rxbuf, NODE_RX_BUF_SIZE); usart_n0.begin(NODE_BAUD_RATE); usart_n[0] = &usart_n0; xgrid.add_node(&usart_n0); usart_n1.set_tx_buffer(usart_n1_txbuf, NODE_TX_BUF_SIZE); usart_n1.set_rx_buffer(usart_n1_rxbuf, NODE_RX_BUF_SIZE); usart_n1.begin(NODE_BAUD_RATE); usart_n[1] = &usart_n1; xgrid.add_node(&usart_n1); usart_n2.set_tx_buffer(usart_n2_txbuf, NODE_TX_BUF_SIZE); usart_n2.set_rx_buffer(usart_n2_rxbuf, NODE_RX_BUF_SIZE); usart_n2.begin(NODE_BAUD_RATE); usart_n[2] = &usart_n2; xgrid.add_node(&usart_n2); usart_n3.set_tx_buffer(usart_n3_txbuf, NODE_TX_BUF_SIZE); usart_n3.set_rx_buffer(usart_n3_rxbuf, NODE_RX_BUF_SIZE); usart_n3.begin(NODE_BAUD_RATE); usart_n[3] = &usart_n3; xgrid.add_node(&usart_n3); usart_n4.set_tx_buffer(usart_n4_txbuf, NODE_TX_BUF_SIZE); usart_n4.set_rx_buffer(usart_n4_rxbuf, NODE_RX_BUF_SIZE); usart_n4.begin(NODE_BAUD_RATE); usart_n[4] = &usart_n4; xgrid.add_node(&usart_n4); usart_n5.set_tx_buffer(usart_n5_txbuf, NODE_TX_BUF_SIZE); usart_n5.set_rx_buffer(usart_n5_rxbuf, NODE_RX_BUF_SIZE); usart_n5.begin(NODE_BAUD_RATE); usart_n[5] = &usart_n5; xgrid.add_node(&usart_n5); // ADC setup ADCA.CTRLA = ADC_DMASEL_OFF_gc | ADC_FLUSH_bm; ADCA.CTRLB = ADC_CONMODE_bm | ADC_RESOLUTION_12BIT_gc; ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | ADC_BANDGAP_bm; ADCA.EVCTRL = ADC_SWEEP_0123_gc | ADC_EVSEL_0123_gc | ADC_EVACT_SWEEP_gc; ADCA.PRESCALER = ADC_PRESCALER_DIV64_gc; ADCA.CALL = SP_ReadCalibrationByte(PROD_SIGNATURES_START + ADCACAL0_offset); ADCA.CALH = SP_ReadCalibrationByte(PROD_SIGNATURES_START + ADCACAL1_offset); ADCA.CH0.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc; ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN1_gc; ADCA.CH0.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc; ADCA.CH0.RES = 0; ADCA.CH1.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc; ADCA.CH1.MUXCTRL = ADC_CH_MUXPOS_PIN2_gc; ADCA.CH1.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc; ADCA.CH1.RES = 0; ADCA.CH2.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc; ADCA.CH2.MUXCTRL = ADC_CH_MUXPOS_PIN3_gc; ADCA.CH2.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc; ADCA.CH2.RES = 0; ADCA.CH3.CTRL = ADC_CH_GAIN_1X_gc | ADC_CH_INPUTMODE_SINGLEENDED_gc; ADCA.CH3.MUXCTRL = ADC_CH_MUXPOS_PIN4_gc; ADCA.CH3.INTCTRL = ADC_CH_INTMODE_COMPLETE_gc | ADC_CH_INTLVL_LO_gc; ADCA.CH3.RES = 0; //ADCA.CTRLA |= ADC_ENABLE_bm; //ADCA.CTRLB |= ADC_FREERUN_bm; // TCC TCC0.CTRLA = TC_CLKSEL_DIV256_gc; TCC0.CTRLB = 0; TCC0.CTRLC = 0; TCC0.CTRLD = 0; TCC0.CTRLE = 0; TCC0.INTCTRLA = TC_OVFINTLVL_LO_gc; TCC0.INTCTRLB = 0; TCC0.CNT = 0; TCC0.PER = 125; // ADC trigger on TCC0 overflow //EVSYS.CH0MUX = EVSYS_CHMUX_TCC0_OVF_gc; //EVSYS.CH0CTRL = 0; // I2C //i2c.begin(400000L); // SPI //spi.begin(SPI_MODE_2_gc, SPI_PRESCALER_DIV4_gc, 1); // CS line //SPI_CS_PORT.OUTSET = SPI_CS_DEV_PIN_bm; //SPI_CS_PORT.DIRSET = SPI_CS_DEV_PIN_bm; // Interrupts PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm; sei(); //LED_PORT.OUTTGL = LED_USR_1_PIN_bm; }
void Sensors_Init(void){ ADCA.CALL = SP_ReadCalibrationByte( PROD_SIGNATURES_START + ADCACAL0_offset ); ADCA.CALH = SP_ReadCalibrationByte( PROD_SIGNATURES_START + ADCACAL1_offset ); ADCB.CALL = SP_ReadCalibrationByte( PROD_SIGNATURES_START + ADCBCAL0_offset ); ADCB.CALH = SP_ReadCalibrationByte( PROD_SIGNATURES_START + ADCBCAL1_offset ); // Port A = ekg, temperature, humdity ADCA.EKGChannel.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; // set input mode ADCA.EKGChannel.MUXCTRL = EKGMUXPos; ADCA.temperatureChannel.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; // set input mode ADCA.temperatureChannel.MUXCTRL = temperatureMUXPos; ADCA.humidityChannel.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; // set input mode ADCA.humidityChannel.MUXCTRL = humidityMUXPos; ADCA.groundChannel.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; // set input mode ADCA.groundChannel.MUXCTRL = groundMUxPos; ADCA.PRESCALER = (ADCA.PRESCALER & (~ADC_PRESCALER_gm)) | ADC_PRESCALER_DIV64_gc; ADCA.REFCTRL = ADC_REFSEL_AREFA_gc; ADCA.EVCTRL = (ADCA.EVCTRL & (~ADC_SWEEP_gm)) | ADC_SWEEP_0123_gc; ADCA.CTRLB |= ADC_FREERUN_bm; // free running mode ADCA.temperatureChannel.CTRL |= ADC_CH_START_bm; ADCA.humidityChannel.CTRL |= ADC_CH_START_bm; ADCA.EKGChannel.CTRL |= ADC_CH_START_bm; ADCA.groundChannel.CTRL |= ADC_CH_START_bm; ADCA.CTRLA = ADC_ENABLE_bm; // enable adc w/o calibrating _delay_ms(10); zeroOffsetA = ADCA.groundResult; // Port B = Respiration ADCB.respirationChannel.CTRL = ADC_CH_INPUTMODE_DIFFWGAIN_gc | ADC_CH_GAIN_8X_gc; // set input mode ADCB.respirationChannel.MUXCTRL = respirationMUXPos | neg_resipirationMUXPos; ADCB.PRESCALER = (ADCB.PRESCALER & (~ADC_PRESCALER_gm)) | ADC_PRESCALER_DIV64_gc; ADCB.REFCTRL = ADC_REFSEL_AREFA_gc; ADCB.EVCTRL = (ADCB.EVCTRL & (~ADC_SWEEP_gm)) | ADC_SWEEP_0123_gc; ADCB.CTRLB |= ADC_FREERUN_bm | ADC_CONMODE_bm; // free running mode & signed ADCB.respirationChannel.CTRL |= ADC_CH_START_bm; ADCB.CTRLA = ADC_ENABLE_bm; // enable adc w/o calibrating _delay_ms(10); zeroOffsetB = ADCB.groundResult; respirationPort.DIRSET = 1<<respirationDriver; respirationPort.OUTSET = 1<<respirationDriver; // ekg @ 300hz // fclk = 14745600 // div = 64 // per = 768 (remember to subtract 1) // => 300 samples per second // Set period/TOP value Sensors_Timer_300HZ.PER = 767; //Sensors_Timer_300HZ.PER = 2303; // 100 hz // Select clock source Sensors_Timer_300HZ.CTRLA = (TCD1.CTRLA & ~TC0_CLKSEL_gm) | TC_CLKSEL_DIV64_gc; // Enable CCA interrupt Sensors_Timer_300HZ.INTCTRLA = (TCD1.INTCTRLA & ~TC0_OVFINTLVL_gm) | TC_CCAINTLVL_HI_gc; Sensors_ResetTemperatureBuffers(); Sensors_ResetRespirationBuffers(); Sensors_ResetEKGBuffers(); Sensors_ResetHumidityBuffers(); }
void IR_sensor_init() { // IR sensors use ADCB channel 0, all the time /* SET INPUT PINS AS INPUTS */ IR_SENSOR_PORT.DIRCLR = IR_SENSOR_0_PIN_bm | IR_SENSOR_1_PIN_bm | IR_SENSOR_2_PIN_bm | IR_SENSOR_3_PIN_bm | IR_SENSOR_4_PIN_bm | IR_SENSOR_5_PIN_bm; // 28.16.3 REFCTRL – Reference Control register // // Bit 1 – BANDGAP: Bandgap enable // Setting this bit enables the Bandgap for ADC measurement. Note that if any other functions are // using the Bandgap already, this bit does not need to be set when the internal 1.00V reference is // used in ADC or DAC, or if the Brown-out Detector is enabled. // // Bits 6:4 – REFSEL[2:0]: ADC Reference Selection // These bits selects the reference for the ADC //ADCB.REFCTRL = ADC_REFSEL_VCC_gc; // Vcc/1.6 //ADCB.REFCTRL = 0b01000000; // Vcc/2 //ADCB.REFCTRL = 0b00100000; // AREFA = 0.71 V //ADCB.REFCTRL = 0b00110000; // AREFB = 0.54 V // 28.16.2 CTRLB – ADC Control Register B // // Bit 7 – IMPMODE: Gain Stage Impedance Mode // This bit controls the impedance mode of the gain stage. See GAIN setting with ADC Channel // Register description for more information. // // Bit 6:5 – CURRLIMIT[1:0]: Current Limitation // These bits can be used to limit the maximum current consumption of the ADC. Setting these bits // will also reduce the maximum sampling rate. The available settings is shown in Table 28-3 on // page 367. The indicated current limitations are nominal values, refer to device datasheet for // actual current limitation for each setting. // // Bit 4 – CONVMODE: ADC Conversion ModePlot // This bit controls whether the ADC will work in signed or unsigned mode. By default this bit is // cleared and the ADC is configured for unsigned mode. When this bit is set the ADC is configured // for signed mode. // // Bit 3 – FREERUN: ADC Free Running Mode // When the bit is set to one, the ADC is in free running mode and ADC channels defined in the // EVCTRL register are swept repeatedly. // // Bit 2:1 – RESOLUTION[1:0]: ADC Conversion Result Resolution // These bits define whether the ADC completes the conversion at 12- or 8-bit result. They also // define whether the 12-bit result is left or right oriented in the 16-bit result registers. See Table // 28-4 on page 367 for possible settings. ADCB.CTRLB = ADC_RESOLUTION_8BIT_gc; // use 8 bit resolution ADCB.CTRLB |= ADC_CONMODE_bm; // switch to signed mode ADCB.PRESCALER = ADC_PRESCALER_DIV512_gc; /* WARNING! WHEN DIFFERENTIAL INPUT IS USED, SIGNED MODE MUST BE USED (sec. 28.6 of Manual) */ //ADCB.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc; // can use either signed or unsigned mode ADCB.CH0.CTRL = ADC_CH_INPUTMODE_DIFF_gc; // requires selecting signed mode //ADCB.CH0.CTRL = ADC_CH_INPUTMODE_DIFFWGAIN_gc; // requires selecting signed mode /* SELECT MUXNEG */ // selecting the negative input sets where the zero of the signed output is // note that this is NOT a negative voltage, no negative voltages should ever be applied the the XMEGA ADCB.CH0.MUXCTRL = ADC_CH_MUXNEG_PIN0_gc; // use VREF_IN for the negative input (0.54 V) /* READ & LOAD CALIBRATION (PRODUCTION SIGNATURE ROW) */ //TODO (unlikely will make any difference) ADCBcalibration0 = SP_ReadCalibrationByte(offsetof( NVM_PROD_SIGNATURES_t, ADCBCAL0 )); ADCBcalibration1 = SP_ReadCalibrationByte(offsetof( NVM_PROD_SIGNATURES_t, ADCBCAL1 )); ADCAcalibration0 = SP_ReadCalibrationByte(offsetof( NVM_PROD_SIGNATURES_t, ADCACAL0 )); ADCAcalibration1 = SP_ReadCalibrationByte(offsetof( NVM_PROD_SIGNATURES_t, ADCACAL1 )); // for Droplet3, the correct values are: // ADCACAL0 68 // ADCACAL1 4 // ADCBCAL0 68 // ADCBCAL1 4 // SHOULD WE WRITE THEM??? //ADCB.CALL = ADCBcalibration0; //ADCB.CALH = ADCBcalibration1; //ADCB.CALL = 68; //ADCB.CALH = 4; ADCB.CTRLA = ADC_ENABLE_bm; /* FIND AND RECORD THE ZERO-OFFSET OF EACH IR DIRECTION */ PORTB.DIRSET = 0b11111100; // set the IR sense pins as OUTPUT PORTB.OUTSET = 0b00000000; // put a low voltage on these pins (typically, this will be about 15 mV) ADCB.CH0.MUXCTRL |= IR_SENSOR_0_FOR_ADCB_MUXPOS; //_delay_ms(1); // may have to wait a bit before the new multiplexer (MUX) connection is made in hardware? TODO ??? ADCB.CTRLA |= ADC_CH0START_bm; while (ADCB.CH0.INTFLAGS==0){}; // wait for 'complete flag' to be set ADCB.CH0.INTFLAGS = 1; // clear the complete flag ADC_offset[0] = ADCB.CH0.RES*(-1); ADCB.CH0.MUXCTRL &= ~0b01111000; // clear out the old value ADCB.CH0.MUXCTRL |= IR_SENSOR_1_FOR_ADCB_MUXPOS; //_delay_ms(1); // may have to wait a bit before the new multiplexer (MUX) connection is made in hardware? TODO ??? ADCB.CTRLA |= ADC_CH0START_bm; while (ADCB.CH0.INTFLAGS==0){}; // wait for 'complete flag' to be set ADCB.CH0.INTFLAGS = 1; // clear the complete flag ADC_offset[1] = ADCB.CH0.RES*(-1); ADCB.CH0.MUXCTRL &= ~0b01111000; // clear out the old value ADCB.CH0.MUXCTRL |= IR_SENSOR_2_FOR_ADCB_MUXPOS; //_delay_ms(1); // may have to wait a bit before the new multiplexer (MUX) connection is made in hardware? TODO ??? ADCB.CTRLA |= ADC_CH0START_bm; while (ADCB.CH0.INTFLAGS==0){}; // wait for 'complete flag' to be set ADCB.CH0.INTFLAGS = 1; // clear the complete flag ADC_offset[2] = ADCB.CH0.RES*(-1); ADCB.CH0.MUXCTRL &= ~0b01111000; // clear out the old value ADCB.CH0.MUXCTRL |= IR_SENSOR_3_FOR_ADCB_MUXPOS; //_delay_ms(1); // may have to wait a bit before the new multiplexer (MUX) connection is made in hardware? TODO ??? ADCB.CTRLA |= ADC_CH0START_bm; while (ADCB.CH0.INTFLAGS==0){}; // wait for 'complete flag' to be set ADCB.CH0.INTFLAGS = 1; // clear the complete flag ADC_offset[3] = ADCB.CH0.RES*(-1); ADCB.CH0.MUXCTRL &= ~0b01111000; // clear out the old value ADCB.CH0.MUXCTRL |= IR_SENSOR_4_FOR_ADCB_MUXPOS; //_delay_ms(1); // may have to wait a bit before the new multiplexer (MUX) connection is made in hardware? TODO ??? ADCB.CTRLA |= ADC_CH0START_bm; while (ADCB.CH0.INTFLAGS==0){}; // wait for 'complete flag' to be set ADCB.CH0.INTFLAGS = 1; // clear the complete flag ADC_offset[4] = ADCB.CH0.RES*(-1); ADCB.CH0.MUXCTRL &= ~0b01111000; // clear out the old value ADCB.CH0.MUXCTRL |= IR_SENSOR_5_FOR_ADCB_MUXPOS; //_delay_ms(1); // may have to wait a bit before the new multiplexer (MUX) connection is made in hardware? TODO ??? ADCB.CTRLA |= ADC_CH0START_bm; while (ADCB.CH0.INTFLAGS==0){}; // wait for 'complete flag' to be set ADCB.CH0.INTFLAGS = 1; // clear the complete flag ADC_offset[5] = ADCB.CH0.RES*(-1); /* printf("ADC offset 0: %i\r\n",ADC_offset[0]); printf("ADC offset 1: %i\r\n",ADC_offset[1]); printf("ADC offset 2: %i\r\n",ADC_offset[2]); printf("ADC offset 3: %i\r\n",ADC_offset[3]); printf("ADC offset 4: %i\r\n",ADC_offset[4]); printf("ADC offset 5: %i\r\n",ADC_offset[5]); */ PORTB.DIRCLR = 0xFF; // return the IR sense pins back to inputs //printf("Offsets: [0: %i, 1: %i, 2: %i, 3: %i, 4: %i, 5: %i\r\n",ADC_offset[0],ADC_offset[1],ADC_offset[2],ADC_offset[3],ADC_offset[4],ADC_offset[5]); }
/************************************************************************************************** * Handle received HID set feature reports */ void HID_set_feature_report_out(uint8_t *report) { uint8_t response[UDI_HID_REPORT_OUT_SIZE]; response[0] = report[0] | 0x80; response[1] = report[1]; response[2] = report[2]; uint16_t addr; addr = *(uint16_t *)(report+1); switch(report[0]) { // no-op case CMD_NOP: break; // write to RAM page buffer case CMD_RESET_POINTER: page_ptr = 0; return; // read from RAM page buffer case CMD_READ_BUFFER: memcpy(response, &page_buffer[page_ptr], UDI_HID_REPORT_OUT_SIZE); page_ptr += UDI_HID_REPORT_OUT_SIZE; page_ptr &= APP_SECTION_PAGE_SIZE-1; break; // erase entire application section case CMD_ERASE_APP_SECTION: SP_WaitForSPM(); SP_EraseApplicationSection(); return; // calculate application and bootloader section CRCs case CMD_READ_FLASH_CRCS: SP_WaitForSPM(); *(uint32_t *)&response[3] = SP_ApplicationCRC(); *(uint32_t *)&response[7] = SP_BootCRC(); break; // read MCU IDs case CMD_READ_MCU_IDS: response[3] = MCU.DEVID0; response[4] = MCU.DEVID1; response[5] = MCU.DEVID2; response[6] = MCU.REVID; break; // read fuses case CMD_READ_FUSES: response[3] = SP_ReadFuseByte(0); response[4] = SP_ReadFuseByte(1); response[5] = SP_ReadFuseByte(2); response[6] = 0xFF; response[7] = SP_ReadFuseByte(4); response[8] = SP_ReadFuseByte(5); break; // write RAM page buffer to application section page case CMD_WRITE_PAGE: if (addr > (APP_SECTION_SIZE / APP_SECTION_PAGE_SIZE)) // out of range { response[1] = 0xFF; response[2] = 0xFF; break; } SP_WaitForSPM(); SP_LoadFlashPage(page_buffer); SP_WriteApplicationPage(APP_SECTION_START + ((uint32_t)addr * APP_SECTION_PAGE_SIZE)); page_ptr = 0; break; // read application page to RAM buffer and return first 32 bytes case CMD_READ_PAGE: if (addr > (APP_SECTION_SIZE / APP_SECTION_PAGE_SIZE)) // out of range { response[1] = 0xFF; response[2] = 0xFF; } else { memcpy_P(page_buffer, (const void *)(APP_SECTION_START + (APP_SECTION_PAGE_SIZE * addr)), APP_SECTION_PAGE_SIZE); memcpy(&response[3], page_buffer, 32); page_ptr = 0; } break; // erase user signature row case CMD_ERASE_USER_SIG_ROW: SP_WaitForSPM(); SP_EraseUserSignatureRow(); break; // write RAM buffer to user signature row case CMD_WRITE_USER_SIG_ROW: SP_WaitForSPM(); SP_LoadFlashPage(page_buffer); SP_WriteUserSignatureRow(); break; // read user signature row to RAM buffer and return first 32 bytes case CMD_READ_USER_SIG_ROW: if (addr > (USER_SIGNATURES_PAGE_SIZE - 32)) { response[1] = 0xFF; response[2] = 0xFF; } else { memcpy_P(page_buffer, (const void *)(USER_SIGNATURES_START + addr), USER_SIGNATURES_SIZE); memcpy(&response[3], page_buffer, 32); page_ptr = 0; } break; case CMD_READ_SERIAL: { uint8_t i; uint8_t j = 3; uint8_t b; for (i = 0; i < 6; i++) { b = SP_ReadCalibrationByte(offsetof(NVM_PROD_SIGNATURES_t, LOTNUM0) + i); response[j++] = hex_to_char(b >> 4); response[j++] = hex_to_char(b & 0x0F); } response[j++] = '-'; b = SP_ReadCalibrationByte(offsetof(NVM_PROD_SIGNATURES_t, LOTNUM0) + 6); response[j++] = hex_to_char(b >> 4); response[j++] = hex_to_char(b & 0x0F); response[j++] = '-'; for (i = 7; i < 11; i++) { b = SP_ReadCalibrationByte(offsetof(NVM_PROD_SIGNATURES_t, LOTNUM0) + i); response[j++] = hex_to_char(b >> 4); response[j++] = hex_to_char(b & 0x0F); } response[j] = '\0'; break; } case CMD_READ_BOOTLOADER_VERSION: response[3] = BOOTLOADER_VERSION; break; case CMD_RESET_MCU: reset_do_soft_reset(); response[1] = 0xFF; // failed break; case CMD_READ_EEPROM: if (addr > (EEPROM_SIZE - 32)) { response[1] = 0xFF; response[2] = 0xFF; } else { EEP_EnableMapping(); memcpy_P(page_buffer, (const void *)(MAPPED_EEPROM_START + addr), APP_SECTION_PAGE_SIZE); EEP_DisableMapping(); memcpy(&response[3], page_buffer, 32); page_ptr = 0; } break; case CMD_WRITE_EEPROM: if (addr > (EEPROM_SIZE / EEPROM_PAGE_SIZE)) { response[1] = 0xFF; response[2] = 0xFF; } else { EEP_LoadPageBuffer(&report[3], EEPROM_PAGE_SIZE); EEP_AtomicWritePage(addr); } break; // unknown command default: response[0] = 0xFF; break; } udi_hid_generic_send_report_in(response); }