先用 LCD制作 一个格子图形,然后定时器触发ADC采样。再将采集到的数据绘制成曲线显示在格子图形上,通过读取 图形上的点来测量信号。 本文使用的是原子哥的F103ZET6的战舰开发板。
LCD的配置代码我是直接复制原子哥的,直接调用了它里面的函数。
因为我的屏幕是480*800,所以为了布局采用了横屏显示。
首先根据方案,我要先制作一个格子图。并且为了观察波形的数据,加入了时间和数值显示。绘制格子是在函数display里面。格子的话时 每隔20绘制一条线,可以根据 自己情况 修改。
void display(void)
{
uint16_t t;
LCD_Fill(0,0,800,340,WHITE);
POINT_COLOR=GREEN;
for(t = 0;t<360;t=t+20)
LCD_DrawLine(0, t, 800, t);
for(t = 0;t<800;t=t+20)
LCD_DrawLine(t,0, t,340 );
POINT_COLOR=BLUE;
LCD_DrawLine(0, 180, 800, 180);
LCD_DrawLine(400,0, 400,340 );
LCD_ShowNum(84,376,k,2,24);
}
void main(void)
{
...
LCD_Init();
LCD_Clear(WHITE);
LCD_Display_Dir(1);
POINT_COLOR=BLUE;
LCD_ShowString(60,400,24*3,24,24,"t1:");
LCD_ShowString(24,376,24*2,24,24,"move:");
LCD_ShowString(60,424,24*3,24,24,"t2:");
LCD_ShowString(0,448,24*4,24,24,"|t1-t2|:");
LCD_ShowString(146,400,24*2,24,24,"ms");
LCD_ShowString(146,424,24*2,24,24,"ms");
LCD_ShowString(146,448,24*2,24,24,"ms");
LCD_ShowString(260,400,24*3,24,24,"V1:");
LCD_ShowString(260,424,24*3,24,24,"V2:");
LCD_ShowString(200,448,24*4,24,24,"|V1-V2|:");
LCD_ShowString(358,400,24*3,24,24,"mV");
LCD_ShowString(358,424,24*3,24,24,"mV");
LCD_ShowString(358,448,24*3,24,24,"mV");
display ();
...
}
效果展示:

