polarimeter_software/User/board/pm_board.c
2025-09-30 10:37:23 +08:00

553 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @Author: mypx
* @Email: mypx_coder@163.com
* @Date: 2025-06-19 12:29:17
* @LastEditors: mypx mypx_coder@163.com
* @Description:
*/
#include "pm_board.h"
#include "ad7793.h"
#include "adc.h"
#include "dma.h"
#include "et_log.h"
#include "gpio.h"
#include "i2c.h"
#include "rtdef.h"
#include "rthw.h"
#include "rtthread.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include <stdbool.h>
#include <string.h>
#define AT24C128_I2C_ADDR 0xA0
#if (DEBUG_MB_ENABLE == 1)
#define DBG_MB(...) ET_DBG("[MB] " __VA_ARGS__)
#else
#define DBG_MB(...)
#endif
volatile uint64_t timestamp_tim_overflow_count = 0;
#if (USING_SOFT_SPI == 1)
soft_spi_t soft_spi = {
.sck_port = GPIOB,
.sck_pin = GPIO_PIN_5,
.mosi_port = GPIOB,
.mosi_pin = GPIO_PIN_6,
.miso_port = GPIOB,
.miso_pin = GPIO_PIN_4,
.cs_port = GPIOB,
.cs_pin = GPIO_PIN_7,
.mode = SOFT_SPI_MODE0,
.bit_order = SOFT_SPI_MSB_FIRST,
.lock = RT_NULL, // 使用 RT_NULL初始化时会创建
.delay_us = 1, // 每半个 SPI 时钟周期延时 1 微秒
};
void pm_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
{
soft_spi_transfer_buffer(&soft_spi, tx_buf, rx_buf, len);
}
void pm_ad7793_spi_write(uint8_t cmd, uint8_t *data, uint16_t len)
{
soft_spi_write(&soft_spi, &cmd, 1, data, len);
}
void pm_ad7793_spi_read(uint8_t cmd, uint8_t *rx_buf, uint16_t len)
{
soft_spi_read(&soft_spi, &cmd, 1, rx_buf, len);
}
void pm_ad7793_gpio_set(uint8_t pin, bool state)
{
if (pin == soft_spi.cs_pin)
{
HAL_GPIO_WritePin(soft_spi.cs_port, soft_spi.cs_pin, state);
}
}
bool pm_ad7793_gpio_get(uint8_t pin)
{
if (pin == soft_spi.miso_pin)
{
return HAL_GPIO_ReadPin(soft_spi.miso_port, soft_spi.miso_pin) == GPIO_PIN_SET;
}
return false;
}
#else
#define AD7793_CS_LOW() HAL_GPIO_WritePin(AD7793_CS_GPIO_Port, AD7793_CS_Pin, GPIO_PIN_RESET)
#define AD7793_CS_HIGH() HAL_GPIO_WritePin(AD7793_CS_GPIO_Port, AD7793_CS_Pin, GPIO_PIN_SET)
void pm_ad7793_spi_transfer(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len)
{
HAL_StatusTypeDef status;
status = HAL_SPI_TransmitReceive(&AD7793_SPI, tx_buf, rx_buf, len, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI transfer error:%d!", status);
}
}
void pm_ad7793_spi_write(uint8_t cmd, uint8_t *data, uint16_t len)
{
uint8_t tx_buffer[256];
HAL_StatusTypeDef status;
tx_buffer[0] = cmd;
if (data && len > 0)
{
for (uint16_t i = 0; i < len; i++)
{
tx_buffer[i + 1] = data[i];
}
}
status = HAL_SPI_Transmit(&AD7793_SPI, tx_buffer, len + 1, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI write error:%d!", status);
}
}
void pm_ad7793_spi_read(uint8_t cmd, uint8_t *rx_buf, uint16_t len)
{
uint8_t tx_buffer[1] = {cmd};
HAL_StatusTypeDef status;
status = HAL_SPI_Transmit(&AD7793_SPI, tx_buffer, 1, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI write error:%d!", status);
}
status = HAL_SPI_Receive(&AD7793_SPI, rx_buf, len, 1000);
if (status != HAL_OK)
{
ET_ERR("SPI read error:%d!", status);
}
}
void pm_ad7793_gpio_set(uint8_t pin, bool state)
{
if (pin == SPI3_CS_AD7793_Pin)
{
HAL_GPIO_WritePin(SPI3_CS_AD7793_GPIO_Port, SPI3_CS_AD7793_Pin, state);
}
}
bool pm_ad7793_gpio_get(uint8_t pin)
{
if (pin == SPI3_MISO_AD7793_Pin)
{
return HAL_GPIO_ReadPin(SPI3_MISO_AD7793_GPIO_Port, SPI3_MISO_AD7793_Pin) == GPIO_PIN_SET;
}
return false;
}
#endif
void pm_ad7793_delay_ms(uint16_t ms)
{
rt_thread_mdelay(ms); // 使用 RT-Thread 的延时函数
}
ad7793_hw_if_t ad7793_hw_if = {
.spi_transfer = pm_ad7793_spi_transfer,
.spi_write = pm_ad7793_spi_write,
.spi_read = pm_ad7793_spi_read,
.gpio_set = pm_ad7793_gpio_set,
.gpio_get = pm_ad7793_gpio_get,
.delay_ms = pm_ad7793_delay_ms,
};
#if (ENABLE_SV630P == 1)
struct rt_messagequeue servo_mq;
static char mq_pool[SERVO_RX_QUEUE_SIZE];
#endif // USING_SOFT_SPI
void pm_system_led_toggle(void)
{
HAL_GPIO_TogglePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin);
}
void pm_system_led_on(void)
{
HAL_GPIO_WritePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin, GPIO_PIN_SET);
}
void pm_system_led_off(void)
{
HAL_GPIO_WritePin(PE5_LED1_GPIO_Port, PE5_LED1_Pin, GPIO_PIN_RESET);
}
void pm_error_led_on(void)
{
HAL_GPIO_WritePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin, GPIO_PIN_SET);
}
void pm_error_led_off(void)
{
HAL_GPIO_WritePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin, GPIO_PIN_RESET);
}
void pm_error_led_toggle(void)
{
HAL_GPIO_TogglePin(PE6_LED2_GPIO_Port, PE6_LED2_Pin);
}
int pm_i2c_write_bytes(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data, uint16_t len)
{
HAL_StatusTypeDef res;
// AT24C128 单页为 64 字节,写入时建议不要跨页,否则需要拆包
if (len > 64)
return HAL_ERROR;
res = HAL_I2C_Mem_Write(&hi2c2, dev_addr, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
HAL_Delay(5); // EEPROM写入周期典型为5ms
return res;
}
int pm_i2c_read_bytes(uint16_t dev_addr, uint16_t mem_addr, uint8_t *data, uint16_t len)
{
return HAL_I2C_Mem_Read(&hi2c2, dev_addr, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, HAL_MAX_DELAY);
}
void pm_uart_print_send(const uint8_t *data, uint16_t len)
{
#ifdef USING_RTT_AS_CONSOLE
#warning "USING_RTT_AS_CONSOLE defined, use uart1 as hmi interface"
#else
HAL_UART_Transmit(&huart1, data, len, 1000);
#endif
}
void pm_lcd_cmd_send(const uint8_t *data, uint16_t len)
{
//#ifdef USING_RTT_AS_CONSOLE
HAL_UART_Transmit(&huart1, data, strlen(data), 1000);
// #else
// HAL_UART_Transmit(&huart2, data, strlen(data), 1000);
// #endif
}
struct rt_messagequeue mb_hmi_mq;
static char mq_pool[MB_HMI_RX_QUEUE_SIZE];
int pm_mb_hmi_init(void)
{
MX_USART1_UART_Init(); // used tx
MX_USART2_UART_Init(); // used rx
rt_err_t result = rt_mq_init(&mb_hmi_mq, "mb_hmi_mq", mq_pool, 1, MB_HMI_RX_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
ET_ERR("mb_hmi mq init failed\n");
return -1;
}
//__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&HMI_COM, UART_IT_RXNE);
return 0;
}
int32_t pm_hmi_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
uint16_t bytes_read = 0;
uint8_t byte;
rt_tick_t timeout_ticks = rt_tick_from_millisecond(byte_timeout_ms);
DBG_MB("start read:count:%d, timeout:%d\n", count, byte_timeout_ms);
while (bytes_read < count)
{
rt_err_t result = rt_mq_recv(&mb_hmi_mq, &byte, sizeof(byte), timeout_ticks);
if (result == RT_EOK)
{
DBG_MB("rx: %02X\n", byte);
buf[bytes_read++] = byte;
}
else
{
if (bytes_read > 0)
{
DBG_MB("Modbus read finished %d\n", bytes_read);
return bytes_read;
}
DBG_MB("HMI read timeout, no data received\n");
return 0;
}
}
DBG_MB("Actually read:%d, count:%d\r\n", bytes_read, count);
return bytes_read;
}
int32_t pm_hmi_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
rt_thread_mdelay(2);
//#ifdef USING_RTT_AS_CONSOLE
if (HAL_UART_Transmit(&huart1, buf, count, byte_timeout_ms) == HAL_OK)
// #else
// if (HAL_UART_Transmit(&HMI_COM, buf, count, byte_timeout_ms) == HAL_OK)
// #endif
{
ET_DBG("HMI write:%d", count);
return count;
}
else
{
return 0;
}
}
#if (ENABLE_SV630P == 1)
int pm_servo_init(void)
{
MX_USART3_UART_Init();
rt_err_t result = rt_mq_init(&servo_mq, "servo_mq", mq_pool, 1, SERVO_RX_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
ET_ERR("servo mq init failed\n");
return -1;
}
__HAL_UART_ENABLE_IT(&SERVO_COM, UART_IT_RXNE);
return 0;
}
int32_t pm_servo_uart_read(uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
uint16_t bytes_read = 0;
uint8_t byte;
rt_tick_t timeout_ticks = rt_tick_from_millisecond(byte_timeout_ms);
DBG_MB("start read:count:%d, timeout:%d\n", count, byte_timeout_ms);
while (bytes_read < count)
{
rt_err_t result = rt_mq_recv(&servo_mq, &byte, sizeof(byte), timeout_ticks);
if (result == RT_EOK)
{
DBG_MB("rx: %02X\n", byte);
buf[bytes_read++] = byte;
}
else
{
if (bytes_read > 0)
{
DBG_MB("Modbus read finished %d\n", bytes_read);
return bytes_read;
}
DBG_MB("Servo read timeout, no data received\n");
return 0;
}
}
DBG_MB("Actually read:%d, count:%d\r\n", bytes_read, count);
return bytes_read;
}
int32_t pm_servo_uart_write(const uint8_t *buf, uint16_t count, int32_t byte_timeout_ms, void *arg)
{
UNUSED(arg);
rt_thread_mdelay(2);
if (HAL_UART_Transmit(&SERVO_COM, buf, count, byte_timeout_ms) == HAL_OK)
{
return count;
}
else
{
return 0;
}
}
#endif // ENABLE_SV630P
#if (ENABLE_TEC == 1)
void pm_tec_init(void)
{
#if (USING_SOFT_SPI == 0)
MX_SPI3_Init();
#endif
MX_TIM1_Init();
}
void pm_tec_start(void)
{
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
}
void pm_tec_stop(void)
{
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);
}
void pm_tec_set_duty(float pid_output)
{
uint32_t period = htim1.Init.Period;
// 限制PID输出范围
const float MIN_POWER = 2.0f; // 最小有效功率(%)
if (pid_output > 100.0f)
pid_output = 100.0f;
if (pid_output < -100.0f)
pid_output = -100.0f;
// 应用最小功率限制
if (pid_output > 0 && pid_output < MIN_POWER)
pid_output = MIN_POWER;
if (pid_output < 0 && pid_output > -MIN_POWER)
pid_output = -MIN_POWER;
// 计算占空比
uint32_t compare_value;
if (pid_output >= 0)
{
// 制热占空比50%~0%
compare_value = period / 2 + (uint32_t)(period * pid_output / 200.0f);
}
else
{
// 制热占空比50%~0%
compare_value = period / 2 - (uint32_t)(period * fabs(pid_output) / 200.0f);
}
ET_DBG("PWM COMAPRE: %d", compare_value);
// 设置PWM占空比
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, compare_value);
//__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 2);
}
void pm_fan_start(void)
{
HAL_GPIO_WritePin(FAN_CONTROL_GPIO_Port, FAN_CONTROL_Pin, GPIO_PIN_SET);
}
void pm_fan_stop(void)
{
HAL_GPIO_WritePin(FAN_CONTROL_GPIO_Port, FAN_CONTROL_Pin, GPIO_PIN_RESET);
}
#endif // ENABLE_TEC
void pm_timestamp_tim_init(void)
{
MX_TIM4_Init();
}
void pm_timestamp_start(void)
{
__HAL_TIM_SET_COUNTER(&TIMESTAMP_TIM, 0);
timestamp_tim_overflow_count = 0;
HAL_TIM_Base_Start_IT(&TIMESTAMP_TIM);
}
void pm_timestamp_stop(void)
{
HAL_TIM_Base_Stop_IT(&TIMESTAMP_TIM);
}
uint64_t pm_get_timestamp_us(void)
{
uint16_t current_cnt = __HAL_TIM_GET_COUNTER(&TIMESTAMP_TIM);
return (uint64_t)timestamp_tim_overflow_count * 65536 + current_cnt;
}
void pm_adc_init(void)
{
MX_DMA_Init();
MX_ADC1_Init();
}
uint32_t pm_adc_get_value(void)
{
if (HAL_ADC_PollForConversion(&LIGHT_ADC, 10) != HAL_OK)
{
ET_ERR("ADC read should not wait!");
pm_error_led_on();
return 0;
}
return HAL_ADC_GetValue(&LIGHT_ADC);
}
void pm_sampling_tim_init(void)
{
MX_TIM3_Init();
}
void pm_sampling_tim_start(void)
{
HAL_TIM_Base_Start_IT(&SAMPLING_TIM);
}
void pm_sampling_tim_stop(void)
{
HAL_TIM_Base_Stop_IT(&SAMPLING_TIM);
}
void pm_sampling_adc_start(uint16_t *buffer, uint32_t length)
{
if (HAL_ADC_Start_DMA(&LIGHT_ADC, (uint32_t *)buffer, length) != HAL_OK)
{
ET_ERR("ADC start DMA error!");
pm_error_led_on();
}
//HAL_ADC_Start(&LIGHT_ADC);
}
void pm_sampling_adc_stop(void)
{
//HAL_ADC_Stop(&hadc1);
HAL_ADC_Stop_DMA(&LIGHT_ADC);
}
float pm_sampling_tim_get_freq(void)
{
uint32_t pclk1 = HAL_RCC_GetPCLK1Freq();
uint32_t timer_clk;
// 获取APB1预分频系数来自RCC配置寄存器
uint32_t apb1_prescaler = (RCC->CFGR & RCC_CFGR_PPRE1) >> RCC_CFGR_PPRE1_Pos;
// 根据APB1预分频计算定时器时钟源
if (apb1_prescaler == RCC_HCLK_DIV1)
{
// APB1预分频=1定时器时钟=PCLK1
timer_clk = pclk1;
}
else
{
// APB1预分频>1定时器时钟=2×PCLK1
timer_clk = pclk1 * 2;
}
// 计算定时器计数频率经过PSC分频后
uint32_t counter_freq = timer_clk / (SAMPLING_TIM.Init.Prescaler + 1);
// 计算最终输出频率(周期触发频率 = 计数频率 / (Period + 1)
// 注意如果是向上计数模式完整周期是从0到Period共Period+1个计数
return (float)counter_freq / (SAMPLING_TIM.Init.Period + 1);
}
int pm_board_init(void)
{
MX_GPIO_Init();
// MX_USART1_UART_Init();
#if (USING_SOFT_SPI == 1)
soft_spi_init(&soft_spi);
soft_spi_set_mode(&soft_spi, SOFT_SPI_MODE3); // 设置 SPI 模式
soft_spi_set_bit_order(&soft_spi, SOFT_SPI_MSB_FIRST); // 设置位序
soft_spi_set_delay(&soft_spi, 1); // 设置延时为 1 微秒
soft_spi_select(&soft_spi); // 选择 SPI 设备
#endif
return 0;
}