diff --git a/A11_F407_SDC_ACQ_DAC_CubeMX/Src/main.c b/A11_F407_SDC_ACQ_DAC_CubeMX/Src/main.c index 675a3d4..a433a2c 100644 --- a/A11_F407_SDC_ACQ_DAC_CubeMX/Src/main.c +++ b/A11_F407_SDC_ACQ_DAC_CubeMX/Src/main.c @@ -62,26 +62,29 @@ typedef struct #define GPIO_INPUT_MASK 0x7FFFU #define UART_LINE_SIZE 32U -#define ADC_REPORT_PERIOD_MS 500U -#define PWM_REPORT_PERIOD_MS 20U -#define CAN_HEARTBEAT_PERIOD_MS 100U -#define DAC_CAN_TIMEOUT_MS 100U +#define ADC_REPORT_PERIOD_MS 500U +#define PWM_REPORT_PERIOD_MS 20U +#define GPIO_REPORT_PERIOD_MS 1000U +#define CAN_HEARTBEAT_PERIOD_MS 100U +#define DAC_CAN_TIMEOUT_MS 100U #define PWM_TIMEOUT_MS 1000U +#define WHEEL_PULSES_PER_REV 49U +#define WHEEL_CIRCUMFERENCE_MM 1414U -#define CAN_HEARTBEAT_STD_ID 0x6A0U -#define CAN_ADC_REPORT_STD_ID 0x6A1U -#define CAN_PWM_REPORT_STD_ID 0x6A2U -#define CAN_DAC_CMD_STD_ID 0x6A3U -#define CAN_DAC_ACK_STD_ID 0x6A4U -#define CAN_ASSI_LAMP_CMD_STD_ID 0x6A5U -#define CAN_GPIO_REPORT_STD_ID 0x6A6U - -#define ASSI_LAMP_FLASH_HALF_PERIOD_MS 250U -#define ASSI_STATE_OFF 0U -#define ASSI_STATE_READY 1U -#define ASSI_STATE_DRIVING 2U -#define ASSI_STATE_EMERGENCY 3U -#define ASSI_STATE_FINISHED 4U +#define CAN_HEARTBEAT_STD_ID 0x6A0U +#define CAN_ADC_REPORT_STD_ID 0x6A1U +#define CAN_PWM_REPORT_STD_ID 0x6A2U +#define CAN_DAC_CMD_STD_ID 0x6A3U +#define CAN_DAC_ACK_STD_ID 0x6A4U +#define CAN_ASSI_LAMP_CMD_STD_ID 0x6A5U +#define CAN_GPIO_REPORT_STD_ID 0x6A6U + +#define ASSI_LAMP_FLASH_HALF_PERIOD_MS 250U +#define ASSI_STATE_OFF 0U +#define ASSI_STATE_READY 1U +#define ASSI_STATE_DRIVING 2U +#define ASSI_STATE_EMERGENCY 3U +#define ASSI_STATE_FINISHED 4U /* USER CODE END PD */ @@ -100,31 +103,31 @@ I2C_HandleTypeDef hi2c2; TIM_HandleTypeDef htim3; TIM_HandleTypeDef htim5; -TIM_HandleTypeDef htim9; -TIM_HandleTypeDef htim12; UART_HandleTypeDef huart1; /* USER CODE BEGIN PV */ -static CaptureChannel capture_channels[4]; +/* PA0 left-front and PA6 right-front wheel-speed inputs only. */ +static CaptureChannel capture_channels[2]; static uint16_t gpio_last_state; static volatile uint16_t gpio_shadow_state; static volatile uint16_t gpio_report_state; static volatile uint16_t gpio_pending_changed_mask; -static uint32_t last_adc_report_ms; -static uint32_t last_pwm_report_ms; -static uint32_t last_heartbeat_ms; -static uint32_t last_can_heartbeat_ms; -static uint32_t dac_can_last_command_ms[2]; -static bool dac_can_watchdog_active[2]; +static uint32_t last_adc_report_ms; +static uint32_t last_pwm_report_ms; +static uint32_t last_gpio_report_ms; +static uint32_t last_heartbeat_ms; +static uint32_t last_can_heartbeat_ms; +static uint32_t dac_can_last_command_ms[2]; +static bool dac_can_watchdog_active[2]; static uint16_t can_heartbeat_sequence; static uint32_t adc_filter_history[ADC_FILTER_WINDOW]; static uint32_t adc_filter_sum; static uint8_t adc_filter_count; static uint8_t adc_filter_index; static char uart_line[UART_LINE_SIZE]; -static uint8_t uart_line_len; -static uint8_t assi_lamp_state; +static uint8_t uart_line_len; +static uint8_t assi_lamp_state; /* USER CODE END PV */ @@ -136,8 +139,6 @@ static void MX_CAN2_Init(void); static void MX_I2C2_Init(void); static void MX_TIM3_Init(void); static void MX_TIM5_Init(void); -static void MX_TIM9_Init(void); -static void MX_TIM12_Init(void); static void MX_USART1_UART_Init(void); static void MX_ADC1_Init(void); /* USER CODE BEGIN PFP */ @@ -147,11 +148,11 @@ static void PollCanBus(CAN_HandleTypeDef *hcan, const char *name); static void PollCaptureChannels(void); static void PollGpioChanges(void); static void SendPeriodicReport(void); -static void ServiceHeartbeat(void); -static void ServiceAssiLamp(void); -static void ServiceDacCanWatchdog(void); -static void SendCanHeartbeat(void); -static void HandleAssiLampSetRequest(const char *source, uint8_t state); +static void ServiceHeartbeat(void); +static void ServiceAssiLamp(void); +static void ServiceDacCanWatchdog(void); +static void SendCanHeartbeat(void); +static void HandleAssiLampSetRequest(const char *source, uint8_t state); static void HandleTextCommand(const char *source, const char *line); static void HandleDacSetRequest(const char *source, uint8_t dac_id, uint16_t millivolts); static void UartWrite(const char *text); @@ -163,6 +164,7 @@ static const char *DacName(uint8_t dac_id); static uint16_t ReadGpioInputState(void); static void QueueGpioSnapshot(uint16_t state); static uint32_t ReadAdcInputMillivolts(void); +static uint32_t WheelSpeedMmPerSecond(uint32_t frequency_hz); static uint32_t GetTimerClockHz(const TIM_HandleTypeDef *htim); static void InitCaptureChannels(void); static void ConfigureCanRuntime(CAN_HandleTypeDef *hcan); @@ -221,8 +223,6 @@ int main(void) MX_I2C2_Init(); MX_TIM3_Init(); MX_TIM5_Init(); - MX_TIM9_Init(); - MX_TIM12_Init(); MX_USART1_UART_Init(); MX_ADC1_Init(); /* USER CODE BEGIN 2 */ @@ -238,11 +238,11 @@ int main(void) /* USER CODE BEGIN 3 */ PollUart(); - PollCanBus(&hcan1, "CAN1"); - PollCanBus(&hcan2, "CAN2"); - ServiceDacCanWatchdog(); - ServiceAssiLamp(); - PollCaptureChannels(); + PollCanBus(&hcan1, "CAN1"); + PollCanBus(&hcan2, "CAN2"); + ServiceDacCanWatchdog(); + ServiceAssiLamp(); + PollCaptureChannels(); PollGpioChanges(); SendPeriodicReport(); ServiceHeartbeat(); @@ -548,88 +548,6 @@ static void MX_TIM5_Init(void) } -/** - * @brief TIM9 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM9_Init(void) -{ - - /* USER CODE BEGIN TIM9_Init 0 */ - - /* USER CODE END TIM9_Init 0 */ - - TIM_IC_InitTypeDef sConfigIC = {0}; - - /* USER CODE BEGIN TIM9_Init 1 */ - - /* USER CODE END TIM9_Init 1 */ - htim9.Instance = TIM9; - htim9.Init.Prescaler = 159; - htim9.Init.CounterMode = TIM_COUNTERMODE_UP; - htim9.Init.Period = 0xFFFF; - htim9.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim9.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_IC_Init(&htim9) != HAL_OK) - { - Error_Handler(); - } - sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; - sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; - sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; - sConfigIC.ICFilter = 0; - if (HAL_TIM_IC_ConfigChannel(&htim9, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM9_Init 2 */ - - /* USER CODE END TIM9_Init 2 */ - -} - -/** - * @brief TIM12 Initialization Function - * @param None - * @retval None - */ -static void MX_TIM12_Init(void) -{ - - /* USER CODE BEGIN TIM12_Init 0 */ - - /* USER CODE END TIM12_Init 0 */ - - TIM_IC_InitTypeDef sConfigIC = {0}; - - /* USER CODE BEGIN TIM12_Init 1 */ - - /* USER CODE END TIM12_Init 1 */ - htim12.Instance = TIM12; - htim12.Init.Prescaler = 159; - htim12.Init.CounterMode = TIM_COUNTERMODE_UP; - htim12.Init.Period = 0xFFFF; - htim12.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; - htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; - if (HAL_TIM_IC_Init(&htim12) != HAL_OK) - { - Error_Handler(); - } - sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; - sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; - sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; - sConfigIC.ICFilter = 0; - if (HAL_TIM_IC_ConfigChannel(&htim12, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) - { - Error_Handler(); - } - /* USER CODE BEGIN TIM12_Init 2 */ - - /* USER CODE END TIM12_Init 2 */ - -} - /** * @brief USART1 Initialization Function * @param None @@ -685,7 +603,7 @@ static void MX_GPIO_Init(void) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11, GPIO_PIN_RESET); + HAL_GPIO_WritePin(GPIOE, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11, GPIO_PIN_RESET); /*Configure GPIO pins : PD8 PD9 PD10 PD11 PD12 PD13 PD14 PD0 @@ -706,8 +624,8 @@ static void MX_GPIO_Init(void) GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); - /*Configure GPIO pins : PE0 PE1 PE9 PE10 PE11 */ - GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11; + /*Configure GPIO pins : PE0 PE1 PE9 PE10 PE11 */ + GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; @@ -765,29 +683,29 @@ static void AppInit(void) gpio_shadow_state = gpio_last_state; gpio_report_state = gpio_last_state; gpio_pending_changed_mask = 0U; - last_adc_report_ms = HAL_GetTick(); - last_pwm_report_ms = last_adc_report_ms; - last_heartbeat_ms = last_adc_report_ms; - last_can_heartbeat_ms = last_adc_report_ms; - dac_can_last_command_ms[0] = 0U; - dac_can_last_command_ms[1] = 0U; - dac_can_watchdog_active[0] = false; - dac_can_watchdog_active[1] = false; + last_adc_report_ms = HAL_GetTick(); + last_pwm_report_ms = last_adc_report_ms; + last_gpio_report_ms = last_adc_report_ms; + last_heartbeat_ms = last_adc_report_ms; + last_can_heartbeat_ms = last_adc_report_ms; + dac_can_last_command_ms[0] = 0U; + dac_can_last_command_ms[1] = 0U; + dac_can_watchdog_active[0] = false; + dac_can_watchdog_active[1] = false; can_heartbeat_sequence = 0U; adc_filter_sum = 0U; adc_filter_count = 0U; adc_filter_index = 0U; - uart_line_len = 0U; - assi_lamp_state = ASSI_STATE_OFF; + uart_line_len = 0U; + assi_lamp_state = ASSI_STATE_OFF; (void)DacSetMillivolts(1U, 0U); (void)DacSetMillivolts(2U, 0U); UartWrite("\r\nA11 F407 monitor ready\r\n"); UartWrite("UART: DAC1 1.234 or DAC2 2500\r\n"); - UartWrite("CAN cmd 0x6A3: [dac_id][mV_L][mV_H] or ASCII text\r\n"); - UartWrite("CAN cmd 0x6A5: [ASSI state 0=OFF,1=READY,2=DRIVING,3=EMERGENCY,4=FINISHED]\r\n"); - BroadcastGpioReport(0U, gpio_last_state); + UartWrite("CAN cmd 0x6A3: [dac_id][mV_L][mV_H] or ASCII text\r\n"); + UartWrite("CAN cmd 0x6A5: [ASSI state 0=OFF,1=READY,2=DRIVING,3=EMERGENCY,4=FINISHED]\r\n"); } static void PollUart(void) @@ -836,14 +754,14 @@ static void PollCanBus(CAN_HandleTypeDef *hcan, const char *name) continue; } - if (rx_header.StdId == CAN_DAC_CMD_STD_ID) + if (rx_header.StdId == CAN_DAC_CMD_STD_ID) { - if ((rx_header.DLC >= 3U) && (rx_data[0] >= 1U) && (rx_data[0] <= 2U)) - { - uint16_t millivolts = (uint16_t)rx_data[1] | ((uint16_t)rx_data[2] << 8); - HandleDacSetRequest(name, rx_data[0], millivolts); - dac_can_last_command_ms[rx_data[0] - 1U] = HAL_GetTick(); - dac_can_watchdog_active[rx_data[0] - 1U] = true; + if ((rx_header.DLC >= 3U) && (rx_data[0] >= 1U) && (rx_data[0] <= 2U)) + { + uint16_t millivolts = (uint16_t)rx_data[1] | ((uint16_t)rx_data[2] << 8); + HandleDacSetRequest(name, rx_data[0], millivolts); + dac_can_last_command_ms[rx_data[0] - 1U] = HAL_GetTick(); + dac_can_watchdog_active[rx_data[0] - 1U] = true; } else { @@ -853,15 +771,15 @@ static void PollCanBus(CAN_HandleTypeDef *hcan, const char *name) memcpy(line, rx_data, length); line[length] = '\0'; HandleTextCommand(name, line); - } - } - else if (rx_header.StdId == CAN_ASSI_LAMP_CMD_STD_ID) - { - if ((rx_header.DLC >= 1U) && (rx_data[0] <= ASSI_STATE_FINISHED)) - { - HandleAssiLampSetRequest(name, rx_data[0]); - } - } + } + } + else if (rx_header.StdId == CAN_ASSI_LAMP_CMD_STD_ID) + { + if ((rx_header.DLC >= 1U) && (rx_data[0] <= ASSI_STATE_FINISHED)) + { + HandleAssiLampSetRequest(name, rx_data[0]); + } + } } } @@ -903,62 +821,68 @@ static void PollCaptureChannels(void) static void PollGpioChanges(void) { + uint32_t now = HAL_GetTick(); uint16_t state; uint16_t changed; + char line[96]; __disable_irq(); QueueGpioSnapshot(ReadGpioInputState()); + __enable_irq(); + + if ((now - last_gpio_report_ms) < GPIO_REPORT_PERIOD_MS) + { + return; + } + + __disable_irq(); changed = gpio_pending_changed_mask; state = gpio_report_state; gpio_pending_changed_mask = 0U; __enable_irq(); - if (changed != 0U) - { - char line[96]; - - gpio_last_state = state; - snprintf(line, - sizeof(line), - "GPIO changed mask=0x%04X state=0x%04X\r\n", - (unsigned int)changed, - (unsigned int)state); - UartWrite(line); - BroadcastGpioReport(changed, state); - } + last_gpio_report_ms = now; + gpio_last_state = state; + snprintf(line, + sizeof(line), + "GPIO state=0x%04X changed=0x%04X\r\n", + (unsigned int)state, + (unsigned int)changed); + UartWrite(line); + BroadcastGpioReport(changed, state); } static void SendPeriodicReport(void) { uint32_t now = HAL_GetTick(); - if ((now - last_adc_report_ms) >= ADC_REPORT_PERIOD_MS) - { + if ((now - last_adc_report_ms) >= ADC_REPORT_PERIOD_MS) + { uint32_t adc_mv = ReadAdcInputMillivolts(); char line[160]; - last_adc_report_ms = now; + last_adc_report_ms = now; snprintf(line, sizeof(line), - "ADC=%lumV PWM=%lu,%lu,%lu,%luHz\r\n", + "ADC=%lumV WheelSpeed=LF:%lu.%03lum/s RF:%lu.%03lum/s\r\n", (unsigned long)adc_mv, - (unsigned long)capture_channels[0].frequency_hz, - (unsigned long)capture_channels[1].frequency_hz, - (unsigned long)capture_channels[2].frequency_hz, - (unsigned long)capture_channels[3].frequency_hz); - UartWrite(line); - BroadcastAdcReport(adc_mv); - } - - if ((now - last_pwm_report_ms) >= PWM_REPORT_PERIOD_MS) - { - last_pwm_report_ms = now; - BroadcastPwmReport(); - } -} + (unsigned long)(WheelSpeedMmPerSecond(capture_channels[0].frequency_hz) / 1000U), + (unsigned long)(WheelSpeedMmPerSecond(capture_channels[0].frequency_hz) % 1000U), + (unsigned long)(WheelSpeedMmPerSecond(capture_channels[1].frequency_hz) / 1000U), + (unsigned long)(WheelSpeedMmPerSecond(capture_channels[1].frequency_hz) % 1000U)); + UartWrite(line); + BroadcastAdcReport(adc_mv); + } -static void ServiceHeartbeat(void) + if ((now - last_pwm_report_ms) >= PWM_REPORT_PERIOD_MS) + { + last_pwm_report_ms = now; + BroadcastPwmReport(); + } +} + +static void ServiceHeartbeat(void) { uint32_t now = HAL_GetTick(); @@ -967,71 +891,71 @@ static void ServiceHeartbeat(void) last_heartbeat_ms = now; HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_0); } -} - -static void ServiceAssiLamp(void) -{ - GPIO_PinState red = GPIO_PIN_RESET; - GPIO_PinState green = GPIO_PIN_RESET; - GPIO_PinState blue = GPIO_PIN_RESET; - bool flash_on = ((HAL_GetTick() / ASSI_LAMP_FLASH_HALF_PERIOD_MS) & 1U) == 0U; - - switch (assi_lamp_state) - { - case ASSI_STATE_READY: - red = GPIO_PIN_SET; - green = GPIO_PIN_SET; - break; - - case ASSI_STATE_DRIVING: - red = flash_on ? GPIO_PIN_SET : GPIO_PIN_RESET; - green = flash_on ? GPIO_PIN_SET : GPIO_PIN_RESET; - break; - - case ASSI_STATE_EMERGENCY: - blue = flash_on ? GPIO_PIN_SET : GPIO_PIN_RESET; - break; - - case ASSI_STATE_FINISHED: - blue = GPIO_PIN_SET; - break; - - case ASSI_STATE_OFF: - default: - break; - } - - HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, red); - HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10, green); - HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, blue); -} - -static void ServiceDacCanWatchdog(void) -{ - uint32_t now = HAL_GetTick(); - uint8_t dac_id; - - for (dac_id = 1U; dac_id <= 2U; dac_id++) - { - uint8_t index = dac_id - 1U; - - if (dac_can_watchdog_active[index] && - ((now - dac_can_last_command_ms[index]) >= DAC_CAN_TIMEOUT_MS)) - { - (void)DacSetMillivolts(dac_id, 0U); - dac_can_watchdog_active[index] = false; - } - } -} - -static void HandleAssiLampSetRequest(const char *source, uint8_t state) -{ - char reply[64]; - - assi_lamp_state = state; - snprintf(reply, sizeof(reply), "OK %s ASSI state=%u\r\n", source, (unsigned int)state); - UartWrite(reply); -} +} + +static void ServiceAssiLamp(void) +{ + GPIO_PinState red = GPIO_PIN_RESET; + GPIO_PinState green = GPIO_PIN_RESET; + GPIO_PinState blue = GPIO_PIN_RESET; + bool flash_on = ((HAL_GetTick() / ASSI_LAMP_FLASH_HALF_PERIOD_MS) & 1U) == 0U; + + switch (assi_lamp_state) + { + case ASSI_STATE_READY: + red = GPIO_PIN_SET; + green = GPIO_PIN_SET; + break; + + case ASSI_STATE_DRIVING: + red = flash_on ? GPIO_PIN_SET : GPIO_PIN_RESET; + green = flash_on ? GPIO_PIN_SET : GPIO_PIN_RESET; + break; + + case ASSI_STATE_EMERGENCY: + blue = flash_on ? GPIO_PIN_SET : GPIO_PIN_RESET; + break; + + case ASSI_STATE_FINISHED: + blue = GPIO_PIN_SET; + break; + + case ASSI_STATE_OFF: + default: + break; + } + + HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, red); + HAL_GPIO_WritePin(GPIOE, GPIO_PIN_10, green); + HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, blue); +} + +static void ServiceDacCanWatchdog(void) +{ + uint32_t now = HAL_GetTick(); + uint8_t dac_id; + + for (dac_id = 1U; dac_id <= 2U; dac_id++) + { + uint8_t index = dac_id - 1U; + + if (dac_can_watchdog_active[index] && + ((now - dac_can_last_command_ms[index]) >= DAC_CAN_TIMEOUT_MS)) + { + (void)DacSetMillivolts(dac_id, 0U); + dac_can_watchdog_active[index] = false; + } + } +} + +static void HandleAssiLampSetRequest(const char *source, uint8_t state) +{ + char reply[64]; + + assi_lamp_state = state; + snprintf(reply, sizeof(reply), "OK %s ASSI state=%u\r\n", source, (unsigned int)state); + UartWrite(reply); +} static void SendCanHeartbeat(void) { @@ -1049,7 +973,7 @@ static void SendCanHeartbeat(void) can_heartbeat_sequence++; gpio_state = ReadGpioInputState(); - /* CAN ID 0x6A0, sent independently on CAN1 and CAN2: + /* CAN ID 0x6A0, sent independently on CAN1 and CAN2: * [0] 0xA1 board identifier; [1] firmware protocol version 1; * [2..3] sequence counter, little-endian; [4..7] uptime in ms, little-endian. */ @@ -1338,6 +1262,12 @@ static uint32_t GetTimerClockHz(const TIM_HandleTypeDef *htim) return (pclk * multiplier) / prescaler; } +static uint32_t WheelSpeedMmPerSecond(uint32_t frequency_hz) +{ + return (uint32_t)(((uint64_t)frequency_hz * WHEEL_CIRCUMFERENCE_MM + + (WHEEL_PULSES_PER_REV / 2U)) / WHEEL_PULSES_PER_REV); +} + static void InitCaptureChannels(void) { capture_channels[0].htim = &htim5; @@ -1347,43 +1277,21 @@ static void InitCaptureChannels(void) capture_channels[0].timer_clock_hz = GetTimerClockHz(&htim5); capture_channels[0].last_edge_ms = HAL_GetTick(); - capture_channels[1].htim = &htim9; + capture_channels[1].htim = &htim3; capture_channels[1].channel = TIM_CHANNEL_1; capture_channels[1].flag = TIM_FLAG_CC1; capture_channels[1].counter_mask = 0xFFFFU; - capture_channels[1].timer_clock_hz = GetTimerClockHz(&htim9); + capture_channels[1].timer_clock_hz = GetTimerClockHz(&htim3); capture_channels[1].last_edge_ms = HAL_GetTick(); - capture_channels[2].htim = &htim3; - capture_channels[2].channel = TIM_CHANNEL_1; - capture_channels[2].flag = TIM_FLAG_CC1; - capture_channels[2].counter_mask = 0xFFFFU; - capture_channels[2].timer_clock_hz = GetTimerClockHz(&htim3); - capture_channels[2].last_edge_ms = HAL_GetTick(); - - capture_channels[3].htim = &htim12; - capture_channels[3].channel = TIM_CHANNEL_1; - capture_channels[3].flag = TIM_FLAG_CC1; - capture_channels[3].counter_mask = 0xFFFFU; - capture_channels[3].timer_clock_hz = GetTimerClockHz(&htim12); - capture_channels[3].last_edge_ms = HAL_GetTick(); - if (HAL_TIM_IC_Start(&htim5, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } - if (HAL_TIM_IC_Start(&htim9, TIM_CHANNEL_1) != HAL_OK) - { - Error_Handler(); - } if (HAL_TIM_IC_Start(&htim3, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } - if (HAL_TIM_IC_Start(&htim12, TIM_CHANNEL_1) != HAL_OK) - { - Error_Handler(); - } } static void ConfigureCanRuntime(CAN_HandleTypeDef *hcan) @@ -1480,14 +1388,15 @@ static void BroadcastAdcReport(uint32_t input_mv) static void BroadcastPwmReport(void) { - uint8_t payload[8]; + uint8_t payload[4]; size_t i; - for (i = 0U; i < 4U; i++) + for (i = 0U; i < 2U; i++) { - uint16_t clamped = (capture_channels[i].frequency_hz > 0xFFFFU) ? + uint32_t speed_mm_per_s = WheelSpeedMmPerSecond(capture_channels[i].frequency_hz); + uint16_t clamped = (speed_mm_per_s > 0xFFFFU) ? 0xFFFFU : - (uint16_t)capture_channels[i].frequency_hz; + (uint16_t)speed_mm_per_s; payload[i * 2U] = (uint8_t)(clamped & 0xFFU); payload[(i * 2U) + 1U] = (uint8_t)(clamped >> 8); } diff --git a/PC_Test/SDCAcqDacTester/MainWindow.xaml.cs b/PC_Test/SDCAcqDacTester/MainWindow.xaml.cs index f06df27..7304e18 100644 --- a/PC_Test/SDCAcqDacTester/MainWindow.xaml.cs +++ b/PC_Test/SDCAcqDacTester/MainWindow.xaml.cs @@ -146,8 +146,8 @@ public partial class MainWindow : Window case AdcId when d.Length >= 2: string adc = $"{U16(d, 0)} mV ({U16(d, 0) / 1000.0:F3} V)"; AdcValue.Text = adc; return adc; - case PwmId when d.Length >= 8: - string pwm = string.Join(" / ", Enumerable.Range(0, 4).Select(i => $"{U16(d, i * 2)} Hz")); + case PwmId when d.Length >= 4: + string pwm = $"左前 PA0={U16(d, 0) / 1000.0:F3} m/s / 右前 PA6={U16(d, 2) / 1000.0:F3} m/s"; PwmValue.Text = pwm; return pwm; case DacAckId when d.Length >= 4: string ack = $"DAC{d[1]} {(d[0] == 1 ? "OK" : "ERR")} = {U16(d, 2)} mV"; diff --git a/SDC-ACQ_DAC_固件交付说明.html b/SDC-ACQ_DAC_固件交付说明.html index ecbbee6..9de25b8 100644 --- a/SDC-ACQ_DAC_固件交付说明.html +++ b/SDC-ACQ_DAC_固件交付说明.html @@ -76,8 +76,8 @@
| 功能 | 当前实现 | ||
|---|---|---|---|
| 状态采集 | 采集 PD0–PD14 共 15 路数字输入。上电和任一输入变化时,向 CAN1、CAN2 发送完整状态与变化掩码。 | ||
| 频率采集 | 采集 PA0、PA2、PA6、PB14 的上升沿频率,每 20 ms 通过 CAN 输出;串口每 500 ms 汇总输出。 | ||
| 状态采集 | 采集 PD0–PD14 共 15 路数字输入。每 1 秒向 CAN1、CAN2 发送完整状态与该秒内累积的变化掩码。 | ||
| 轮速采集 | 仅采集左前 PA0、右前 PA6 的上升沿;以每圈 49 个方波、轮周长 1414 mm 换算为 m/s,每 20 ms 通过 CAN 输出。 | ||
| 电压采集 | ADC1_IN8(PB0)测量外部经分压后的输入;对外输出分压前、已标定且滤波后的电压。 | ||
| CAN | CAN1 与 CAN2 均为 500 kbit/s、11 位标准数据帧,并使用相同协议。 | ||
| DAC | 通过 I2C2 控制两颗 MCP4725,接收 CAN 命令或串口命令设置 0–5.000 V 输出。 |
| 接口 | MCU 引脚 / 外设 | 说明 | |
|---|---|---|---|
| 15 路状态输入 | PD0–PD14 / EXTI | bit0 对应 PD0,bit14 对应 PD14。 | |
| 频率输入 | PA0/TIM5、PA2/TIM9、PA6/TIM3、PB14/TIM12 | 上升沿输入捕获。 | |
| 轮速输入 | 左前 PA0/TIM5、右前 PA6/TIM3 | 上升沿输入捕获。 | |
| 电压输入 | PB0 / ADC1_IN8 | 报告值为分压前电压,单位 mV。 | |
| CAN1 / CAN2 | PB8/PB9;PB5/PB6 | 分别为 RX/TX。 | |
| DAC I2C | PB10/PB11 / I2C2 | DAC1 地址 0x61,DAC2 地址 0x60(7 位地址)。 | |
| CAN ID | 名称 | 方向 / 时机 | 数据定义 |
0x6A0 | 心跳 | 板卡发送,每 100 ms | A1 01 seq_L seq_H uptime_0..3;板号 A1、协议版本 1、序号、运行时间 ms。 |
0x6A1 | 输入电压 | 板卡发送,每 500 ms | uint16,单位 mV。例如 90 2F = 0x2F90 = 12176 mV = 12.176 V。 |
0x6A2 | 四路频率 | 板卡发送,每 20 ms | 4 个 uint16,单位 Hz;依次为 PA0、PA2、PA6、PB14。 |
0x6A2 | 双路轮速 | 板卡发送,每 20 ms | DLC=4;左前 PA0、右前 PA6 各一个 uint16,原始单位 mm/s(DBC 物理量纲 0.001 m/s)。按每圈 49 方波、轮周长 1414 mm 换算。 |
0x6A3 | DAC 设置 | 外部发送给板卡 | [DAC 编号][mV_L][mV_H],编号仅允许 1 或 2;每路 100 ms 未续帧归零。 |
0x6A4 | DAC 应答 | 板卡发送,设置后 | [结果][DAC 编号][mV_L][mV_H];结果 1 成功,0 失败。 |
0x6A5 | ASSI RGB 灯控 | 外部发送给板卡 | byte0:0=关、1=黄常亮、2=黄闪、3=蓝闪、4=蓝常亮。 |
0x6A6 | 15 路状态 | 板卡发送,上电或状态变化 | 状态 uint16、变化掩码 uint16、时间戳 uint32 ms。 |
0x6A6 | 15 路状态 | 板卡发送,每 1 s | 状态 uint16、变化掩码 uint16、时间戳 uint32 ms;变化掩码在本秒内累积。 |
ADC 输出为分压前输入电压,换算使用 100 kΩ / 30 kΩ 分压参数,并采用实测标定系数 12300 / 11656。单次报告包含 16 次 ADC 平均,随后对最近 4 次报告做移动平均;输出刷新周期为 500 ms,滤波时间约 2 秒。
串口示例:ADC=12300mV PWM=10,0,0,0Hz+
串口示例:ADC=12300mV WheelSpeed=LF:0.289m/s RF:0.577m/s
串口参数:115200、8 数据位、1 停止位、无校验、无流控。USB-TTL 应交叉连接:PA9/TX → 转换器 RX,PA10/RX → 转换器 TX,并连接公共地。
diff --git a/SDC_ACQ_DAC.dbc b/SDC_ACQ_DAC.dbc index 767c357..b1887d5 100644 --- a/SDC_ACQ_DAC.dbc +++ b/SDC_ACQ_DAC.dbc @@ -43,11 +43,9 @@ BO_ 1696 SDC_Heartbeat: 8 SDC_ACQ_DAC BO_ 1697 SDC_ADC_InputVoltage: 2 SDC_ACQ_DAC SG_ Input_Voltage : 0|16@1+ (0.001,0) [0|65.535] "V" VehicleController -BO_ 1698 SDC_InputFrequencies: 8 SDC_ACQ_DAC - SG_ Frequency_PA0 : 0|16@1+ (1,0) [0|65535] "Hz" VehicleController - SG_ Frequency_PA2 : 16|16@1+ (1,0) [0|65535] "Hz" VehicleController - SG_ Frequency_PA6 : 32|16@1+ (1,0) [0|65535] "Hz" VehicleController - SG_ Frequency_PB14 : 48|16@1+ (1,0) [0|65535] "Hz" VehicleController +BO_ 1698 SDC_WheelSpeed: 4 SDC_ACQ_DAC + SG_ WheelSpeed_LeftFront_PA0 : 0|16@1+ (0.001,0) [0|65.535] "m/s" VehicleController + SG_ WheelSpeed_RightFront_PA6 : 16|16@1+ (0.001,0) [0|65.535] "m/s" VehicleController BO_ 1699 SDC_DAC_Set: 3 VehicleController SG_ DAC_Select : 0|8@1+ (1,0) [1|2] "" SDC_ACQ_DAC @@ -74,15 +72,13 @@ VAL_ 1701 ASSI_State 0 "AS_OFF" 1 "AS_READY" 2 "AS_DRIVING" 3 "AS_EMERGENCY" 4 " CM_ BO_ 1696 "Standard ID 0x6A0. Sent every 100 ms on CAN1 and CAN2."; CM_ BO_ 1697 "Standard ID 0x6A1. Filtered and calibrated physical input voltage before the hardware divider."; -CM_ BO_ 1698 "Standard ID 0x6A2. Sent every 20 ms. Frequency order is PA0, PA2, PA6, PB14."; +CM_ BO_ 1698 "Standard ID 0x6A2. Sent every 20 ms with two wheel speeds: left-front PA0 then right-front PA6. Raw value is mm/s (physical scale 0.001 m/s), derived from 49 pulses/rev and 1414 mm wheel circumference."; CM_ BO_ 1699 "Standard ID 0x6A3. Accepted on CAN1 and CAN2. The on-wire voltage is an unsigned mV value. Each DAC is reset to 0 mV if its own control frame is not received again within 100 ms."; CM_ BO_ 1700 "Standard ID 0x6A4. Sent on CAN1 and CAN2 after a DAC command. Result 0 indicates the I2C write failed."; CM_ BO_ 1701 "Standard ID 0x6A5. Accepted on CAN1 and CAN2. Byte 0 selects the latched ASSI RGB lamp state: AS_OFF, AS_READY, AS_DRIVING, AS_EMERGENCY, or AS_FINISHED."; -CM_ BO_ 1702 "Standard ID 0x6A6. Sent at startup and whenever PD0-PD14 state changes, on CAN1 and CAN2."; +CM_ BO_ 1702 "Standard ID 0x6A6. Sent every 1000 ms on CAN1 and CAN2. Changed-mask bits accumulate PD0-PD14 transitions since the preceding report."; CM_ SG_ 1696 Board_Identifier "Fixed board identifier 0xA1."; -CM_ SG_ 1698 Frequency_PA0 "TIM5_CH1 input capture on PA0."; -CM_ SG_ 1698 Frequency_PA2 "TIM9_CH1 input capture on PA2."; -CM_ SG_ 1698 Frequency_PA6 "TIM3_CH1 input capture on PA6."; -CM_ SG_ 1698 Frequency_PB14 "TIM12_CH1 input capture on PB14."; +CM_ SG_ 1698 WheelSpeed_LeftFront_PA0 "Left-front wheel speed. TIM5_CH1 input capture on PA0; 49 pulses/rev and 1414 mm circumference."; +CM_ SG_ 1698 WheelSpeed_RightFront_PA6 "Right-front wheel speed. TIM3_CH1 input capture on PA6; 49 pulses/rev and 1414 mm circumference."; CM_ SG_ 1702 SDC_Input_State "Bit 0 through bit 14 map directly to PD0 through PD14."; -CM_ SG_ 1702 SDC_Changed_Mask "A set bit identifies an input that changed since the preceding GPIO-status frame."; +CM_ SG_ 1702 SDC_Changed_Mask "A set bit identifies an input that changed during the preceding 1000 ms reporting interval."; diff --git a/output/dbc/SDC_ACQ_DAC.dbc b/output/dbc/SDC_ACQ_DAC.dbc index 767c357..b1887d5 100644 --- a/output/dbc/SDC_ACQ_DAC.dbc +++ b/output/dbc/SDC_ACQ_DAC.dbc @@ -43,11 +43,9 @@ BO_ 1696 SDC_Heartbeat: 8 SDC_ACQ_DAC BO_ 1697 SDC_ADC_InputVoltage: 2 SDC_ACQ_DAC SG_ Input_Voltage : 0|16@1+ (0.001,0) [0|65.535] "V" VehicleController -BO_ 1698 SDC_InputFrequencies: 8 SDC_ACQ_DAC - SG_ Frequency_PA0 : 0|16@1+ (1,0) [0|65535] "Hz" VehicleController - SG_ Frequency_PA2 : 16|16@1+ (1,0) [0|65535] "Hz" VehicleController - SG_ Frequency_PA6 : 32|16@1+ (1,0) [0|65535] "Hz" VehicleController - SG_ Frequency_PB14 : 48|16@1+ (1,0) [0|65535] "Hz" VehicleController +BO_ 1698 SDC_WheelSpeed: 4 SDC_ACQ_DAC + SG_ WheelSpeed_LeftFront_PA0 : 0|16@1+ (0.001,0) [0|65.535] "m/s" VehicleController + SG_ WheelSpeed_RightFront_PA6 : 16|16@1+ (0.001,0) [0|65.535] "m/s" VehicleController BO_ 1699 SDC_DAC_Set: 3 VehicleController SG_ DAC_Select : 0|8@1+ (1,0) [1|2] "" SDC_ACQ_DAC @@ -74,15 +72,13 @@ VAL_ 1701 ASSI_State 0 "AS_OFF" 1 "AS_READY" 2 "AS_DRIVING" 3 "AS_EMERGENCY" 4 " CM_ BO_ 1696 "Standard ID 0x6A0. Sent every 100 ms on CAN1 and CAN2."; CM_ BO_ 1697 "Standard ID 0x6A1. Filtered and calibrated physical input voltage before the hardware divider."; -CM_ BO_ 1698 "Standard ID 0x6A2. Sent every 20 ms. Frequency order is PA0, PA2, PA6, PB14."; +CM_ BO_ 1698 "Standard ID 0x6A2. Sent every 20 ms with two wheel speeds: left-front PA0 then right-front PA6. Raw value is mm/s (physical scale 0.001 m/s), derived from 49 pulses/rev and 1414 mm wheel circumference."; CM_ BO_ 1699 "Standard ID 0x6A3. Accepted on CAN1 and CAN2. The on-wire voltage is an unsigned mV value. Each DAC is reset to 0 mV if its own control frame is not received again within 100 ms."; CM_ BO_ 1700 "Standard ID 0x6A4. Sent on CAN1 and CAN2 after a DAC command. Result 0 indicates the I2C write failed."; CM_ BO_ 1701 "Standard ID 0x6A5. Accepted on CAN1 and CAN2. Byte 0 selects the latched ASSI RGB lamp state: AS_OFF, AS_READY, AS_DRIVING, AS_EMERGENCY, or AS_FINISHED."; -CM_ BO_ 1702 "Standard ID 0x6A6. Sent at startup and whenever PD0-PD14 state changes, on CAN1 and CAN2."; +CM_ BO_ 1702 "Standard ID 0x6A6. Sent every 1000 ms on CAN1 and CAN2. Changed-mask bits accumulate PD0-PD14 transitions since the preceding report."; CM_ SG_ 1696 Board_Identifier "Fixed board identifier 0xA1."; -CM_ SG_ 1698 Frequency_PA0 "TIM5_CH1 input capture on PA0."; -CM_ SG_ 1698 Frequency_PA2 "TIM9_CH1 input capture on PA2."; -CM_ SG_ 1698 Frequency_PA6 "TIM3_CH1 input capture on PA6."; -CM_ SG_ 1698 Frequency_PB14 "TIM12_CH1 input capture on PB14."; +CM_ SG_ 1698 WheelSpeed_LeftFront_PA0 "Left-front wheel speed. TIM5_CH1 input capture on PA0; 49 pulses/rev and 1414 mm circumference."; +CM_ SG_ 1698 WheelSpeed_RightFront_PA6 "Right-front wheel speed. TIM3_CH1 input capture on PA6; 49 pulses/rev and 1414 mm circumference."; CM_ SG_ 1702 SDC_Input_State "Bit 0 through bit 14 map directly to PD0 through PD14."; -CM_ SG_ 1702 SDC_Changed_Mask "A set bit identifies an input that changed since the preceding GPIO-status frame."; +CM_ SG_ 1702 SDC_Changed_Mask "A set bit identifies an input that changed during the preceding 1000 ms reporting interval."; diff --git a/output/pdf/CAN报文说明表.pdf b/output/pdf/CAN报文说明表.pdf index 891a288..aedcfef 100644 --- a/output/pdf/CAN报文说明表.pdf +++ b/output/pdf/CAN报文说明表.pdf @@ -47,7 +47,7 @@ endobj endobj 6 0 obj << -/Author (\(anonymous\)) /CreationDate (D:20260922213520+08'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260922213520+08'00') /Producer (ReportLab PDF Library - \(opensource\)) +/Author (\(anonymous\)) /CreationDate (D:20261005194858+08'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261005194858+08'00') /Producer (ReportLab PDF Library - \(opensource\)) /Subject (\(unspecified\)) /Title (\376\377\000S\000D\000C\000-\000A\000C\000Q\000_\000D\000A\000C\000 \000C\000A\000N\000 b\245e\207\213\364f\016\210h) /Trapped /False >> endobj @@ -58,10 +58,10 @@ endobj endobj 8 0 obj << -/Filter [ /ASCII85Decode /FlateDecode ] /Length 2151 +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2289 >> stream -Gb!;fh/h((&:`#5Tg!QhR79`J0:Nh#kSMSP$(!`jF>-9*)FC,(+b;*3d(d7Eq=JfBT?eu4mA(J_G-tMWp:%YsNt[7tgpPI($2JI.DZ.K[#;UWl\>5;3[`OT:NOKjSc7(9DGcbbU^eb=EE#t/kdpSsqP\Y!#-5>25`Q8>,jC&p#AuE-A&74*;5hQ<+#\kghUsFKsU2DlJZ)MShYg)pNfs8lYd%/hO0X*qH#u>GTa,M)s+OtH6bV[+(,IDjG0Wq[u?@G7RYK6&WfueG('27o!b7%WQUJu*Td*9@N?pHe>"[WM^7BMuW#ZXPu'eJ$0;WTK1gIJjpc;[J8W2htJ.O/kT]IseM\CG4)BS8)L'4