/* * @Author: mypx * @Description: ADS8866采集-3线模式(TIM3中断驱动) */ #include "bsp_collect.h" #include "bsp_misc.h" #include "et_log.h" #include "main.h" #include "spi.h" #include "tim.h" #include #include static uint16_t *g_buffer = NULL; static uint16_t g_buf_size = 0; static uint16_t g_target_count = 0; // 目标采集数 static volatile uint16_t g_count = 0; // 已采集数 static dma_full_cb_t g_complete_cb = NULL; // 3线模式: DIN保持高, CONVST控制转换+片选 #define ADS8866_DIN_HIGH() GPIOB->BSRR = (1u << 15) #define PB15_AS_GPIO() GPIOB->CRH = (GPIOB->CRH & ~(0xFu << 28)) | (0x3u << 28) // 延时n个时钟周期(每个周期≈13.89ns) static inline void ns_delay(uint32_t cycles) { // 关闭编译器优化,确保NOP不被删除 __asm volatile("1: SUBS %0, %0, #1\n" // 计数器减1 " BNE 1b" // 若未到0则循环 : "=r"(cycles) // 输出操作数 : "0"(cycles) // 输入操作数 ); } void delay_100ns(void) { ns_delay(7); } // 单次读取(在中断里调用,要快速) static inline uint16_t ads8866_read_once(void) { uint16_t rx = 0; // ADS8866转换时间约3µs, 等待转换完成 // 72MHz系统时钟, 1个NOP约14ns, 300个NOP约4.2µs for (volatile int i = 0; i < 6; i++) delay_100ns(); // CONVST回到高电平, 数据就绪, 直接SPI读取 HAL_SPI_Receive(&ADS8866_SPI, (uint8_t *)&rx, 1, 10); return rx; } int bsp_light_data_sampling_init(uint16_t *adc_buf, uint16_t length, dma_full_cb_t cb) { g_buffer = adc_buf; g_buf_size = length; g_complete_cb = cb; g_count = 0; g_target_count = 0; // 初始化TIM3(产生CONVST), SPI2(读取数据) MX_TIM3_Init(); MX_SPI2_Init(); __HAL_SPI_ENABLE(&ADS8866_SPI); // PB15(DIN)配置为GPIO并保持高电平 PB15_AS_GPIO(); ADS8866_DIN_HIGH(); ET_INFO("bsp_collect: init done, buf_size=%u", length); return 0; } // // Shell命令: 启动采集指定数量 // int cstart(int argc, char **argv) // { // if (argc < 2) // { // rt_kprintf("Usage: cstart \r\n"); // return -1; // } // int count = atoi(argv[1]); // if (count <= 0 || count > g_buf_size) // { // rt_kprintf("cstart: invalid count (1~%u)\r\n", g_buf_size); // return -2; // } // if (!g_buffer) // { // rt_kprintf("cstart: buffer not init\r\n"); // return -3; // } // // 设置目标数量并启动 // g_count = 0; // g_target_count = count; // bsp_system_led_on(); // rt_kprintf("cstart: collecting %d samples...\r\n", count); // // 启动TIM3 PWM + 中断 // HAL_TIM_PWM_Start(&COLLECT_TIM, TIM_CHANNEL_3); // HAL_TIM_Base_Start_IT(&COLLECT_TIM); // rt_kprintf("cstart: started (non-blocking)\r\n"); // return 0; // } // int cprint(int argc, char **argv) // { // uint16_t n = 32; // if (argc >= 2) // n = (uint16_t)atoi(argv[1]); // if (!g_buffer || g_count == 0) // { // rt_kprintf("cprint: no data\r\n"); // return -1; // } // if (n > g_count) // n = g_count; // uint16_t minv = 0xFFFF, maxv = 0; // uint32_t sum = 0; // for (uint16_t i = 0; i < g_count; i++) // { // uint16_t v = g_buffer[i]; // if (v < minv) // minv = v; // if (v > maxv) // maxv = v; // sum += v; // } // rt_kprintf("cprint: N=%u min=%u max=%u avg=%lu\r\n", g_count, minv, maxv, sum / g_count); // rt_kprintf("first %u (dec / hex):\r\n", n); // for (uint16_t i = 0; i < n; i++) // { // if (i % 8 == 0) // rt_kprintf("\r\n[%04u] ", i); // rt_kprintf("%5u/0x%04X ", g_buffer[i], g_buffer[i]); // } // rt_kprintf("\r\n"); // return 0; // } // MSH_CMD_EXPORT_ALIAS(cstart, cstart, collect N samples) // MSH_CMD_EXPORT_ALIAS(cprint, cprint, print data) // 启动采集: 设置目标数量, 启动TIM3+中断 int bsp_light_data_sampling_start(void) { if (!g_buffer || g_buf_size == 0) { ET_ERR("bsp_collect: buffer not initialized"); return -1; } g_count = 0; g_target_count = g_buf_size; // 采集满缓冲区 bsp_system_led_on(); // 启动TIM3 PWM(产生CONVST) + Update中断 HAL_TIM_PWM_Start(&COLLECT_TIM, TIM_CHANNEL_3); HAL_TIM_Base_Start_IT(&COLLECT_TIM); //ET_INFO("bsp_collect: start, target=%u", g_target_count); return 0; } // 停止采集 int bsp_light_data_sampling_stop(void) { HAL_TIM_Base_Stop_IT(&COLLECT_TIM); HAL_TIM_PWM_Stop(&COLLECT_TIM, TIM_CHANNEL_3); bsp_system_led_off(); //ET_INFO("bsp_collect: stopped, collected=%u/%u", g_count, g_target_count); return 0; } uint16_t light_raw_map_u16(uint16_t val) { /* Avoid integer truncation: do multiply before divide using 32-bit intermediate. * Map 0..65535 -> 0..4095. Use rounding by adding half-divisor. */ uint32_t tmp = (uint32_t)val * 4095u; return (uint16_t)(tmp / 65535u + 0.5f); } float light_raw_map_f32(uint16_t val) { return (float)(val / 65535.0f * 4095.0f); } // TIM3中断回调: 每次CONVST触发后采集一个点 void sampling_tim_elapsed_callback(void) { // 未启动或已完成 if (g_target_count == 0 || g_count >= g_target_count) return; // 读取一个点 if (g_buffer && g_count < g_buf_size) { /* drive LED pin high at the start of ISR and low at the end * so each interrupt produces one pulse. This makes the pulse * frequency equal to the timer update frequency (no 2x toggle * ambiguity). */ bsp_system_led_on(); g_buffer[g_count] = light_raw_map_u16(ads8866_read_once()); g_count++; bsp_system_led_off(); // 达到目标数量, 停止并回调 if (g_count >= g_target_count) { HAL_TIM_Base_Stop_IT(&COLLECT_TIM); HAL_TIM_PWM_Stop(&COLLECT_TIM, TIM_CHANNEL_3); bsp_system_led_off(); //ET_INFO("bsp_collect: complete, N=%u", g_count); // 调用完成回调 if (g_complete_cb) g_complete_cb(); g_target_count = 0; // 标记完成 } } }