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 @@

1. 功能概览

- - + + @@ -88,7 +88,7 @@
功能当前实现
状态采集采集 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)测量外部经分压后的输入;对外输出分压前、已标定且滤波后的电压。
CANCAN1 与 CAN2 均为 500 kbit/s、11 位标准数据帧,并使用相同协议。
DAC通过 I2C2 控制两颗 MCP4725,接收 CAN 命令或串口命令设置 0–5.000 V 输出。
- + @@ -101,11 +101,11 @@ - + - +
接口MCU 引脚 / 外设说明
15 路状态输入PD0–PD14 / EXTIbit0 对应 PD0,bit14 对应 PD14。
频率输入PA0/TIM5、PA2/TIM9、PA6/TIM3、PB14/TIM12上升沿输入捕获。
轮速输入左前 PA0/TIM5、右前 PA6/TIM3上升沿输入捕获。
电压输入PB0 / ADC1_IN8报告值为分压前电压,单位 mV。
CAN1 / CAN2PB8/PB9;PB5/PB6分别为 RX/TX。
DAC I2CPB10/PB11 / I2C2DAC1 地址 0x61,DAC2 地址 0x60(7 位地址)。
CAN ID名称方向 / 时机数据定义
0x6A0心跳板卡发送,每 100 msA1 01 seq_L seq_H uptime_0..3;板号 A1、协议版本 1、序号、运行时间 ms。
0x6A1输入电压板卡发送,每 500 msuint16,单位 mV。例如 90 2F = 0x2F90 = 12176 mV = 12.176 V。
0x6A2四路频率板卡发送,每 20 ms4 个 uint16,单位 Hz;依次为 PA0、PA2、PA6、PB14。
0x6A2双路轮速板卡发送,每 20 msDLC=4;左前 PA0、右前 PA6 各一个 uint16,原始单位 mm/s(DBC 物理量纲 0.001 m/s)。按每圈 49 方波、轮周长 1414 mm 换算。
0x6A3DAC 设置外部发送给板卡[DAC 编号][mV_L][mV_H],编号仅允许 1 或 2;每路 100 ms 未续帧归零。
0x6A4DAC 应答板卡发送,设置后[结果][DAC 编号][mV_L][mV_H];结果 1 成功,0 失败。
0x6A5ASSI RGB 灯控外部发送给板卡byte0:0=关、1=黄常亮、2=黄闪、3=蓝闪、4=蓝常亮。
0x6A615 路状态板卡发送,上电或状态变化状态 uint16、变化掩码 uint16、时间戳 uint32 ms。
0x6A615 路状态板卡发送,每 1 s状态 uint16、变化掩码 uint16、时间戳 uint32 ms;变化掩码在本秒内累积。

DAC 设置示例

@@ -118,7 +118,7 @@ DAC2 输出 1.140 V:ID = 0x6A3,DLC = 3,DATA = 02 74 04

4. ADC 电压说明

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

5. 串口使用