接下来就是ADC配置 。先上代码:
void adc_init(void)
{
GPIO_InitTypeDef adc1io;
ADC_InitTypeDef adc1;
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
adc1io.GPIO_Mode = GPIO_Mode_AIN ;
adc1io.GPIO_Pin = GPIO_Pin_1 ;
GPIO_Init (GPIOA,&adc1io);
ADC_DeInit(ADC1);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
adc1.ADC_Mode = ADC_Mode_Independent ;
adc1.ADC_ContinuousConvMode = ENABLE ;//连续转换模式
adc1.ADC_ScanConvMode = DISABLE;//扫描模式 ,在于单通道还是双通道
adc1.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None ;//转换由软件启动而不是外部
adc1.ADC_DataAlign = ADC_DataAlign_Right ;
adc1.ADC_NbrOfChannel = 1;
ADC_Init (ADC1,&adc1);
ADC_Cmd(ADC1,ENABLE );
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)); //等待校 AD 准结束
ADC_RegularChannelConfig(ADC1, 1, 1, ADC_SampleTime_239Cycles5 );//通道 1,规则采样顺序值为 1,采样时间为 239.5 周期
ADC_SoftwareStartConvCmd (ADC1,ENABLE);//使能软件转换功能
}
uint16_t Get_adc()
{
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次 ADC1 规则组的转换结果
}
uint16_t Get_adcAverage(uint8_t times)//最高255,最少3
{
uint32_t t;
uint16_t a[255]={0};
int i,g1,g2;
for(i=0;i<times;i++)
{
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
a[i] = ADC_GetConversionValue(ADC1);
}
g1= g2 = a[0];
for(i = 0;i<times-1;i++)
{
if(g1>a[i+1])g1 = a[i+1];//让 g1为最小值
if(g2<a[i+1])g2 = a[i+1];//让 g2为最大值
}
for(i = 0;i<times;i++)
{
t += a[i];
}
return t = (t-g1-g2)/(times-2);
}
ADC配置简单介绍:
因为只打算做一个通道,所以配置成独立模式 ,开启连续转换模式。这里 的ADC采集我打算放在 主函数里面,所以也是配置成由软件触发。 这里使用了ADC1的通道1。为了采样准确率上升我把 采样时间 配置 239.5个周期 。因为是72M6分频,所以ADCCLK是12M。总转换时间公式是:Tcovn=采样时间+12.5 个周期,计算出来我采样一次时间是18us。计算这个只要是因为使用定时器触发,防止触发时间低于采样时间。
然后我编了一个提高采样精准的函数:连续采样多次(至少3次),去掉最大值和最小值,然后取平均。
同样先上代码:
void TIM_Init(void)
{
TIM_TimeBaseInitTypeDef Tim3;
NVIC_InitTypeDef NVIC_Tim;
RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3 ,ENABLE);
Tim3.TIM_Period = 10;
Tim3.TIM_CounterMode = TIM_CounterMode_Up ;
Tim3.TIM_Prescaler = 7199;
Tim3 .TIM_ClockDivision = TIM_CKD_DIV1 ;
TIM_TimeBaseInit (TIM3,&Tim3 );
TIM_ITConfig (TIM3,TIM_IT_Update ,ENABLE );//允许更新中断
NVIC_Tim .NVIC_IRQChannel = TIM3_IRQn ;
NVIC_Tim .NVIC_IRQChannelPreemptionPriority = 0;
NVIC_Tim .NVIC_IRQChannelSubPriority = 0;
NVIC_Tim .NVIC_IRQChannelCmd = ENABLE ;
NVIC_Init (&NVIC_Tim);
TIM_Cmd (TIM3,ENABLE);
}
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
Z=1;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
ms++;
}
}
我这里是打算 1ms采集一个点 ,所以我的采集信号频率只能在 1KHz以下,由于是绘图,所以在一个周期采集越多的点效果回越好。
因为中断不好执行占用CPU过长的任务,所以我在主函数 里面使用ADC采集,这里 只是 给它一个判断标志。
为了测量周期和峰峰值,我用可移动的两根线在波形上测量。这里就需要 用到按键。
void EXTI0_IRQHandler(void)
{
for(time = 0;time<40000;time++);
if(GPIO_ReadInputDataBit (WKUP_GPIO ,WKUP_GPIO_PIN ) == SET) //WK_UP按键
{
mode ++;
if(mode ==5)mode =0;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
void EXTI2_IRQHandler(void)
{
for(time = 0;time<40000;time++);
if(GPIO_ReadInputDataBit (KEY2_GPIO ,KEY2_GPIO_PIN ) == RESET)
{
node =1;
}
EXTI_ClearITPendingBit(EXTI_Line2); //清除 LINE2 上的中断标志位
}
void EXTI3_IRQHandler(void)
{
for(time = 0;time<40000;time++);
if(GPIO_ReadInputDataBit (KEY1_GPIO ,KEY1_GPIO_PIN ) == RESET)
{
k++;
if(k>10)k=1;
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE3 上的中断标志位
}
void EXTI4_IRQHandler(void)
{
for(time = 0;time<40000;time++);
if(GPIO_ReadInputDataBit (KEY0_GPIO ,KEY0_GPIO_PIN ) == RESET)
{
node = 2;
}
EXTI_ClearITPendingBit(EXTI_Line4); //清除 LINE4 上的中断标志位
}
这里也是通过标志来让单片机运行相应功能。
KEY_UP:第一次按下 :暂停波形,选定第一个竖直线。
第二次按下 :选定第二个数值线。
第三次按下 :选定第一个水平线。
第四次按下 :选定第二 个水平线。
第五次按下 :恢复运行波形。
KEY1:修改线移动的格数,这里设置是1~10。
KEY0:**当选定水平线时:向下移动k。
当选定竖直线时:向左移动k。
KEY2:当选定水平线时:向上移动k。
当选定竖直线时:向右移动k。
首先是先采集并且 绘制波形 ,这里的 方法 是1ms采集一个点并且采集800个点后开始绘制波形。
void main(void)
{
...
while(1)
{
while( mode ==0)
{
if(Z==1)
{
Z = 0;
adcx = Get_adcAverage (5);
temp[ms] = adcx*(3.3/4096)*100;
while(temp[ms]>330)
{
adcx = Get_adcAverage (3);
temp[ms] =adcx*(3.3/4096)*100;
}
}
if(ms == 799)
{
TIM_Cmd (TIM3,DISABLE);
display ();
for(ms=0;ms<800;ms++)xemp[ms]=temp[ms];
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
ms = 0;
TIM_Cmd (TIM3,ENABLE);
}
}
...
}
}
这里的话就是1ms触发定时器中断,然后Z置1,并且变量ms++。Z为 1开启ADC采集 ,将采集的数据存储在数组temp中。当ms为 799时,暂时关闭定时器,将数组temp中的数据转移到xemp中,再根据 xemp绘制曲线(转移到xemp中是为了保证后面绘制的曲线数据 在同一采集周期内)。因为波形需要更新 所以先调用 display更新下次波形。然后调用LCD_DrawLine绘制波形。这样就能将上个0.8s的信号数据绘制出来。将 变为 0并且再次开启定时器。至于要把这些放在while(mode==0)里,是为了后面服务的。
再看后面的代码:
if(mode == 1)
{
TIM_Cmd (TIM3,DISABLE);
while(mode == 1)
{
if(node ==1)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
node = 0;
POINT_COLOR=RED;
X=X+k;
if(X > 800)X = 0;
LCD_DrawLine(X, 0, X, 340);
LCD_ShowNum(110,400,(u32)X,3,24);
}
if(node ==2)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
node = 0;
POINT_COLOR=RED;
X=X-k;
if(X > 800)X = 800;
LCD_DrawLine(X, 0, X, 340);
LCD_ShowNum(110,400,(u32)X,3,24);
}
}
}
while(mode == 2)
{
if(node ==1)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
POINT_COLOR=RED;
LCD_DrawLine(X, 0, X, 340);
node = 0;
X1=X1+k;
if(X1 > 800)X1 = 0;
LCD_DrawLine(X1, 0, X1, 340);
LCD_ShowNum(110,424,(u32)X1,3,24);
}
if(node ==2)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
POINT_COLOR=RED;
LCD_DrawLine(X, 0, X, 340);
node = 0;
X1=X1-k;
if(X1 > 800)X1 = 800;
LCD_DrawLine(X1, 0, X1, 340);
LCD_ShowNum(110,424,(u32)X1,3,24);
}
if(X1>X)LCD_ShowNum(110,448,(u32)X1-X,3,24);
else LCD_ShowNum(110,448,(u32)X-X1,3,24);
}
while(mode == 3)
{
if(node ==1)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
node = 0;
POINT_COLOR=RED;
X=X+k;
if(X > 340)X = 0;
LCD_DrawLine(0,X, 800,X);
LCD_ShowNum(310,400,(u32)(340-X)*10,4,24);
}
if(node ==2)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
node = 0;
POINT_COLOR=RED;
X=X-k;
if(X > 340)X = 340;
LCD_DrawLine(0,X,800,X);
LCD_ShowNum(310,400,(u32)(340-X)*10,4,24);
}
}
if(mode == 4)
{
while(mode == 4)
{
if(node ==1)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
POINT_COLOR=RED;
LCD_DrawLine(0, X, 800, X);
node = 0;
X1=X1+k;
if(X1 > 340)X1 = 0;
LCD_DrawLine(0,X1, 800, X1);
LCD_ShowNum(310,424,(u32)(340-X1)*10,4,24);
}
if(node ==2)
{
display ();
POINT_COLOR=BLACK;
for(ms=0;ms<799;ms++)LCD_DrawLine(ms, 480-xemp[ms]-150, ms+1, 480-xemp[ms+1]-150);
POINT_COLOR=RED;
LCD_DrawLine(0, X, 800, X);
node = 0;
X1=X1-k;
if(X1 > 340)X1 = 340;
LCD_DrawLine(0,X1, 800, X1);
LCD_ShowNum(310,424,(u32)(340-X1)*10,4,24);
}
if(X1>X)LCD_ShowNum(310,448,(u32)(X1-X)*10,4,24);
else LCD_ShowNum(310,448,(u32)(X-X1)*10,4,24);
}
ms = 0;
TIM_Cmd (TIM3,ENABLE);
}
这部分代码思路其实都一样,因 为要关闭和开启 定时器,所以再在mode为1和mode为4外面先加了if语句。这里介绍一下mode为一的情况:
当按下第一次WK_UP的时候,mode为1,主函数进入while(mode==1)的循环。当你不 进行人格 操作 时,此时LCD页面 时 静止的,因为 不会进入里面 的if语句,while相当于空内容。当你按下相应功能按键,比如 KEY0,变量node变为2,进入if:先将 node置0。然后调用 display,更新显示,绘制波形,显示线和数值。图中红线 既是选定 的线。可按KEY2和 KEY0移动,move表示移动的大小。其他mode差不多 是一样的情况。

