单片机学习小组
直播中

李俊

7年用户 1328经验值
私信 关注

怎样去设计一种基于STM32的简易示波器呢

串口设置的一般步骤分为哪几步?

怎样去设计一种基于STM32的简易示波器呢?

回帖(1)

陈俊

2022-2-18 14:03:02
1.串口通讯部分

串口设置的一般步骤可以总结为如下几个步骤:


  • 串口时钟使能, GPIO 时钟使能
  • 串口复位
  • GPIO 端口模式设置
  • 串口参数初始化
  • 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
  • 使能串口
  • 编写中断处理函数

查看手册得知, 配置全双工的串口 1,那么 TX(PA9)管脚需要配置为推挽复用输出, RX(PA10)管脚配置为浮空输入或者带上拉输入。 模式配置参考下面表格:
串口的GPIO 端口模式设置和初始化都是一些套路性的东西,比较重要的是中断处理函数的编写,这里代码我是参考原子来的,感觉原子哥写的很巧妙:

u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大字节数
//接收状态
//bit15 接收完成标志
//bit13 接收到的有效字节长度
u16 USART_RX_STA=0;       //接受状态标志


因为TFT屏幕用的是ILI9328型号,分辨率240*320,所以在usart.h里把最大字节数定义为260,可以一次性从USART_RX_BUF[ ]中读260个值送入显示。这里我适当修改了原子的中断服务程序:

void USART1_IRQHandler(void)                 //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS   //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
   OSIntEnter();   
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
  {
  Res =USART_ReceiveData(USART1); //读取接收到的数据
  
  if((USART_RX_STA&0x8000)==0)//接收未完成
   {
      if(USART_RX_STA       {
          USART_RX_BUF[USART_RX_STA++]=Res;  //记录接收到的值
       }else
         {
            USART_RX_STA|=1<<15;     //强制标记接收完成,可以看成0x8000
         }     
       }
    }
#if SYSTEM_SUPPORT_OS  //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
   OSIntExit();              
#endif
}
原子的程序是接收到回车符视为一次接受完成,考虑到这样会降低通信的效率,所以我把它改成了接收到260个字符视为一次完成。这一段代码好好看看,写的特别好,有些地方也不是很好懂。

/*****************************串口接收***********************************/                                                                                       
  if(USART_RX_STA&0x8000)
  {        
   len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
   for(t=0;t    {
    USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
   }
   USART_RX_STA=0;
  }
/*************************************************************************/  
我们发送一个数据到串口USART_RX_BUF[ ]中(数据就可以从这个寄存器中取了),之后,要检测这个数据是否已经被发送完成了。 USART_FLAG_TC 是宏定义的数据发送完成标识符。
2.TFT显示部分

首先,在工程中加入和TFTLCD相关的.c文件和.h文件,基本的GPIO定义就不说了。当时做的时候遇到了一个难题,就是如何让LCD横屏显示,如果你是原子的屏,改一个参数就OK了,但我的屏文件里的代码只有一些基本的,胡乱改了好多东西,显示的都是乱码,当时心里是崩溃的,实在是不想放弃,后来硬着头皮查了下LCD和扫描有关的寄存器,终于算是成功了。
1.显存控制方式 (AM、I/D0、I/D1)


2.扫描线控制方式(R01h)




上面说的有点晦涩难懂,简单来说就是修改01h,03h,60h的控制指令。而Write_Cmd_Data0x_ _,0x _ _ )就是分别写入指令和数据,实际上是这一个函数调用了两个函数,简化了代码。

Write_Cmd_Data(0x0001,0x0000);   //set SS and SM bit //设置扫描方向  
Write_Cmd_Data(0x0003,0x1038);   //set Entry Mode  //设置进入模式  
Write_Cmd_Data(0x0060,0xA700); // Gate Scan Line


void LCD_SetPos(unsigned int x0,unsigned int x1,unsigned int y0,unsigned int y1)
{
     Write_Cmd_Data(0x50,y0);  // Horizontal GRAM Start Address
     Write_Cmd_Data(0x51,y1);  // Horizontal GRAM End Address
     Write_Cmd_Data(0x52,x0);  // Vertical GRAM Start Address
     Write_Cmd_Data(0x53,x1);  // Vertical GRAM Start Address
     Write_Cmd_Data(0x20,y0);  // GRAM horizontal Address
     Write_Cmd_Data(0x21,x0);  // GRAM Vertical Address
     Write_Cmd(0x00,0x22);    // 0x0022,Start to Write Data to GRAM
}
下面是我总结的表格:
[tr]LCD 扫描指令1LCD扫描指令2[/tr]

0x01, 0x00000x01, 0x0100
0x03, 0x10380x03, 0x1030
0x60, 0xA7000x60, 0x2700
把0x20和0x21后面的参数互相调一下,上面的带代码已经调过了。
不同型号的屏的修改方式不同,不同人写的代码,这几个函数的写法也不同,大家可以自己修改尝试一下,大概有8种可能的显示方式(因为有些组合是乱码)。
接下来就是从USART_RX_BUF[ ]中取数据的程序,

for(n=0;n<260;n++) //从串口寄存器中取
  {
        adc1[n]=USART_RX_BUF[n];
  }
  for(n=0;n<260;n++)
  {
        adc[n]=16*(adc1[n])+adc1[n+1]+120;
  }
这里多加了一个adc1[ ]数组作为过渡,不写这个的话不知道为什么取出来的数莫名其妙的变得特别大,后来把这几个寄存器中的值都显示出来计算分析了一下找到了问题所在。因为我接收到的数值是十六进制的,所以在取高位时乘了16。刚开始认为得到的值是ASCII码,应该减去**‘0’**才是真值,但那样写了不对,所以就删掉了。在接收数据之前,可以先自己用串口调试助手调试一下,给大家看一下我调试的结果:

右边的频率和峰值什么的参数,本来是想加的,但时间不允许了,就把它糊弄过去了,要加的话,只需让发送方周期性的发送这些信息(例如每发260个点发一次)。波形还是挺漂亮的!
3.示波器最终效果(半成品)


采样的波形把网格线刷掉了,时间原因就没有去掉,之前从网上看到过,是一篇LCD的“或关系”显示的文章,有想完善的朋友可以完善一下。出现了波形失真是因为当时用信号发生器时忘了加直流偏移量,一般要加到峰峰值的一半以上,AD采样的芯片采不到真正的0,忘加直流偏移量可能会烧芯片,幸好我们的没烧。这里说明一下为什么要用通信的方法,单片机外设接的多了必然会影响速度,而且采样到的波形会有很多毛刺,所以真正显示的波形以后要改成自己生成的所谓的“完美波形”,串口发送的也不再是一系列的点,而是波形的信息。
当然还有一点更重要的,提供的单片机的型号是STM32F103C8T6,端口实在是少得可怜,插上TFT屏之后ADC端口就用不了了,改端口的话还要自己插线,麻烦得很,就偷了个懒。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分