STM32F103C8T6用中断采集AD677的16位数字信号,采的是2V基准电压,采出的值在0.5和3之间来回跳。
AD677的参考电压是5V,AD677采样时序如下
串口助手得到的数如下,我用fb判断busy是否为高,用fa判断busy是否为低,所以fb和fa之间的数就是采出来的数
我用外部中断检测SCLK的上升沿,检测到上升沿就进入中断,把sdata的值赋给data,采完16位通过usart发到上位机,以下是代码
#define SAMPLE_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4)
#define SAMPLE_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4)
#define CLK_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_6)
#define CLK_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_6)
#define CAL_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_7)
#define CAL_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_7)
#define READ_SDATA() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)
#define READ_BUSY() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)
#define READ_SCLK() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)
#define READ_CLK() GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_6)
#define ResetCOUNTER() TIM_SetCounter(TIM3, 0)
volatile uint16_t data = 0; // 最终的16位数据
volatile uint8_t bitCounter = 0; // 位计数器
volatile uint8_t dataReady = 0; // 数据就绪标志
void GPIO_ENABLE(uint32_t RCC_APB2Periph, GPIO_TypeDef* GPIOx, GPIOMode_TypeDef GPIO_Mode, uint16_t GPIO_Pin, GPIOSpeed_TypeDef GPIO_Speed)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed;
GPIO_Init(GPIOx, &GPIO_InitStructure);
}
void AD_Init(void)
{
GPIO_ENABLE(RCC_APB2Periph_GPIOA, GPIOA, GPIO_Mode_AF_PP, GPIO_Pin_6, GPIO_Speed_50MHz);
GPIO_ENABLE(RCC_APB2Periph_GPIOA, GPIOA, GPIO_Mode_Out_PP, GPIO_Pin_4 | GPIO_Pin_7, GPIO_Speed_50MHz);
GPIO_ENABLE(RCC_APB2Periph_GPIOA, GPIOA, GPIO_Mode_IN_FLOATING, GPIO_Pin_8, GPIO_Speed_50MHz);
GPIO_ENABLE(RCC_APB2Periph_GPIOB, GPIOB, GPIO_Mode_IN_FLOATING, GPIO_Pin_10 | GPIO_Pin_11, GPIO_Speed_50MHz);
//A6、tim3ch1初始化,输出pwm波
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 1000 - 1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler = 4 - 1; //PSC,18kHz
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 500; //CCR
TIM_OC1Init(TIM3, &TIM_OCInitStruct);
// TIM_Cmd(TIM3, ENABLE);
}
void PB10EXTI_Init(void)
{
//B10、EXTI
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//设置IO口与中断线的映射关系
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource10);
//初始化线上中断
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line10;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
EXTI_Init(&EXTI_InitStruct);
//配置中断分组
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
}
void EXTI15_10_IRQHandler(void)
{
int8_t temp;
// static uint32_t lastCaptureTime = 0;
// uint32_t currentCaptureTime;
if(EXTI_GetITStatus(EXTI_Line10) != RESET)
{
EXTI_ClearITPendingBit(EXTI_Line10);//重置标志位
// currentCaptureTime = TIM_GetCapture1(TIM1);
// for(volatile int i = 0; i < 25; i++);
// 接收一位数据
if(bitCounter < 16)
{
// 将读取的位存入capturedValue
data = (data << 1) | READ_SDATA();
bitCounter++;
// 当收到16位数据时设置数据就绪标志
if(bitCounter == 16)
{
dataReady = 1;
bitCounter = 0; // 重置位计数器,准备下一次接收
if(dataReady == 1)
{
temp = (data >> 8) & 0xFF; //高8位
Serial_SendByte(temp);
temp = (data & 0xFF); //低8位
Serial_SendByte(temp);
// Serial_SendByte(0xF7);//发送完成
dataReady = 0;//重置标志位
}
}
}
// lastCaptureTime = currentCaptureTime;
}
}
3
举报
针对AD677数据采集不稳定的问题,以下是分步解决方案:
AD677数据在SCLK的下降沿稳定,因此应改为在SCLK的下降沿触发中断读取数据:
// 修改外部中断触发边沿为下降沿
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
volatile uint16_t adc_data = 0;
volatile uint8_t bit_counter = 0;void EXTI_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_LineX) != RESET) {
// 在SCLK下降沿读取SDATA引脚状态
uint8_t bit = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_X);
adc_data = (adc_data << 1) | bit; // 高位在前
bit_counter++;
if (bit_counter >= 16) {
// 完成16位采集,禁用中断或重置计数器
EXTI_IRQDisable();
bit_counter = 0;
// 发送数据(需添加校验或滤波)
USART_SendData(adc_data);
}
EXTI_ClearITPendingBit(EXTI_LineX);
}}
---
### **4. 添加软件滤波**
在STM32端对连续采样值做滑动平均滤波:
```c
#define FILTER_SIZE 8
uint16_t filter_buffer[FILTER_SIZE];
uint8_t filter_index = 0;
void apply_filter(uint16_t raw_data) {
filter_buffer[filter_index] = raw_data;
filter_index = (filter_index + 1) % FILTER_SIZE;
uint32_t sum = 0;
for (uint8_t i = 0; i < FILTER_SIZE; i++) {
sum += filter_buffer[i];
}
uint16_t avg = sum / FILTER_SIZE;
USART_SendData(avg); // 发送滤波后的数据
}while (GPIO_ReadInputDataBit(GPIOA, BUSY_PIN) == HIGH); // 等待BUSY变低
EXTI_IRQEnable(); // 开始采集在AD677初始化时降低SCLK频率,排除因速度过快导致的数据丢失:
// 调整GPIO模拟SPI的延时(示例)
void delay_us(uint32_t us) {
// 根据STM32主频实现微秒级延时
}
void generate_sclk() {
CLK_HIGH();
delay_us(10); // 降低SCLK频率至约50kHz
CLK_LOW();
delay_us(10);
}若STM32的SPI资源可用,改用硬件SPI自动读取数据,避免中断延迟:
// 配置SPI为主机模式,CPOL=0, CPHA=1(下降沿采样)
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_Init(SPI1, &SPI_InitStructure);
// 读取数据
uint16_t data = SPI_ReceiveData(SPI1);总结:优先检查硬件连接与时序,调整中断触发边沿,添加滤波,并考虑切换为硬件SPI。通过上述步骤,数据稳定性应显著提升。
举报
更多回帖