用信号发生器产生10Hz的峰峰值为2.8V的正弦波形(因为没搞采集负电压所以需要将低电平调为0):


5Hz1.5V方波:


2Hz三角波1V


这个代码我也只是测了几组,不知道还有没有问题,有的话欢迎大家指出。 然后误差也有,这跟32采集的精准度以及对他的处理还有我设置的分辨率都有关系,再者这里是将采集的两个点直接连线连起来,所以图形也会有误差。不过这个代码可改进的 方面有很多,例如用DMA传输,改变触发采集 时间(应该只要保证采样时间小于触发时间就行) 等等 。
Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、
LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L
我有一个关于多维数组的初学者ruby问题。我想按年份和月份对条目进行排序。所以我想创建一个包含年->月->月条目的多维数组所以数组应该是这样的:2009->08->Entry1->Entry209->Entry32007->10->Entry5现在我有:@years=[]@entries.eachdo|entry|timeobj=Time.parse(entry.created_at.to_s)year=timeobj.strftime("%Y").to_imonth=timeobj.strftime("%m").to_itmparr=[]tmparrentry}@years.pu
问题总结我想尝试使用Ruby来完成我在Python中所做的事情。在Python中它有r"""syntaxtosupportrawstrings,这很好,因为它允许将原始字符串与代码内联,并以更自然的方式连接它们,而无需特殊缩进。在Ruby中,当使用原始字符串时,必须使用其次是EOT在单独的行中,这会破坏代码布局。你可能会问,为什么不使用Ruby的%q{}?嗯,因为%q{}与Python的r"""相比有局限性因为它不会转义多个\\\并且只处理单个\.我正在动态生成Latex代码并写入一个文件,该文件稍后用pdflatex编译。Latex代码包含类似\\\的内容在许多地方。如果我使用Rub
我正在使用Ruby1.8。似乎downcase不会改变非拉丁字符。例如:"Δ".downcase返回“Δ”我知道在Ruby1.9.1及更高版本中,我可以使用UnicodeUtils(fromhere)。我试过了,它工作正常。返回上一个示例的"δ"。是否有适用于1.8Ruby的等效(或任何)解决方案? 最佳答案 nash@nash:~$ruby-vruby1.8.7(2011-02-18patchlevel334)[i686-linux]gem安装unicode(https://rubygems.org/gems/unicode)re
我正在使用Ruby制作一个命令行工具。它将在屏幕上打印大量文本。目前,我正在使用shell管道(may_app|more)来执行此操作。但我认为最好有一个默认的寻呼机。就像你在执行gitlog时看到的一样。可以使用git--nopagerlog禁用寻呼机。我已经完成了大量的谷歌工作并找到了一颗gem:hirb,但似乎有点矫枉过正。经过多次尝试,我目前正在使用shellwrapper来这样做:#!/bin/bash#xray.rbisthecorescript#doingthemainlogicandwill#outputmanyrowsoftexton#screenXRAY=$HOME