From bb10078065553c1c241c25d0f5f0d47a39e2bfcb Mon Sep 17 00:00:00 2001 From: Padmal Date: Wed, 12 Oct 2022 11:57:06 +0200 Subject: [PATCH] feat: frequency measurements --- src/commands.c | 10 +-- src/helpers/interval.c | 109 ++++++++++++++++++++++++++ src/helpers/interval.h | 120 ++++++++++++++++++++++++++++- src/instruments/multimeter.c | 143 +++++++++++++++++++++++++++++++++++ src/instruments/multimeter.h | 71 +++++++++++++++++ 5 files changed, 447 insertions(+), 6 deletions(-) diff --git a/src/commands.c b/src/commands.c index 20f14b3..b3b1a97 100644 --- a/src/commands.c +++ b/src/commands.c @@ -228,13 +228,13 @@ command_func_t* const cmd_table[NUM_PRIMARY_CMDS + 1][NUM_SECONDARY_CMDS_MAX + 1 }, { // 10 TIMING // 0 1 GET_TIMING 2 3 - Undefined, Unimplemented, Undefined, Undefined, + Undefined, INTERVAL_UntilEvent, Undefined, Undefined, // 4 START_ONE_CHAN_LA 5 START_TWO_CHAN_LA 6 START_FOUR_CHAN_LA 7 FETCH_DMA_DATA LOGICANALYZER_OneChannel, LOGICANALYZER_TwoChannel, LOGICANALYZER_FourChannel, Removed, // 8 FETCH_INT_DMA_DATA 9 FETCH_LONG_DMA_DATA 10 COMPARATOR_TO_LA 11 GET_INITIAL_STATES BUFFER_FetchInt, BUFFER_FetchLong, Unimplemented, INTERVAL_GetState, // 12 TIMING_MEASUREMENTS 13 INTERVAL_MEASUREMENTS 14 CONFIGURE_COMPARATOR 15 START_ALTERNATE_ONE_CHAN_LA - Unimplemented, Unimplemented, Removed, LOGICANALYZER_OneChannelAlt, + INTERVAL_TimeMeasure, INTERVAL_IntervalMeasure, Removed, LOGICANALYZER_OneChannelAlt, // 16 START_THREE_CHAN_LA 17 STOP_LA 18 19 LOGICANALYZER_ThreeChannel, LOGICANALYZER_Stop, Undefined, Undefined, // 20 21 22 23 @@ -244,17 +244,17 @@ command_func_t* const cmd_table[NUM_PRIMARY_CMDS + 1][NUM_SECONDARY_CMDS_MAX + 1 }, { // 11 COMMON // 0 1 GET_CTMU_VOLTAGE 2 GET_CAPACITANCE 3 GET_FREQUENCY - Undefined, MULTIMETER_GetCTMUVolts, MULTIMETER_GetCapacitance, Unimplemented, + Undefined, MULTIMETER_GetCTMUVolts, MULTIMETER_GetCapacitance, MULTIMETER_LowFrequency, // 4 GET_INDUCTANCE 5 GET_VERSION 6 7 Unimplemented, DEVICE_GetVersion, Undefined, Undefined, // 8 RETRIEVE_BUFFER 9 GET_HIGH_FREQUENCY 10 CLEAR_BUFFER 11 SET_RGB1 - BUFFER_Retrieve, Unimplemented, BUFFER_Clear, Removed, + BUFFER_Retrieve, MULTIMETER_HighFrequency, BUFFER_Clear, Removed, // 12 READ_PROGRAM_ADDRESS 13 WRITE_PROGRAM_ADDRESS 14 READ_DATA_ADDRESS 15 WRITE_DATA_ADDRESS Removed, Removed, DEVICE_ReadRegisterData, DEVICE_WriteRegisterData, // 16 GET_CAP_RANGE 17 SET_RGB2 18 READ_LOG 19 RESTORE_STANDALONE MULTIMETER_GetCapRange, Removed, Removed, DEVICE_Reset, // 20 GET_ALTERNATE_HIGH_FREQUENCY 21 SET_RGB_COMMON 22 SET_RGB3 23 START_CTMU - Unimplemented, LIGHT_RGBPin, Removed, CTMU_Start, + MULTIMETER_HighFrequencyAlt, LIGHT_RGBPin, Removed, CTMU_Start, // 24 STOP_CTMU 25 START_COUNTING 26 FETCH_COUNT 27 FILL_BUFFER CTMU_Stop, SENSORS_StartCounter, SENSORS_GetCounter, BUFFER_Fill, }, diff --git a/src/helpers/interval.c b/src/helpers/interval.c index a77f949..16484f5 100644 --- a/src/helpers/interval.c +++ b/src/helpers/interval.c @@ -9,6 +9,7 @@ #include "../registers/memory/dma.h" #include "../registers/system/interrupt_manager.h" #include "../registers/system/pin_manager.h" +#include "../registers/system/watchdog.h" #include "../registers/timers/tmr2.h" #include "buffer.h" @@ -196,3 +197,111 @@ response_t INTERVAL_GetState(void) { return SUCCESS; } + +response_t INTERVAL_IntervalMeasure(void) { + + uint16_t timeout = UART1_ReadInt(); // t * 64e6 >> 16 + uint8_t pins = UART1_Read(); + uint8_t modes = UART1_Read(); + + IC_PARAMS_ConfigureIntervalCaptureWithIC1AndIC2(pins & 0xF, + IC_PARAMS_CAPTURE_TIMER_PERIPHERAL, + IC_PARAMS_CAPTURE_INTERRUPT_EVERY_EVENT, modes & 0x7); + IC_PARAMS_ConfigureIntervalCaptureWithIC3AndIC4((pins >> 4) & 0xF, + IC_PARAMS_CAPTURE_TIMER_PERIPHERAL, + IC_PARAMS_CAPTURE_INTERRUPT_EVERY_EVENT, (modes >> 3) & 0x7); + + IC_PARAMS_ManualTriggerAll(); + + while ((!_IC1IF) && (IC2TMR < timeout)) WATCHDOG_TimerClear(); + UART1_WriteInt(IC1BUF); + UART1_WriteInt(IC2BUF); + + while ((!_IC3IF) && (IC2TMR < timeout)) WATCHDOG_TimerClear(); + UART1_WriteInt(IC3BUF); + UART1_WriteInt(IC4BUF); + UART1_WriteInt(IC2TMR); + + IC_PARAMS_DisableAllModules(); + + return SUCCESS; +} + +response_t INTERVAL_TimeMeasure(void) { + + uint16_t timeout = UART1_ReadInt(); // t * 64e6 >> 16 + uint8_t pins = UART1_Read(); + uint8_t modes = UART1_Read(); + uint8_t intrpts = UART1_Read(); + + if ((pins & 0xF) == 4 || ((pins >> 4) & 0xF) == 4) { + CMP4_SetupComparator(); + CVR_SetupComparator(); + } + + IC_PARAMS_ConfigureIntervalCaptureWithIC1AndIC2(pins & 0xF, + IC_PARAMS_CAPTURE_TIMER2, (intrpts & 0xF) - 1, modes & 0x7); + IC_PARAMS_ConfigureIntervalCaptureWithIC3AndIC4((pins >> 4) & 0xF, + IC_PARAMS_CAPTURE_TIMER2, ((intrpts >> 4) & 0xF) - 1, (modes >> 3) & 0x7); + + TMR2_Initialize(); + + SetDefaultDIGITAL_STATES(); + + IC_PARAMS_ManualTriggerAll(); + TMR2_Start(); + + if ((modes >> 6) & 0x1) { + RPOR5bits.RP54R = RPN_DEFAULT_PORT; // Disconnect SQR1 pin + ((modes >> 7) & 0x1) ? SQR1_SetHigh() : SQR1_SetLow(); + } + + while ((!_IC1IF || !_IC3IF) && (IC2TMR < timeout)) WATCHDOG_TimerClear(); + + uint8_t i; + for (i = 0; i < (intrpts & 0xF); i++) { + UART1_WriteInt(IC1BUF); + UART1_WriteInt(IC2BUF); + } + for (i = 0; i < ((intrpts >> 4) & 0xF); i++) { + UART1_WriteInt(IC3BUF); + UART1_WriteInt(IC4BUF); + } + + IC1_InterruptFlagClear(); + IC3_InterruptFlagClear(); + + UART1_WriteInt(IC2TMR); + + IC_PARAMS_DisableAllModules(); + TMR2_Stop(); + + return SUCCESS; +} + +response_t INTERVAL_UntilEvent(void) { + + uint16_t timeout = UART1_ReadInt(); // t * 64e6 >> 16 + uint8_t mode = UART1_Read(); + uint8_t pin = UART1_Read(); + + IC_PARAMS_ConfigureIntervalCaptureWithIC1AndIC2(pin & 0xF, + IC_PARAMS_CAPTURE_TIMER_PERIPHERAL, (mode & 0x3) - 1, mode & 0x7); + + while (!_IC1IF && (IC2TMR < timeout)) WATCHDOG_TimerClear(); + + IC1_InterruptFlagClear(); + + UART1_WriteInt(IC2TMR); + + uint8_t i; + for (i = 0; i < (mode & 0x3); i++) { + UART1_WriteInt(IC1BUF); + UART1_WriteInt(IC2BUF); + } + + IC_PARAMS_DisableAllModules(); + TMR2_Stop(); + + return SUCCESS; +} diff --git a/src/helpers/interval.h b/src/helpers/interval.h index bbe1608..0757c3b 100644 --- a/src/helpers/interval.h +++ b/src/helpers/interval.h @@ -86,8 +86,126 @@ extern "C" { * @return None */ void INTERVAL_CaptureFour(uint16_t count, uint16_t mode, uint8_t prescaler); - + + /** + * @brief Reads DMA status registry data + * + * @description + * This method will sequentially read register addresses at BUFFER pointer + * and all four DMA channel pointers. It will also read digital state parameters. + * + * There are no input parameters to this method. The output of this method should + * be read over serial in the following order. + * 1. (int) BUFFER pointer + * 2. (int) DMA Channel 0 pointer + * 3. (int) DMA Channel 1 pointer + * 4. (int) DMA Channel 2 pointer + * 5. (int) DMA Channel 3 pointer + * 6. (char) Digital states + * 7. (char) Digital states error + * + * @return SUCCESS + */ response_t INTERVAL_GetState(void); + + /** + * @brief Measures the time interval between two pin state change events + * + * @description + * This method will count the time difference between two pin change events + * attached to two pins. + * The events can be any event defined at the list of events in `IC_PARAMS_CAPTURE_MODE`. + * The pins should be any pin in the list of `PIN_MANAGER_DIGITAL_PINS`. + * + * @param timeout : period of wait until the operation is aborted + * @param pins : input pins defined at `PIN_MANAGER_DIGITAL_PINS` + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | PIN EVENT 2 | PIN EVENT 1 | + * @param modes : pin change event defined at `IC_PARAMS_CAPTURE_MODE` + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | X | X | EVENT 2 | EVENT 1 | + * + * The output of this method should be read over serial in the following order. + * 1. (int) IC1BUF - LSW + * 2. (int) IC2BUF - MSW + * Combine 1. and 2. to get the trigger time of the event 1 + * 3. (int) IC3BUF - LSW + * 4. (int) IC4BUF - MSW + * Combine 3. and 4. to get the trigger time of the event 2 + * 5. (int) IC2TMR + * + * @return SUCCESS + */ + response_t INTERVAL_IntervalMeasure(void); + + /** + * @brief Measures the time between multiple pin state change events + * + * @description + * This method will log time units for multiple changes occurred on + * defined digital pins. Unlike `INTERVAL_IntervalMeasure` where it + * measure only a single change of states, this method will measure + * upto 4 change of pin states. + * The events can be any event defined at the list of events in `IC_PARAMS_CAPTURE_MODE`. + * The pins should be any pin in the list of `PIN_MANAGER_DIGITAL_PINS`. + * + * @param timeout : period of wait until the operation is aborted + * @param pins : input pins defined at `PIN_MANAGER_DIGITAL_PINS` + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | PIN EVENT 2 | PIN EVENT 1 | + * @param modes : pin change event defined at `IC_PARAMS_CAPTURE_MODE` + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | X | X | EVENT 2 | EVENT 1 | + * @param intrpts : input pins defined at `PIN_MANAGER_DIGITAL_PINS` + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | INTERRUPT 2 | INTERRUPT 1 | + * + * The output of this method should be read over serial in the following order. + * 1. (int) IC1BUF - LSW + * 2. (int) IC2BUF - MSW + * Combine 1. and 2. to get the trigger time of the change event. Depending on the + * intrpts, the two registers (1. and 2.) may need to be read repeatedly to capture + * timing data for each event occurrence. + * 3. (int) IC3BUF - LSW + * 4. (int) IC4BUF - MSW + * Combine 3. and 4. to get the trigger time of the change event. Depending on the + * intrpts, the two registers (3. and 4.) may need to be read repeatedly to capture + * timing data for each event occurrence. + * Note: ICxBUF is a 4-level buffer that can store time log for four change events. + * 5. (int) IC2TMR + * + * @return SUCCESS + */ + response_t INTERVAL_TimeMeasure(void); + + /** + * @brief Measures the time until a pin state change event occurs + * + * @description + * This method will stop counting time when the defined pin change event occurred. + * The event can be any event defined at the list of events in `IC_PARAMS_CAPTURE_MODE`. + * The pin should be any pin in the list of `PIN_MANAGER_DIGITAL_PINS`. + * + * @param timeout : period of wait until the operation is aborted + * @param mode : pin change event defined at `IC_PARAMS_CAPTURE_MODE` + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | X | X | X | X | X | EVENT | + * @param pin : input pin defined at `PIN_MANAGER_DIGITAL_PINS` + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | X | X | X | X | PIN EVENT | + * + * The output of this method should be read over serial in the following order. + * 1. (int) IC2TMR + * 2. (int) IC1BUF - LSW + * 3. (int) IC2BUF - MSW + * Combine 2. and 3. to get the trigger time of the change event. Depending on the mode, + * the last two registers (2. and 3.) may need to be read repeatedly to capture timing + * data for each event occurrence. + * Note: ICxBUF is a 4-level buffer that can store time log for four change events. + * + * @return SUCCESS + */ + response_t INTERVAL_UntilEvent(void); // Getters and setters diff --git a/src/instruments/multimeter.c b/src/instruments/multimeter.c index deb64fa..ce78433 100644 --- a/src/instruments/multimeter.c +++ b/src/instruments/multimeter.c @@ -1,10 +1,19 @@ #include "../commands.h" #include "../bus/uart/uart1.h" #include "../helpers/delay.h" +#include "../helpers/interval.h" +#include "../registers/comparators/ic1.h" +#include "../registers/comparators/ic2.h" +#include "../registers/comparators/ic_params.h" #include "../registers/converters/adc1.h" #include "../registers/converters/ctmu.h" +#include "../registers/comparators/cmp4.h" +#include "../registers/comparators/cvr.h" #include "../registers/memory/dma.h" #include "../registers/system/pin_manager.h" +#include "../registers/timers/timer_params.h" +#include "../registers/timers/tmr2.h" +#include "../registers/timers/tmr3.h" #include "../registers/timers/tmr5.h" #include "multimeter.h" @@ -222,6 +231,140 @@ response_t MULTIMETER_GetCapacitance(void) { return SUCCESS; } +response_t MULTIMETER_HighFrequency(void) { + + uint8_t config = UART1_Read(); + + LED_SetLow(); + + if ((config & 0xF) == 4) { + CVR_SetupComparator(); + CMP4_SetupComparator(); + } + + RPINR3bits.T2CKR = PIN_MANAGER_DIGITAL_PINS[config & 0xF]; + + TMR2_Initialize(); + TMR3_Initialize(); + TMR5_Initialize(); + + TMR2_CombineWithTimer3(); + TMR2_SetExternalClockAsSource(); + TMR2_SetPrescaler((config >> 4) & 0x4); + TMR5_SetPrescaler(TMR_PRESCALER_256); + TMR5_Period16BitSet(25000); // 100 millisecond sampling + + _T5IP = 0x01; // Set Timer5 Interrupt Priority Level + TMR5_InterruptEnable(); + TMR2_Start(); + TMR5_Start(); + + TMR5_WaitForInterruptEvent(); + + LED_SetHigh(); + UART1_Write(1); // Scaling factor + UART1_WriteInt(TMR2_Counter16BitGet()); + UART1_WriteInt(TMR3_Carry16BitGet()); + + return SUCCESS; +} + +response_t MULTIMETER_HighFrequencyAlt(void) { + + uint8_t config = UART1_Read(); + + LED_SetLow(); + + if ((config & 0xF) == 4) { + CVR_SetupComparator(); + CMP4_SetupComparator(); + } + + TMR2_Initialize(); + TMR2_SetExternalClockAsSource(); + TMR2_SetPrescaler((config >> 4) & 0x4); + TMR5_Initialize(); + IC1_Initialize(); + IC2_Initialize(); + + RPINR7bits.IC1R = RPN_DEFAULT_PORT; + RPINR7bits.IC2R = RPN_DEFAULT_PORT; + RPINR3bits.T2CKR = PIN_MANAGER_DIGITAL_PINS[config & 0xF]; + + IC1_SetCaptureTimer(IC_PARAMS_CAPTURE_TIMER2); + IC1_InputCaptureInterruptOn(IC_PARAMS_CAPTURE_INTERRUPT_EVERY_SECOND); + IC1_CombineOddEvenICModules(); + IC1_UseSourceTo(IC_PARAMS_SOURCE_TASK_TRIGGER); + IC1_SetCaptureMode(IC_PARAMS_CAPTURE_MODE_EVERY_16TH_RISING_EDGE); + + IC2_SetCaptureTimer(IC_PARAMS_CAPTURE_TIMER2); + IC2_InputCaptureInterruptOn(IC_PARAMS_CAPTURE_INTERRUPT_EVERY_SECOND); + IC2_CombineOddEvenICModules(); + IC2_UseSourceTo(IC_PARAMS_SOURCE_TASK_TRIGGER); + IC2_SetCaptureMode(IC_PARAMS_CAPTURE_MODE_EVERY_16TH_RISING_EDGE); + + TMR5_SetPrescaler(TMR_PRESCALER_256); + TMR5_Period16BitSet(25000); // 100 millisecond sampling + + _T5IP = 0x01; + TMR2_Start(); + TMR5_Start(); + IC1_ManualTriggerSet(); + IC2_ManualTriggerSet(); + + TMR5_WaitForInterruptEvent(); + + LED_SetHigh(); + UART1_Write(1); // Scaling factor + UART1_WriteInt(IC1TMR); + UART1_WriteInt(IC2TMR); + + return SUCCESS; +} + +response_t MULTIMETER_LowFrequency(void) { + + uint16_t timeout = UART1_ReadInt(); + uint8_t config = UART1_Read(); + + RPINR7bits.IC1R = PIN_MANAGER_DIGITAL_PINS[config & 0xF]; + + IC1_Initialize(); + IC1_SetCaptureTimer(IC_PARAMS_CAPTURE_TIMER_PERIPHERAL); + IC1_CombineOddEvenICModules(); + IC1_SetCaptureMode(IC_PARAMS_CAPTURE_MODE_EVERY_16TH_RISING_EDGE); + IC1_InputCaptureInterruptOn(IC_PARAMS_CAPTURE_INTERRUPT_EVERY_SECOND); + + IC2_Initialize(); + IC2_SetCaptureTimer(IC_PARAMS_CAPTURE_TIMER_PERIPHERAL); + IC2_CombineOddEvenICModules(); + IC2_SetCaptureMode(IC_PARAMS_CAPTURE_MODE_EVERY_16TH_RISING_EDGE); + IC2_InputCaptureInterruptOn(IC_PARAMS_CAPTURE_INTERRUPT_EVERY_SECOND); + + IC1_ManualTriggerSet(); + IC2_ManualTriggerSet(); + SetDefaultDIGITAL_STATES(); + + IC1_InterruptFlagClear(); + while ((IC2TMR < timeout) && (!_IC1IF)); + IC1_InterruptFlagClear(); + + RPINR7bits.IC1R = RPN_DEFAULT_PORT; + + if ((IC2TMR >= timeout) || + (IC2_HasCaptureBufferOverflowed())) { + UART1_Write(1); + } else { + UART1_Write(0); + } + + UART1_WriteInt(IC1_CaptureDataRead()); + UART1_WriteInt(IC2_CaptureDataRead()); + IC_PARAMS_DisableAllModules(); + + return SUCCESS; +} + response_t MULTIMETER_GetCTMUVolts(void) { uint8_t config = UART1_Read(); diff --git a/src/instruments/multimeter.h b/src/instruments/multimeter.h index 6cf090f..46e6b6a 100644 --- a/src/instruments/multimeter.h +++ b/src/instruments/multimeter.h @@ -29,6 +29,77 @@ extern "C" { */ response_t MULTIMETER_GetCapacitance(void); + /** + * @brief Count the frequency (occurrence) of a signal over a + * 100 ms time period using only timer registers + * + * @description + * This command function takes one argument over serial: + * 1. (uint8) Configuration byte: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | PRE-SCALER | PIN | + * PIN: Pin from PIN_MANAGER_DIGITAL_PINS + * PRE-SCALER: Scaling factor from TIMER_PARAMS_PRESCALER + * + * This method will output the following data over serial + * 1. (char) scaling factor + * 2. (int) TIMER 2 (Least significant potion) + * 3. (int) TIMER 3 (Most significant potion) + * Combine 3. and 2. to structure the full timing data + * + * @return SUCCESS + */ + response_t MULTIMETER_HighFrequency(void); + + /** + * @brief Count the frequency (occurrence) of a signal over a + * 100 ms time period using input capture registers. Unlike + * `MULTIMETER_HighFrequency`, this method will down-sample the + * input signal frequency by a factor of 16 supporting higher + * frequency measurements. + * + * @description + * This command function takes one argument over serial: + * 1. (uint8) Configuration byte: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | PRE-SCALER | PIN | + * PIN: Pin from PIN_MANAGER_DIGITAL_PINS + * PRE-SCALER: Scaling factor from TIMER_PARAMS_PRESCALER + * + * This method will output the following data over serial + * 1. (char) scaling factor + * 2. (int) IC1TMR (Least significant potion) + * 3. (int) IC2TMR (Most significant potion) + * Combine 3. and 2. to structure the full timing data + * + * @return SUCCESS + */ + response_t MULTIMETER_HighFrequencyAlt(void); + + /** + * @brief Count the frequency (occurrence) of a signal. This method + * is to be used for low frequency measurements as it only measures + * the time interval for 32 rising edges. This will have much finer + * granularity than high frequency measurement methods. + * + * @description + * This command function takes one argument over serial: + * 1. (uint16) Timeout: period of wait until the operation is aborted + * 1. (uint8) Configuration byte: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | X | X | X | X | PIN | + * PIN: Pin from PIN_MANAGER_DIGITAL_PINS + * + * This method will output the following data over serial + * 1. (char) overflow flag + * 2. (int) IC1BUF (Least significant potion) + * 3. (int) IC2BUF (Most significant potion) + * Combine 3. and 2. to structure the full timing data + * + * @return SUCCESS + */ + response_t MULTIMETER_LowFrequency(void); + /** * @brief Get an estimate of the capacitor range *