F407+AD9226数字示波器设计
基于STM32F407和AD9226的简易数字示波器设计项目。示波器是我们调试硬件、分析信号的必备工具因此自己动手搭建一个简易示波器不仅能加深对ADC采样、波形显示、频率测量的理解还能满足日常简易信号测试需求。本文将从硬件选型、软件实现、调试优化三个方面详细讲解整个项目的设计思路。一、项目概述本项目实现一款简易数字示波器核心基于STM32F407VET6单片机和AD9226模数转换器支持波形实时显示、频率测量、占空比计算、信号统计信息最大值、最小值、峰峰值显示等核心功能。采用TFTLCD显示屏作为显示终端通过按键切换采样率档位LED指示系统运行状态整体设计简洁、易移植适合嵌入式入门学习者参考。核心参数主控芯片STM32F407VET6168MHz主频足够支撑高速ADC采样和波形处理ADC芯片AD922612位分辨率最高65MSPS采样率并行数据输出满足中高频信号采样需求显示终端TFTLCD显示屏横屏显示适配波形网格和统计信息显示采样率8档可调1KHz~7.67MHz覆盖低速到高速信号采样功能支持波形显示、频率/占空比测量、信号峰峰值/平均值统计、波形平滑滤波二、硬件设计2.1 核心硬件选型硬件选型的核心是匹配“采样精度采样速度”同时兼顾成本和易获取性具体选型如下器件名称型号核心作用主控芯片STM32F407VET6系统控制、ADC数据处理、LCD显示驱动、按键控制模数转换器AD9226将模拟信号转换为12位数字信号并行输出给STM32显示模块TFTLCD如9341控制器显示波形网格、实时波形、信号统计信息按键独立按键PE4切换采样率档位、刷新显示模式指示模块LED灯指示系统运行状态每500ms翻转一次定时器TIM3产生AD9226采样时钟低速采样模式2.2 硬件连接原理硬件连接的核心是AD9226与STM32F407的接口设计以及LCD、按键的基础连接重点关注AD9226的时钟和数据接口具体连接如下关键引脚AD9226与STM32连接AD9226数据输出端D0~D11连接STM32 GPIOG的低12位PG0~PG11并行读取采样数据AD9226采样时钟端CLK连接STM32 PA8由STM32产生采样时钟上升沿触发采样AD9226电源端VDD接3.3VVSS接地确保稳定供电LCD与STM32连接采用8位并行接口或SPI接口根据LCD型号调整本文采用并行接口连接STM32对应的GPIO口驱动LCD显示波形和文字关键配置LCD横屏显示分辨率适配网格绘制设置背景色和波形色区分网格、轴线和波形按键与LED连接独立按键PE4GPIO输入模式上拉配置按下时触发采样率档位切换LED灯如LED0GPIO输出模式用于指示系统运行状态每500ms翻转一次注意AD9226的采样时钟稳定性直接影响采样精度高速采样模式7.67MHz采用GPIO手动翻转产生时钟低速采样模式≤1MHz采用TIM3定时器产生精确时钟确保采样时钟的占空比为50%。三、软件实现核心代码解析软件部分基于HAL库开发整体分为“系统初始化、ADC采样、波形处理、LCD显示、按键控制”五大模块代码结构清晰可直接移植到对应硬件平台。以下重点解析核心模块的实现思路完整代码附在文末。3.1 系统初始化模块系统初始化是项目运行的基础需按顺序初始化HAL库、系统时钟、延时函数、串口、LED、LCD、AD9226、定时器和按键确保各外设正常工作。int main(void) { uint32_t last_toggle 0; // LED闪烁计时变量 uint8_t i 0; // 循环计数器 uint16_t k 0; // 采样点数计数器 uint8_t Gear 0; // 采样率档位0~7 uint32_t Sampling_Rate[8] {7670000, 1000000, 500000, 100000, 50000, 10000, 5000, 1000}; // 8档采样率 uint16_t *pb AD9226_Data; // 指向AD9226数据缓冲区的指针 static const uint8_t s_gear_map[6] {0U, 1U, 2U, 3U, 4U, 5U}; // 第一阶段系统硬件初始化 HAL_Init(); // 初始化HAL库 sys_stm32_clock_init(336, 8, 2, 7); // 配置系统时钟168MHz delay_init(168); // 初始化延时函数 usart_init(115200); // 初始化串口调试输出 led_init(); // 初始化LED lcd_init(); // 初始化LCD显示屏 AD9226_Init(); // 初始化AD9226 ADC模块 TIM3_Init(999, 83); // 初始化定时器3低速采样时钟 key_pe4_init(); // 初始化按键PE4 // 第二阶段UI界面绘制 sprintf(g_lcd_id, LCD ID:%04X, lcddev.id); // 格式化LCD ID字符串 lcd_display_dir(1); // 设置横屏显示 osc_ui_draw_static(); // 绘制静态UI网格、背景 osc_draw_header(Sampling_Rate[Gear]); // 绘制顶部状态栏采样率等信息 // 第三阶段主循环数据采集与波形显示 while (1) { // 按键处理切换采样率档位 if (key_pe4_pressed()) { g_disp_mode; if (g_disp_mode 6U) g_disp_mode 0U; Gear s_gear_map[g_disp_mode]; osc_draw_header(Sampling_Rate[Gear]); } // LED闪烁指示系统运行状态每500ms翻转一次 if ((HAL_GetTick() - last_toggle) 500) { last_toggle HAL_GetTick(); LED0_TOGGLE(); // 调试输出PA8电平状态AD9226时钟输出 uint8_t pa8_state HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8); printf(PA8 State: %d\r\n, pa8_state); } // 采样控制每20次循环约100ms进行一次采样和波形刷新 if (i 20) { // 高速采样模式7.67MHzGPIO手动产生时钟 if (Sampling_Rate[Gear] 7670000) { k 290; // 采样290个点丢弃前10个无效点 pb AD9226_Data; do { GPIOA-BSRR GPIO_PIN_8; // PA8置高时钟上升沿 *(pb) GPIOG-IDR 0x0FFF; // 读取AD9226数据低12位 GPIOA-BSRR GPIO_PIN_8 16; // PA8置低时钟下降沿 } while (--k); // 更新波形显示和统计信息使用后280个有效点 osc_ui_update(AD9226_Data[10], 280U); osc_show_stats(AD9226_Data[10], 280U, Sampling_Rate[Gear]); } // 低速采样模式≤1MHzTIM3定时器产生时钟 else { // 计算定时器参数定时器频率2×采样率确保50%占空比 uint32_t psc 0, arr 0; if (Sampling_Rate[Gear] 100000) { psc 0; arr 168000000 / (Sampling_Rate[Gear] * 2U) - 1; } else { psc 168000000 / (Sampling_Rate[Gear] * 2U) / 1000 - 1; arr 1000 - 1; } TIM3_Init((uint16_t)arr, (uint16_t)psc); // 重新初始化定时器 while (Time_Flag 0); // 等待采样完成 // 更新波形显示和统计信息 osc_ui_update(AD9226_Data[10], 280U); osc_show_stats(AD9226_Data[10], 280U, Sampling_Rate[Gear]); Time_Flag 0; // 清除采样完成标志 } i 0; // 重置循环计数器 } delay_ms(5); // 延时5ms i; // 计数器递增 } }3.2 AD9226采样模块AD9226的初始化和采样逻辑是核心分为高速采样和低速采样两种模式确保不同频率信号的采样精度。1. AD9226初始化配置PA8为输出产生采样时钟PG0~PG11为输入读取采样数据确保GPIO口速度匹配高速采样需求。void AD9226_Init(void) { GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_GPIOA_CLK_ENABLE(); // PA8配置为输出AD9226采样时钟 GPIO_Initure.Pin GPIO_PIN_8; GPIO_Initure.Mode GPIO_MODE_OUTPUT_PP; GPIO_Initure.Pull GPIO_PULLUP; GPIO_Initure.Speed GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOA, GPIO_Initure); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); __HAL_RCC_GPIOG_CLK_ENABLE(); // PG0~PG11配置为输入AD9226数据输入 GPIO_Initure.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10| GPIO_PIN_11; GPIO_Initure.Mode GPIO_MODE_INPUT; GPIO_Initure.Pull GPIO_PULLUP; GPIO_Initure.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOG, GPIO_Initure); }2. 采样时钟产生高速模式7.67MHz通过GPIO手动翻转产生时钟低速模式通过TIM3定时器中断产生时钟中断中读取采样数据并存储到缓冲区。// TIM3中断服务函数低速采样模式 void TIM3_IRQHandler(void) { __HAL_TIM_CLEAR_IT(had9226_tim, TIM_IT_UPDATE); // 清除中断标志 GPIOA-BSRR GPIO_PIN_8; // PA8置高时钟上升沿触发采样 g_ad9226_clk_level 1U; // 读取AD9226并行数据保留低12位有效数据 g_ad9226_last_sample (uint16_t)(GPIOG-IDR 0x0FFFU); AD9226_Data[s_sample_index] g_ad9226_last_sample; GPIOA-BSRR GPIO_PIN_8 16; // PA8置低时钟下降沿 g_ad9226_clk_level 0U; g_ad9226_pulse_count; s_sample_index; // 采样290个点后置位完成标志停止定时器 if (s_sample_index 290) { Time_Flag 1; s_sample_index 0; HAL_TIM_Base_Stop_IT(had9226_tim); HAL_NVIC_DisableIRQ(TIM3_IRQn); } }3.3 波形处理与显示模块波形处理的核心是将AD9226采集的12位数字信号映射到LCD屏幕的像素坐标并通过平滑滤波优化波形显示效果同时计算信号的频率、占空比等统计信息。网格绘制绘制示波器背景、边框、中心轴线和虚线网格确保波形显示清晰网格按X轴10等分、Y轴8等分设计适配LCD屏幕尺寸。波形映射将ADC采样值0~4095通过线性插值映射到LCD屏幕的Y坐标自动缩放波形确保波形占屏幕高度的75%避免波形过大或过小。平滑滤波采用三点加权平均滤波算法对波形进行两次滤波处理减少波形锯齿提升显示效果。统计信息计算计算信号的最小值、最大值、峰峰值、平均值通过过零点检测线性插值计算频率和占空比格式化后显示在LCD底部。// 波形更新核心函数ADC数据→屏幕坐标→波形绘制 static void osc_ui_update(const uint16_t *samples, uint32_t len) { if (samples NULL || len 4U) return; if (g_grid_w 4U || g_grid_h 4U) return; // 步骤1计算采样数据统计值最小值、最大值、平均值 uint32_t sum 0U; uint16_t minv 0xFFFFU, maxv 0U; for (uint32_t i 0; i len; i) { uint16_t v samples[i]; if (v minv) minv v; if (v maxv) maxv v; sum v; } // 步骤2计算自动缩放系数波形占屏幕高度75% float mid (float)sum / (float)len; float amp (float)((uint32_t)maxv - (uint32_t)minv); float k 0.0f; if (amp 32.0f) { if (amp 64.0f) amp 64.0f; float target (float)g_grid_h * 0.75f; k target / amp; if (k 1.0f) k 1.0f; } // 步骤3擦除上一帧波形避免残影 osc_erase_prev_wave(); // 步骤4线性插值将ADC数据映射到屏幕Y坐标 uint16_t point_cnt g_grid_w; if (point_cnt OSC_MAX_W) point_cnt OSC_MAX_W; if (point_cnt 4U) point_cnt 4U; // 线性插值映射 for (uint16_t ix 0; ix point_cnt; ix) { float pos ((float)ix * (float)(disp_len - 1U)) / (float)(point_cnt - 1U); uint32_t i0 (uint32_t)pos; uint32_t i1 (i0 1U len) ? (i0 1U) : i0; float frac pos - (float)i0; float v (1.0f - frac) * (float)samples[i0] frac * (float)samples[i1]; // 映射到屏幕Y坐标 float dy (mid - v) * k; float y (float)g_grid_cy dy; float ymin (float)(g_grid_y0 1U); float ymax (float)(g_grid_y0 g_grid_h - 2U); if (y ymin) y ymin; if (y ymax) y ymax; g_wave_prev_y[ix] (uint16_t)(y 0.5f); } // 步骤5三点加权平均平滑滤波执行两遍 for (uint8_t pass 0; pass 2U; pass) { if (point_cnt 2U) break; uint16_t prev g_wave_prev_y[0]; uint16_t curr g_wave_prev_y[1]; for (uint16_t ix 1; ix (uint16_t)(point_cnt - 1U); ix) { uint16_t next g_wave_prev_y[ix 1U]; uint32_t acc (uint32_t)prev ((uint32_t)curr 1) (uint32_t)next; g_wave_prev_y[ix] (uint16_t)((acc 2U) 2); prev curr; curr next; } } // 步骤6绘制当前帧波形 uint16_t x0 g_grid_x0; uint16_t x_end (uint16_t)(x0 point_cnt - 1U); for (uint16_t x (uint16_t)(x0 2U); x x_end; x) { uint16_t ix (uint16_t)(x - x0); uint16_t y1 g_wave_prev_y[ix - 1U]; uint16_t y2 g_wave_prev_y[ix]; osc_vseg_wave(x, y1, y2); } // 步骤7保存波形信息供下一帧擦除 g_wave_prev_len point_cnt; g_wave_prev_valid 1U; }3.4 按键控制模块采用独立按键PE4实现采样率档位切换按键采用消抖处理20ms消抖避免误触发切换档位后重新绘制顶部状态栏显示当前采样率。// 按键初始化PE4 static void key_pe4_init(void) { GPIO_InitTypeDef gpio {0}; __HAL_RCC_GPIOE_CLK_ENABLE(); gpio.Pin GPIO_PIN_4; gpio.Mode GPIO_MODE_INPUT; gpio.Pull GPIO_PULLUP; gpio.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOE, gpio); } // 按键消抖检测返回1表示按键按下 static uint8_t key_pe4_pressed(void) { enum { IDLE 0, DEBOUNCE 1, HELD 2 }; static uint8_t state IDLE; static uint32_t t0 0U; uint8_t down (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) GPIO_PIN_RESET) ? 1U : 0U; uint32_t now HAL_GetTick(); if (state IDLE) { if (down) { state DEBOUNCE; t0 now; } } else if (state DEBOUNCE) { if (!down) state IDLE; else if ((now - t0) 20U) { state HELD; return 1U; } } else { if (!down) state IDLE; } return 0U; }四、调试优化与注意事项4.1 调试要点串口调试通过串口输出PA8电平状态、AD9226采样数据判断采样时钟和数据读取是否正常。波形显示调试先绘制静态网格再逐步调试波形映射和滤波功能确保波形无残影、无锯齿。采样率调试不同采样率档位切换后检查波形显示是否稳定频率测量是否准确可通过函数信号发生器输入标准信号验证。按键调试测试按键消抖效果确保档位切换无误触发采样率更新及时。4.2 优化方向增加波形触发模式如上升沿触发、下降沿触发提升示波器实用性。优化采样数据缓冲区采用双缓冲机制进一步减少波形闪烁。增加采样率自动切换功能根据输入信号频率自动匹配最佳采样率。优化频率测量算法提升低频信号的测量精度。4.3 注意事项AD9226的电源供电要稳定避免电源噪声影响采样精度。采样时钟的占空比需严格控制在50%否则会导致采样数据失真。LCD显示时需先擦除上一帧波形再绘制当前帧避免残影。高速采样模式下GPIO速度需配置为高速模式确保时钟翻转速度满足要求。