231 lines
6.3 KiB
C
231 lines
6.3 KiB
C
/*
|
||
* @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 <rtthread.h>
|
||
#include <stdlib.h>
|
||
|
||
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 <count>\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; // 标记完成
|
||
}
|
||
}
|
||
}
|