串口参数: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'42B$ps^%rTV1Mo4f9_s+>13[c#E56s>@OCfCSo\YX)$F`RaW7\S+Zu\=u42!i17Yg_o\o+Y2drl;^2J;aMhe$#2>&ti'[omFFCT'5o@fjP?31Sqi>a6-kfhBnoK-V8RjMah-CB*EY]OD2.cuU739?mu=XHfqF3`9f[aZ@Y/CIeZ\`A8XIeq8P`'ZcQiTNkUt:+g3gb[:ncYS=AWg/>EW_VEK^fS#6*/_DPQUD2WcUYu=)1?)pe@[:_,=CqD)4PffjTkp0A""Z*ejWK,4aIt%*u!>jl#/2CJ(4NaM!Op]KftHrB$s`THC;&?_nQtef@O@g!Y^o%OE[MI;jT*]2D]p_0IX:[cjFQKPrP_:m5")Em]T`n9VqnQM,Rm*f=fiTOH9q]=;N+Kp]!>%tdoV:Btb5N40jh!]4#K?F)BM(t8<5GMs:t&CaEsKh*")O^>!A!72N)]`@hQ(`3_S10k'T5W+F+VbI"(E.?(G`0u=0%6YDoqr0UEb24uHKM.tC5mpo2p#M$=UWG+R0c:9dep%P&;,YN\J/@f_S8,H\'`t9.5lEkCE<)F]9k^jEO^eLX)6BM"El)p&F=E_fF*tVQE4Di.k"S^"abUg1>9I^g!"maLc9^L\6dr,g3!k[bL*gQe"mYo](RV_jt0$\QF8C"A6M5&ZVe"^_'W#(JJ(0q$&Ho)hdFQm,fI?FEZp]C<%otU\a/LVs45"e?V+abtl0mQU_\8Q?n"(Vi'Dj"YSnd;ur85IY>'=W4o,el]kn6K`UKo&LX.(u]9,9\IEND$cJhhrhgo-jaicR0epbX=?YrUa%%oQ.$NFnV_scF9TL+dU_UnS3pk@CEs!1N5@OZk>_^o4i=F(,'YPALHFot\9XBMM9m!426P^knmOB"ogSgQf&qL3]!D7m/(puhEE>cBVN=Eendstream +Gau`VD0+2b&H88.Tml3e1.=aGHEims"+>ku?@'.,%"h.*U`mAsKE*Tt;gs'pnl`8Gp0FbNA.B=.>-1i+I3,M$9.&jMI\Q@F'SJ^R%'#[;ku$B9@4$#/Tm4TCGd?F&6YFt9tW>VMjYT,XTgW86(h5-SMmjb"jPXQHqPuOs]N)p3Rg`CJB<+p+p&DW7gKaM*Hcn=V[*d&lj.E[Ge!9;DKpe#V:\I.CB7lQj(niYTt/%4$R3#DN[olS'/kc=u'dR1;$Ra>1-Mf;]#k;dXkM2BVdAU)07X$Wtek&'3V#1'eAt*dq'_p["pMk`rR_fQ6orSot1S1W!bM*4BI0;i$N1>aUPd.5V8s?O-(BVWRYB"QAjp=!-4&B5*Aqb4_,$7R[(3OJ.1S99bn/#7ds=>D"f?o5o3o6*<]8@W6DWcQ;M@s"f&kpP'eo#Y,'LMCsG/jCj6^(7O?:pCdM!<'NX?7eMC?@LSmot5hLD0fJ)bOBMRR=AG&Sg'3ITV&ngi7BlH_>?oXf8Xg>YOed_oVE@%`Xror,6RgK,sYO@2llsV_.dC(=Oo8H,4ItN&$emDEJ$Wgi2lrcYNr,N[[R4'&06)JRO(R/tl`Bb)aQUib9Il#FE&fW5i]FbOVhO$FPs1L6L&aA[EhLKc?<*5s,jE[me.Z0X.]GpuV4LBHY(q>oN#bDOIG&roojmpm'%@$DWM_JJc\><@j'Vt>PG8Y7l7pk7ksGIBBu%/i"#Br>#$B?0gRG1GGJgR3ib^!g`)QjJ`W@a@]'BB#T!=M4QaBu3,g]pZ`Y)Rp(s@$e,qu49@n_9LjdfF$MLCT?"hm@Z.EsI?$LK.d(.;hYCUncq#eNX&jB?rbWu&nSCu_MLMsNj-rZ2a0HHE"k'79%>F+-E.JbCf-%MpkgMr[Q"SA0$\EpL#QU%=]'gDJj4FEksZ10'`UPC?Vhm`Gdo,M,;EpPXJ\S6l!HiG7uaK]M*6p=s9hdWQ7)Q!STfa:8a[V04Bbb[)TdiCH#Q1i-%e;<`]QM2%o8D`a)7&P%Q:Y?B,29]t^qo%HDQm?@]t(bXC80mPFh<6]%Bi=C0,CV>UerYf%glDMf*G9$dA*LC]Ohlq>bgjSP?DuK&Q:nkHd;!O19bV\`k4)%"Kp)P!k%<#JPGX&4_B\[ed1D6=H,AgG%\CqGuZ2p5/O3UJ0m>A.qnhUng[Rb_Zb8@e*5Bo$QYYBN"c@Y5d(fF`KUlK%3,8._pc.4X^2S;*YM%E,M0=SD*fH=l94U;Mqm`>"j_dGN::bYYTaK7L(gE7sUGB9_8J<%IW1mQ`G>/7>`+h050MjQp)cLeuLEs:1fu-mGRog1*H.0@8NjU:>L7#"O2bj6]kbAiL(N*6$C=b+h71a]:;bU==4lp@:,5kcWad3C:de`[9R"&@o&`0VplPFcR]DL"$2FBBLNV_"m,U6drX,>1/=PT#+[^F+.K^K>P:tX'W>OV>SUI>4@DAKCLl:<-g-mA^ICufrl[jJ"d(Aj6Sd&Y/000]s1@fj+45FK'i8H~>endstream endobj xref 0 9 @@ -77,7 +77,7 @@ xref trailer << /ID -[] +[<3d22dd34981c7eff4009fb12bd7de53c><3d22dd34981c7eff4009fb12bd7de53c>] % ReportLab generated PDF document -- digest (opensource) /Info 6 0 R @@ -85,5 +85,5 @@ trailer /Size 9 >> startxref -4097 +4235 %%EOF diff --git a/固件使用说明.md b/固件使用说明.md index bdfb0d1..9592f72 100644 --- a/固件使用说明.md +++ b/固件使用说明.md @@ -11,8 +11,8 @@ 1. 接收并处理串口命令。 2. 接收 CAN1、CAN2 的 DAC 控制帧。 3. 读取四路定时器捕获值并计算频率。 -4. 汇总 15 路 GPIO 变化并上报。 -5. 每 500 ms 上报 ADC、每 20 ms 上报四路频率。 +4. 汇总 15 路 GPIO 状态与本周期变化,并每秒上报一次。 +5. 每 500 ms 上报 ADC、每 20 ms 上报双路轮速。 6. 每 500 ms 翻转运行指示灯,每 100 ms 发送 CAN 心跳。 `PE0` 为正常运行心跳灯;若进入 `Error_Handler()`,`PE0` 和 `PE1` 会快速同时翻转。 @@ -22,10 +22,8 @@ | 功能 | MCU 引脚/外设 | 当前配置 | |---|---|---| | 15 路状态输入 | `PD0`~`PD14` | 双边沿 EXTI,状态位直接对应端口位 | -| 频率 1 | `PA0` / `TIM5_CH1` | 上升沿输入捕获,32 位计数器,16 MHz 计数时钟 | -| 频率 2 | `PA2` / `TIM9_CH1` | 上升沿输入捕获,16 位计数器,100 kHz 计数时钟 | -| 频率 3 | `PA6` / `TIM3_CH1` | 上升沿输入捕获,16 位计数器,100 kHz 计数时钟 | -| 频率 4 | `PB14` / `TIM12_CH1` | 上升沿输入捕获,16 位计数器,100 kHz 计数时钟 | +| 左前轮速 | `PA0` / `TIM5_CH1` | 上升沿输入捕获,32 位计数器,16 MHz 计数时钟 | +| 右前轮速 | `PA6` / `TIM3_CH1` | 上升沿输入捕获,16 位计数器,100 kHz 计数时钟 | | 电压采集 | `PB0` / `ADC1_IN8` | 480 周期采样,16 次过采样 + 4 次报告均值滤波 | | CAN1 | `PB8` RX、`PB9` TX | 标准帧,500 kbit/s | | CAN2 | `PB5` RX、`PB6` TX | 标准帧,500 kbit/s | @@ -38,15 +36,15 @@ ## 3. 15 路状态输入 -`PD0`~`PD14` 的逻辑电平组成一个 15 位无符号数:bit0 对应 `PD0`,bit14 对应 `PD14`。任何一路变化会触发 EXTI 回调,主循环中合并同一时段的变化并输出: +`PD0`~`PD14` 的逻辑电平组成一个 15 位无符号数:bit0 对应 `PD0`,bit14 对应 `PD14`。任何一路变化会触发 EXTI 回调,变化掩码会在当前 1 秒周期内累积;`0x6A6` 固定每秒发送一次: ```text GPIO changed mask=0x0004 state=0x0005 ``` - `state`:当前 15 路完整状态。 -- `mask`:相对于上一次 GPIO 状态帧发生变化的位。 -- 上电时也会发送一帧状态,变化掩码为 0。 +- `mask`:过去 1 秒内发生过变化的位;同一路即使变化多次也只置 1。 +- 上电后首个周期结束时发送第一帧;若无变化,变化掩码为 0。 每秒串口还会打印一次完整状态,例如 `GPIO state=0x0005`。 @@ -61,16 +59,16 @@ ADC 原始值 -> 3.3 V ADC 电压 -> 100 kΩ / 30 kΩ 分压反算 -> 标定系 标定系数固定为 `12300 / 11656`:即以实测输入 12.3 V、旧换算结果 11656 mV 为基准。每次报告读取 16 次 ADC 后求平均,再对最近 4 次报告做移动平均;报告周期为 500 ms,滤波窗口约 2 秒。输出示例: ```text -ADC=12300mV PWM=10,0,0,0Hz +ADC=12300mV WheelSpeed=LF:0.289m/s RF:0.577m/s ``` 若硬件分压电阻或外部标定基准改变,应同步修改 `main.c` 中的 `ADC_DIVIDER_*` 和 `ADC_CALIBRATION_*` 宏。 -## 5. 四路频率输入 +## 5. 双路轮速输入 -四路频率按 `PA0`、`PA2`、`PA6`、`PB14` 顺序输出。频率由相邻两个上升沿的计数差计算;超过 1 秒没有边沿则显示 0 Hz。当前固件只测频率,不测 PWM 占空比。 +仅采集左前轮速 `PA0` 和右前轮速 `PA6`。频率由相邻两个上升沿的计数差计算;超过 1 秒没有边沿则输出 0 m/s。每圈 49 个方波、轮周长 1414 mm,因此 `轮速(m/s) = 方波频率(Hz) × 1.414 / 49`。 -后三路 16 位定时器采用 100 kHz 计数时钟,以避免低频输入时 16 位溢出。其可无歧义测量的最低频率约为 1.53 Hz;TIM5 为 32 位计数器,低频范围更大。 +`0x6A2` 每 20 ms 发送两个 `uint16`:左前在 Byte0–1,右前在 Byte2–3。原始值的单位为 mm/s,DBC 的物理量纲为 `0.001 m/s`;最高表示 65.535 m/s。PA6 的 TIM3 为 100 kHz、16 位计数器,低频无歧义测量下限约 1.53 Hz;PA0 的 TIM5 为 32 位计数器,低频范围更大。 ## 6. CAN 通信 @@ -80,11 +78,11 @@ CAN1 和 CAN2 使用相同协议,所有主动上报均分别发送到两条总 |---:|---|---|---| | `0x6A0` | 心跳 | 每 100 ms | `A1 01 seq_L seq_H uptime_0..3` | | `0x6A1` | ADC 电压 | 每 500 ms | 输入电压 `uint16`,单位 mV | -| `0x6A2` | 四路频率 | 每 20 ms | 4 个 `uint16`,单位 Hz,顺序 PA0/PA2/PA6/PB14 | +| `0x6A2` | 双路轮速 | 每 20 ms | 左前 PA0、右前 PA6 各一个 `uint16`,单位 `0.001 m/s`,DLC=4 | | `0x6A3` | DAC 设置命令 | 接收 | `[DAC 编号][mV_L][mV_H]`;每路在 100 ms 内未收到本路下一帧时归零 | | `0x6A4` | DAC 应答 | 每次 DAC 设置后 | `[结果][DAC 编号][mV_L][mV_H]` | | `0x6A5` | ASSI 灯控 | 接收 | `byte0`:`0=AS_OFF`、`1=AS_READY`、`2=AS_DRIVING`、`3=AS_EMERGENCY`、`4=AS_FINISHED` | -| `0x6A6` | GPIO 状态 | 上电、任一输入变化 | 状态 `uint16`、变化掩码 `uint16`、时间戳 `uint32 ms` | +| `0x6A6` | GPIO 状态 | 每 1 s | 状态 `uint16`、变化掩码 `uint16`、时间戳 `uint32 ms` | 心跳帧中板号为 `0xA1`、协议版本为 `0x01`,序号与上电时间均为低字节在前。完整信号定义和量纲以 [DBC 文件](SDC_ACQ_DAC.dbc) 为准。