part1 硬件简介
本设计基于Lattice的ICE40UP5K FPGA和STM32G031 MCU,板载LPC11U35下载器,可以通过USB-C接口进行FPGA的配置,并通过虚拟串口通信配置STM32G031,支持在ICE40UP5K上对RISC-V软核的移植以及开源的FPGA开发工具链,板上RGB三色LED灯用于简单的调试,总计36个IO用于扩展使用,其中14个连接STM32G031 芯片,另外的22根连接ICE40UP5K FPGA芯片。
搭配电赛扩展板,帮助信号源、仪器仪表、控制以及信号处理类题目的训练。板上有通过两个16Pin的插座可以安装高速ADC(16Pin可再用模块/同时支持DIP和邮票孔)、高速DAC(16Pin可再用模块/支持DIP和邮票孔)、板上安装了高速比较器、姿态传感器、旋转编码器以及按键等。
part2 设计思路
利用板上的高速比较器和STM32的逻辑实现高速频率计和计数器的功能。以STM32输出PWM波,经过一阶低通滤波,与输入信号比较,产生稳定的脉冲信号,方便STM32捕获计算频率。
采用方法: 频率测量法
以STM32产生PWM波,经过一阶低通滤波,与被测信号作比较,产生较为稳定的脉冲信号,在时间t内对被测信号的时钟周期N进行计数,然后求出单位时间内的时钟周期数,即为被测信号的频率。
下面为配置图
时钟树倍频到64mhz
定时器采用外部计数器的方法
oled为软件spi驱动(配置省略,附件有ioc配置文件)
part3 代码简介
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
for (uint8_t i = 0; i < 15; i++)
{
HAL_TIM_Base_Start(&htim2);
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
switch (TIM2_CAPTURE_STA)
{
case 0:
{
TIM2_TIMEOUT_COUNT = 0;
__HAL_TIM_SET_COUNTER(&htim2, 0);
TIM2_CAPTURE_BUF[0] = 0;
TIM2_CAPTURE_BUF[1] = 0;
TIM2_CAPTURE_BUF[2] = 0;
TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_RISING);
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
TIM2_CAPTURE_STA++;
break;
}
case 4:
{
uint32_t high = TIM2_CAPTURE_BUF[1] - TIM2_CAPTURE_BUF[0];
uint32_t cycle = TIM2_CAPTURE_BUF[2] - TIM2_CAPTURE_BUF[0];
float frq = 1.0 / (((float)cycle) / 1000000.0);
TIM2_CAPTURE_STA = 0;
buf[i] = frq;
break;
}
default:
break;
}
}
qsort(buf, 15, sizeof(float), cmpfunc);
OLED_Clear1();
SetCursor(0, 32);
printf("%.1fHz\r\n",buf[7] );
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
HAL_Delay(50);
OLED_Refresh();
}
下面是外部定时器TIM2的配置函数
/* USER CODE BEGIN 1 */
__IO uint32_t TIM2_TIMEOUT_COUNT = 0; ///< 定时器2定时溢出计数
uint32_t TIM2_CAPTURE_BUF[3] = {0, 0, 0}; ///< 分别存储上升沿计数、下降沿计数、下个上升沿计数
__IO uint8_t TIM2_CAPTURE_STA = 0; ///< 状态标记
/**
* 设置TIM2输入捕获极性
* @param TIM_ICPolarity:
* TIM_INPUTCHANNELPOLARITY_RISING :上升沿捕获
* TIM_INPUTCHANNELPOLARITY_FALLING :下降沿捕获
* TIM_INPUTCHANNELPOLARITY_BOTHEDGE:上升沿和下降沿都捕获
*/
void TIM2_SetCapturePolarity(uint32_t TIM_ICPolarity)
{
htim2.Instance->CCER &= ~(TIM_CCER_CC2P | TIM_CCER_CC2NP);
htim2.Instance->CCER |= (TIM_ICPolarity & (TIM_CCER_CC2P | TIM_CCER_CC2NP));
}
/// 定时器2时间溢出回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == htim2.Instance)
{
TIM2_TIMEOUT_COUNT++; // 溢出次数计数
}
}
///< 输入捕获回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == htim2.Instance)
{
switch (TIM2_CAPTURE_STA)
{
case 1:
{
// printf("准备捕获下降沿...\r\n");
TIM2_CAPTURE_BUF[0] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2) + TIM2_TIMEOUT_COUNT * 0xFFFF;
TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_FALLING); // 设置为下降沿触发
TIM2_CAPTURE_STA++;
break;
}
case 2:
{
// printf("准备捕获下个上升沿...\r\n");
TIM2_CAPTURE_BUF[1] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2) + TIM2_TIMEOUT_COUNT * 0xFFFF;
TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_RISING); // 设置为上升沿触发
TIM2_CAPTURE_STA++;
break;
}
case 3:
{
// printf("捕获结束...\r\n");
// printf("# end ----------------------------------------------------\r\n");
TIM2_CAPTURE_BUF[2] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2) + TIM2_TIMEOUT_COUNT * 0xFFFF;
HAL_TIM_IC_Stop_IT(htim, TIM_CHANNEL_2); // 停止捕获
HAL_TIM_Base_Stop_IT(&htim2); // 停止定时器更新中断
TIM2_CAPTURE_STA++;
break;
}
default:
break;
}
}
}
实际测试中发现由于比较器的误差太大,故使用多次测量的方法进行降噪,同时测量15次,存入数组,后排序,输出位于第7位的数组进行输出。
下面为测试环节
使用小脚丫fpga做的dds测试频率计
同时验证dds
下面使用信号发生器进行测试
part4 总结
排除信号源问题,发现是比较器输出上的问题,输出的频率是不准确的,需要滤波和计算,故存在一定的误差。
目前只测试了正弦波和三角波。
ps:未使用fpga,只使用了stm32和高速比较器