? ? 使用STM32f103c8t6制作简易示波器将波形检测后上传到网页进行描点绘图。我负责下位机部分我的同学负责网页部分。
? ?硬件要求:STM32f103c8t6最小系统板,波形发生器,示波器,正点原子的ESP8266WiFi模块 CH340USB转TLL。
? ? 软件要求:网络调试助手,串口调试助手。
? ?整体思路:使用MCU,ADC功能提高采集频率采集波形的各点电压,同时使用两个定时器进行输入捕获获得周期以及频率,将电压数据放入数组。使用串口通信于ESP8266WiFi模块通信初始化位TCP Client模式使其成为热点,与网页端建立连接。单片机再通过串口通信将电压数组发送给WiFi模块,WiFi模块再将数据发给网页,网页依据数据进行描点绘图。
? ?各功能的具体过程:
? 1、单片机ADC功能提高采集频率采集波形的各点电压
? ? ? STM32配置好相应的ADC接口即可,将信号发生器的信号进行调整达到单片机的耐受值,即可直接输入STM32初始ADC代码如下
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
u16 Get_Adc(u8 ch)
{
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_1Cycles5 );
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));
return ADC_GetConversionValue(ADC1);
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
}
return temp_val/times;
}
? 2、同时使用两个定时器进行输入捕获获得周期以及频率
? STM32定时有输入捕获的功能,使用定时器中断实现,初始设置为高电平捕获,定时器开启溢出中断,当输入的是方波时若收到低电平转高则进入定时器中断此时重置定时器记时将中断条件改为高转低信号,当再次进入中断进行判断是否为溢出中断如果溢出中断则标志位加一表示还没有检测到低电平但定时器已经完成一个周期的计时,若判断不为溢出中断那么表示已经完成了一次高电平时间的计时,将定时器输入捕获条件改为高电平捕获,将此时的时间记下。在主函数中获取高电平时间只需要将定时器的计时周期乘以标志位的次数加上记下的时间。即可获得高电平的时间。要获得周期只需要配置配置两个想发捕获条件的定时器,即可得到高电平时间和低电平时间。得到周期后,频率就可以算出。一个定时器代码如下:
void TIM2_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM2_ICInitStructure.TIM_ICFilter = 0x00;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM2,ENABLE );
}
u8 TIM2CH1_CAPTURE_STA=0;
u16 TIM2CH1_CAPTURE_VAL;
void TIM2_IRQHandler(void)
{
if((TIM2CH1_CAPTURE_STA&0X80)==0)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
if(TIM2CH1_CAPTURE_STA&0X40)
{
if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)
{
TIM2CH1_CAPTURE_STA|=0X80;
TIM2CH1_CAPTURE_VAL=0XFFFF;
}else TIM2CH1_CAPTURE_STA++;
}
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
if(TIM2CH1_CAPTURE_STA&0X40)
{
TIM2CH1_CAPTURE_STA|=0X80;
TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising);
}else
{
TIM2CH1_CAPTURE_STA=0;
TIM2CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM2,0);
TIM2CH1_CAPTURE_STA|=0X40;
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling);
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update); //??3y?D??±ê????
3,将采集到的电压数据存入数组
? ? ADC采集到电压值经过转换后为浮点型,因为串口只能发送字符型这需要用到数据类型的转换将浮点型转换为字符串,先连续采集一段电压,分别进行浮点型转换为字符串,在将字符串整体存入一个大的字符串数组准备发送。
浮点型转字符型代码
char *F2S2(double d,char *str)
{
char str1[5];
int j = 0,k,i;
i = (int)d;
while(i > 0)
{
str1[j++] = i%10 + '0';
i = i / 10;
}
for(k = 0;k < j;k++)
{
str[k] = str1[j-1-k];
}
str[j++] = '.';
d = d - (int)d;
for(i = 0;i < 10;i++)
{
d = d*10;
str[j++] = (int)d + '0';
d = d - (int)d;
}
while(str[--j] == '0');
str[++j] = '\0';
return str;
}
4、初始ESP8266 WiFi模块
? ?使用串口通信波特率为115200,使用AT指令控制WiFi模块成为热点并能进行无线通信。
串口通信逻辑代码实
USART_SendString(USART1, "AT+CWMODE=2\r\n");
delay(50000);
USART_SendString(USART1, "AT+RST\r\n");
delay(100000);
USART_SendString(USART1, "AT+CIPMUX=1\r\n");
delay(50000);
USART_SendString(USART1, "AT+CIPSERVER=1,8888\r\n");
delay(50000);
USART_SendString(USART1, "AT+CWSAP=\"ESP8277\",\"123456789\",11,3\r\n");
delay(50000);
USART_SendString(USART1, "AT+CIPSERVER=1,8888\r\n");
?最后网页接收我的数据
效果如下
?文章使用正点原子部分代码仅为学习作用。
|