精华内容
下载资源
问答
  • 串口接收 串口接收流程 编程USARTx_CR1的M位来定义字长。 编程USARTx_CR2的STOP位来定义停止位位数。 编程USARTx_BRR寄存器确定波特率。 使能USARTx_CR1的UE位使能USARTx。 如果进行多缓冲通信,配置USARTx_CR3的...

    串口接收

    串口接收流程

    1. 编程USARTx_CR1的M位来定义字长。
    2. 编程USARTx_CR2的STOP位来定义停止位位数。
    3. 编程USARTx_BRR寄存器确定波特率。
    4. 使能USARTx_CR1的UE位使能USARTx。
    5. 如果进行多缓冲通信,配置USARTx_CR3的DMA使能(DMAT)。
    6. 使能USARTx_CR1的RE位为1使能接收器。
    7. 如果要使能接收中断(接收到数据后产生中断),使能USARTx_CR1的RXNEIE位为1。

    当串口接收到数据时

    1. USARTx_SR(ISR)的RXNE位置1。表明移位寄存器内容已经传输到RDR(DR)寄存器。已经接收到数据并且等待读取。
    2. 如果开启了接收数据中断(USARTx_CR1寄存器的RXNEIE位为1),则会产生中断。(程序上会执行中断服务函数)
    3. 如果开启了其他中断(帧错误等),相应标志位会置1。
    4. 读取USARTx_RDR(DR)寄存器的值,该操作会自动将RXNE位清零,等待下次接收后置位。

    串口接收流程(HAL库)

    配置过程:

    接收配置步骤①~⑥和发送流程一样,调用HAL_UART_Init函数HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);

    步骤⑦开启接收中断:HAL_StatusTypeDef HAL_UART_Receive_IT(UART_Ha

    展开全文
  • 方法描述:使用stm32CubeMX自动配置的工程文件,进行串口的不定长接收(非DMA方式)。实际问题解决尝试:1.HAL库函数只有接收定长数据HAL_UART_Receive()和HAL_UART_Receive_IT();函数声明如下:通过查看论坛等找到的...

    方法描述:使用stm32CubeMX自动配置的工程文件,进行串口的不定长接收(非DMA方式)。

    实际问题解决尝试:

    1.HAL库函数只有接收定长数据HAL_UART_Receive()和HAL_UART_Receive_IT();函数声明如下:

    f9d2b0dd0a288bb3bddfb0c6fc44ad15.png

    通过查看论坛等找到的解决方式,一种是通过HAL_UART_Receive_IT(&huart1,(uint8_t *)buffer,1);来进入串口中断回调函数HAL_UART_RxCpltCallback();函数原型如下:

    86a79d7bb5b7f650752b69626a21aa28.png

    实际测试中,这种方式每次都需要去调用HAL_UART_Receive_IT函数,系统处理效率低。

    2.通过审查生成的代码,如下所示: 首先是串口中断的代码,只有一条语句:

    33628217cb0dfbe74185321bbcd56cdf.png

    进行go to definition,可以看到接收中断会进入UART_Receive_IT这个函数。

    710497ce79313ea8c1230c62b77918e8.png

    继续往下级看,则可看到下图画线处所示,UART的RXNE中断使能被关闭了。

    2f4f2ae3eeef8bed3e2347bb9879c152.png

    所以,另一种解决方法是将这一句给屏蔽掉,HAL_UART_Receive_IT只需要执行一次就可以了,类似于打开RXNE中断的操作。但是,这种方法不好的地方在于,如果stm32cubeMX里重新配置并生成代码的话,这个地方的屏蔽会重新打开,也就是说每重新生成一次代码,cube工程不在用户操作区的改动都会被撤回。如果代码只是最终版本,这并无多大关系,如果后续还有其他的功能添加,则会很麻烦。

    3.如果cubeMX配置的串口没有开启RXNE中断的话,那我们在初始化处添加试试:

    经过测试,在经过初始化后,程序能够成功DEBUG进入串口中断函数,所以对串口中断里添加我们自己的处理函数即可,本人的处理代码是通过改编原子的串口代码而成,当然你也可以添加自己的处理算法,根据实际问题而定。这种方式即使重新生成工程,所做的处理也不会更改。

    以下是本人实际测试代码:

    ac736a81de814df3847fd4408c23cdfc.png

    通过KEIL的DEBUG测试,通过此方法处理的程序,在运行到HAL_UART_IRQHandler函数中时,由于串口ORE位会置位为1, errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));相应的不能够为RESET,则UART_Receive_IT函数并未能够进入,所以__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);这句也就不能够被执行到。

    下图为实际测试效果图:

    219ffd851d80cf4e39de4a67aab57bbf.png

    图中时间并不是正确时间,且由图可看出即使间隔10ms发一次数据,串口端都能够对数据进行准确接收方便程序处理。

    展开全文
  • 前言 早在18年,博主第一次开始写博客,内容为 C# 上位机开发,写完后收获了不错的阅读量和好评: ...在第五篇中优化了软件界面,在第三篇中实现了串口助手的基本功能,接下来再实现一些小功能: 串口

    前言

    早在18年,博主第一次开始写博客,内容为 C# 上位机开发,写完后收获了不错的阅读量和好评:

    在第五篇中优化了软件界面,在第三篇中实现了串口助手的基本功能,接下来再实现一些小功能:

    • 串口自动扫描功能
    • 接收数据保存功能
    • 加载发送文件
    • 发送数据历史记录
    • 配置文件保存和加载
    • 点击右下角linkLabel能打开浏览器功能

    Github开源仓库:https://github.com/Mculover666/SerialAssistant

    一、串口自动扫描功能

    这个功能需要使用定时器实现,在串口未打开的情况下,每隔2s扫描一次端口,并添加到串口列表中,当串口打开后,停止扫描。

    1. 添加定时器

    在设计器中拖动一个定时器控件:

    2. 添加扫描串口逻辑

    首先将扫描串口并添加到选择列表的功能编写为一个方法:

    private bool search_port_is_exist(String item, String[] port_list)
    {
        for (int i = 0; i < port_list.Length; i++)
        {
            if (port_list[i].Equals(item))
            {
                return true;
            }
        }
    
        return false;
    }
    
    /* 扫描串口列表并添加到选择框 */
    private void Update_Serial_List()
    {
        try
        {
            /* 搜索串口 */
            String[] cur_port_list = System.IO.Ports.SerialPort.GetPortNames();
    
            /* 刷新串口列表comboBox */
            int count = comboBox1.Items.Count;
            if (count == 0)
            {
                //combox中无内容,将当前串口列表全部加入
                comboBox1.Items.AddRange(cur_port_list);
                return;
            }
            else
            {
                //combox中有内容
    
                //判断有无新插入的串口
                for (int i = 0; i < cur_port_list.Length; i++)
                {
                    if (!comboBox1.Items.Contains(cur_port_list[i]))
                    {
                        //找到新插入串口,添加到combox中
                        comboBox1.Items.Add(cur_port_list[i]);
                    }
                }
    
                //判断有无拔掉的串口
                for (int i = 0; i < count; i++)
                {
                    if (!search_port_is_exist(comboBox1.Items[i].ToString(), cur_port_list))
                    {
                        //找到已被拔掉的串口,从combox中移除
                        comboBox1.Items.RemoveAt(i);
                    }
                }
            }
    
            /* 如果当前选中项为空,则默认选择第一项 */
            if (comboBox1.Items.Count > 0)
            {
                if (comboBox1.Text.Equals(""))
                {
                    //软件刚启动时,列表项的文本值为空
                    comboBox1.Text = comboBox1.Items[0].ToString();
                }
            }
            else
            {
                //无可用列表,清空文本值
                comboBox1.Text = "";
            }
            
    
        }
        catch (Exception ex)
        {
            //当下拉框被打开时,修改下拉框会发生异常
            return;
        }
    }
    

    然后双击定时器,进入定时器的超时回调函数,在其中调用串口扫描函数即可:

    private void timer1_Tick(object sender, EventArgs e)
    {
        Update_Serial_List();
    }
    

    3. 定时器启动/停止控制

    使用下面的代码即可设置定时器的时间为1s,并启动定时器:

    timer1.Interval = 1000;
    timer1.Start();
    

    在软件启动时、用户点击关闭串口时,启动定时器即可。

    停止定时器使用如下代码:

    timer1.Stop();
    

    在用户点击打开串口时,停止定时器。

    二、接收数据保存功能

    此功能需要使用C#的文件操作功能实现,参考教程:C# 文件IO操作

    1. 进入按钮回调函数

    双击【保存日志】按钮,进入该函数回调函数:

    2. 编写保存文件逻辑

    因为串口接收区显示的都是ASCII文本信息,所以直接按文本形式保存为.txt文件即可。

    private void button4_Click(object sender, EventArgs e)
      {
          DateTime time = new DateTime();
          String fileName;
    
          /* 获取当前接收区内容 */
          String recv_data = textBox1.Text;
    
          if (recv_data.Equals(""))
          {
              MessageBox.Show("接收数据为空,无需保存!");
              return;
          }
    
          /* 获取当前时间,用于填充文件名 */
          //eg.log_2021_05_08_10_13_31.txt
          time = System.DateTime.Now;
          fileName = "log" + "_" + time.ToString("yyyy_MM_dd_HH_mm_ss") + ".txt";
    
          try
          {
    
    
              /* 保存串口接收区的内容 */
              //创建 FileStream 类的实例
              FileStream fileStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
    
              //将字符串转换为字节数组
              byte[] bytes = Encoding.UTF8.GetBytes(recv_data);
    
              //向文件中写入字节数组
              fileStream.Write(bytes, 0, bytes.Length);
    
              //刷新缓冲区
              fileStream.Flush();
    
              //关闭流
              fileStream.Close();
    
              //提示用户
              MessageBox.Show("日志已保存!(" + fileName + ")");
          }
          catch (Exception ex)
          {
              //提示用户
              MessageBox.Show("发生异常!(" + ex.ToString() + ")");
          }
      }
    

    三、加载发送文件

    加载发送文件时需要分两步实现:

    ① 弹出文件选择框,供用户选择要加载的文件;
    ② 读取用户选择的文件中的内容,加载到发送框。

    1. 用户文件选择框

    参考教程:C# winform选择文件、选择文件夹、打开文件

    private void button3_Click(object sender, EventArgs e)
    {
        /* 弹出文件选择框供用户选择 */
        OpenFileDialog dialog = new OpenFileDialog();
        dialog.Multiselect = false;//该值确定是否可以选择多个文件
        dialog.Title = "请选择要加载的文件(文本格式)";
        dialog.Filter = "所有文件(*.*)|*.*";
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            string file = dialog.FileName;
            textBox2.AppendText(file);
        }
    }
    

    运行一下看以看到选择成功:

    将用于调试的代码删除:

    //textBox2.AppendText(file);
    

    2. 加载用户选择的文件内容

    这里我新建一个临时文件send_data.json,存放一段JSON格式的内容:

    {
    	"MAC":"11:22:33:44:55:66",
    	"ID":12345
    }
    


    接着编写读取文件内容的代码:

    private void button3_Click(object sender, EventArgs e)
    {
        string file;
    
        /* 弹出文件选择框供用户选择 */
        OpenFileDialog dialog = new OpenFileDialog();
        dialog.Multiselect = false;//该值确定是否可以选择多个文件
        dialog.Title = "请选择要加载的文件(文本格式)";
        dialog.Filter = "文本文件(*.txt)|*.txt";
        if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            file = dialog.FileName;
        }
        else
        {
            return;
        }
    
    
        /* 读取文件内容 */
        try
        {
            //清空发送缓冲区
            textBox2.Text = "";
    
            // 使用 StreamReader 来读取文件
            using (StreamReader sr = new StreamReader(file))
            {
                string line;
    
                // 从文件读取并显示行,直到文件的末尾 
                while ((line = sr.ReadLine()) != null)
                {
                    line = line + "\r\n";
                    textBox2.AppendText(line);
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("加载文件发生异常!(" + ex.ToString()+")");
        }
    }
    

    运行之后,选择文件:

    加载成功:

    四、发送历史记录

    每次点击发送之后,将发送的内容添加到下方的combox中记录,方便下次直接选中。

    1. 记录发送数据

    记录发送数据的时候要先检查一下是否有重复,不要重复添加。

    /* 记录发送数据 */
    //先检查当前是否存在该项
    if (comboBox7.Items.Contains(textBox2.Text) == true)
    {
        return;
    }
    else
    {
        comboBox7.Items.Add(textBox2.Text);
    }
    

    运行看看效果,多次发送相同的数据和不同的数据:

    2. 选择历史数据

    当用户选择某条历史数据时,将其加载到发送文本框中。

    进入comboBox的回调函数中:

    在选项更改时,添加逻辑:

    private void comboBox7_SelectedIndexChanged(object sender, EventArgs e)
    {
        //清空发送缓冲区
        textBox2.Text = comboBox7.SelectedItem.ToString();
    }
    

    五、配置文件保存和加载

    六、linkLabel功能

    1. 进入点击回调函数


    可以看到系统生成的回调函数:

    private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
    
    }
    

    2. 编写回调函数

    在回调函数中,调用系统浏览器访问链接:

    private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
        this.linkLabel1.Links[this.linkLabel1.Links.IndexOf(e.Link)].Visited = true;
        string targetUrl = "https://github.com/Mculover666/SerialAssistant";
    
    
        try
        {
            //尝试用edge打开
            System.Diagnostics.Process.Start("msedge.exe", targetUrl);
            return;
        }
        catch (Exception)
        {
            //edge它不香吗
        }
    
        try
        {
            //好吧,那用chrome
            System.Diagnostics.Process.Start("chrome.exe", targetUrl);
            return;
        }
        catch
        {
            //chrome不好用吗
        }
        try
        {
            //IE也不是不可以
            System.Diagnostics.Process.Start("iexplore.exe", targetUrl);
        }
    
        catch
        { 
            //没救了,砸了吧!
        }
    }
    

    七、定时发送功能

    1. 添加定时器


    双击进入定时器回调函数:

    private void timer2_Tick(object sender, EventArgs e)
    {
    	button5_Click(button5, new EventArgs());    //调用发送按钮回调函数
    }
    

    2. 定时时间的设置

    3. 开启/关闭自动发送

    需要提前处理好发送CheckBox的使能与失能逻辑

    进入checkBox的回调函数:

    private void checkBox4_CheckedChanged(object sender, EventArgs e)
     {
         if (checkBox4.Checked)
         {
             //自动发送功能选中,开始自动发送
             numericUpDown1.Enabled = false;
             timer2.Interval = (int)numericUpDown1.Value;
             timer2.Start(); 
         }
         else
         {
             //自动发送功能未选中,停止自动发送
             numericUpDown1.Enabled = true;
             timer2.Stop();
         }
     }
    
    展开全文
  • 在进行PID参数整定的工作过程中,我需要将电机的转速、扭矩、母线电压、母线电流、相电压、相电流等参数通过串口传递到电脑上用Matlab来进行BP神经网络算法的PID整定计算,再将计算得到的PID参数传给电机的驱动板,...

    在进行PID参数整定的工作过程中,我需要将电机的转速、扭矩、母线电压、母线电流、相电压、相电流等参数通过串口传递到电脑上用Matlab来进行BP神经网络算法的PID整定计算,再将计算得到的PID参数传给电机的驱动板,来控制电机运行。在整个过程中采用了Matlab来进行串口数据的读写。目前整个流程已经走通,在此将实现过程中遇到的问题,解决方法,以及尚未解决的疑问进行总结。

    首先声明本人是一个初学Matlab的新手,因此下面大部分都是自己摸索的东西,可能有肤浅或者不简洁的地方。这里写出来主要还是对自己工作的一个总结,以便未来的使用和提高。

    Matlab串口数据接收一般可以通过两种方法来实现,一种是直接调用tmtool工具,找到相应的串口,配置串口参数,生成代码;另一种方法是通过一系列与串口有关的函数来对串口进行操作。

    通过tmtool进行串口设计比较简单,如图所示,在Communiate项目栏中可以对串口进行读写操作并输出,这里可以设置数据种类(Data type)为ASCII码,二进制、二进制数据块(这里笔者的理解是类似一个数据帧,拥有帧头、传输数据个数位等)。通过数据格式(Data format)来设置接收数据的格式,例如ASCII码的格式有字符、字符串、带换行符的字符串等;二进制有位数等。还可以设置写入和读取是否按照HEX(16进制)进行表示。

    a224a248d021

    image.png

    在Configure项目栏中,可以对串口的参数进行配置,如图所示。

    BauRate是对串口波特率的设置,只有上下位机采用相同的波特率时串口才能够进行正常通讯。

    DataBits : 是数据位,有8位和9位两种,一般选8位。

    DataTerminalReady(DTR) : 数据终端就绪,表明机器已经准备好可以接受数据的一个标志位,一般用在RS232的串口场合。

    FlowControl : 流控制位,当数据读写速度存在差异时,可以采用流控制,来开启和关闭数据流,有硬件和软件控制两种方式。

    InputBufferSize/OutputBufferSize : 是输入缓存大小和输出缓存大小,单位是字节。

    Parity : 奇偶校验位,这里应该与下位机格式相匹配。

    RequestToSend(RTS): 请求结束位,也是在RS232中常用。

    StopBits: 结束位,这里应该与下位机格式相匹配。

    Terminator: 中断标志,这里是以某个字符作为中断函数入口的标志,检测到这个字符存在后即可进入回调函数。

    Timeout: 溢出时间,当开始接收后超过这个时间还没有数据还没有接收完成,即可认为数据接收失败,这个时间可按照实际传输周期来设定,一般越小越好。

    ByreOrder: 是数据的传输模式,有大端模式和小端模式两种。小端模式是指数据的高字节保存在内存的高地址中,大端模式是指数据的高字节保存在内存的低地址中。以unsigned int value = 0x12345678为例,该数分为四个字节 12 34 56 78:

    小端模式情况下,字节存储顺序是

    Buffer[3]=0x12 ------高位

    Buffer[2]=0x34

    Buffer[1]=0x56

    Buffer[0]=0x78 ------低位

    大端模式下,字节存储顺序是

    Buffer[3]=0x78------高位

    Buffer[2]=0x56

    Buffer[1]=0x34

    Buffer[0]=0x12 ------低位

    大端模式和小端模式只在传输的一个数据是个多字节数据时,才需要区分。一般来说下位机ARM是按照小端模式来存储的,这一现象我会在另一篇下位机串口通讯时将共用体时举出例子。因为我这里传输的数据每次就是一个字节,因此无所谓大小端。

    a224a248d021

    image

    Session Log项目栏可以将之前的各种操作用函数语言来表示。

    采用与串口有关的函数也能实现对串口的操作。作者主要是通过这种方法来实现整个串口的运行。

    首先了解几个主要函数:

    delete(instrfindall) : 删除所有串口信息,在程序结束后没有删除串口信息,可能会导致串口被占用,下一次程序无法使用串口或者其他软件无法使用串口。因此在不再使用串口的时候,应将串口释放。为了Matlab运行的正确性,也可以将运行中的所有工作变量删除,这里可以采用clera all语句。

    obj1=serial('com2'): 定义某串口为变量obj1,也就是相当于为串口com2起了一个名字叫obj1,后面对obj1操作就相当于对com2操作。

    fclose(obj1)/fopen(obj1): 关闭串口和开启串口,有些参数设定需要先关闭串口,因此在进行串口参数设定前先将串口关闭,设定完成后再打开。

    obj1:直接在命令行窗口输入定义好的串口变量,可以看到一个串口obj1的参数列表。

    set(obj1, 'BaudRate', 115200): 通过set函数可以对obj1进行参数设定,格式是set(串口变量名,'参数名称',参数),这里是设定波特率是115200。

    set(obj1, 'InputBufferSize', 100): 输入缓存大小 100 字节。

    set(obj1, 'OutputBufferSize', 100): 输出缓存大小 100 字节。

    set(obj1, 'Timeout', 1.0): 溢出时间 1.0 s。

    set(obj1,'BytesAvailableFcnMode','terminator'): 回调函数中断的模式,有两种,一种是terminator模式,关键词中断,当检测到某一关键字时进行中断。一种是byte模式,当二进制达到某个字节数时中断。

    set(obj1,'terminator','D'): 中断关键字是ASCII的D

    set(obj1,'BytesAvailableFcn',@my_callback): 回调函数的设定,当发生上述中断时,进入回调函数my_callback,在回调函数里可以对数据进行读写等操作。

    (上面串口中断模式还有byte模式,这里给出函数示例: set(obj1,'BytesAvailableFcnMode','terminator')

    set(obj1,'byte',24) )

    在回调函数my_callback里面,可以进行串口数据的读取和发送等操作,下面先介绍几个串口接收和发送相关的函数:

    data=fread(obj1, 24):向串口obj1读取24个数据,因为我这里数据是一个数据一个字节,因此也就相当于24个字节,这里默认情况下是按照一个数据8位,也可以加上数据格式,告诉电脑一个每个数据是什么格式,如data= fread(obj1, 24,'short')。

    fwrite(obj2, sendbuff, 'uint8'):向串口obj2发送数据,被发送的数据存储在笔者自定的一个向量sendbuff中,这里会将sendbuff中的所有数据按照unit8的的格式依次发送。

    这里我将整个串口设置和回调函数的代码给出:

    代码1:主函数

    因为项目需要,这里有一个串口接收数据,一个串口发送数据。这里我接收的数据是一个数据帧24个字节,发送的数据是8个字节。在接收的字节中帧头是'D',所以采用关键字中断模式,中断标志是'D'。

    %主函数 进行串口设置和开启

    delete(instrfindall)

    clear obj1

    clear obj2

    clear all

    global obj1;

    global obj2;

    global data;

    global sendbuff;

    global data1;

    global data2;

    global data3;

    global data4;

    global data5;

    global data6;

    global n;

    n=0;

    data=zeros(24,1)

    sendbuff=zeros(1,8);

    sendbuff(7)= hex2dec('0D');

    sendbuff(8)= hex2dec('0A');

    data1=0;

    data2=0;

    data3=0;

    data4=0;

    data5=0;

    data6=0;

    obj1=serial('com9');

    fclose(obj1);

    set(obj1, 'InputBufferSize', 100);

    set(obj1, 'OutputBufferSize', 100);

    set(obj1, 'BaudRate', 112500);

    set(obj1, 'Timeout', 1.0);

    set(obj1,'BytesAvailableFcnMode','terminator');

    set(obj1,'terminator','D');

    set(obj1,'BytesAvailableFcn',@my_callback1);

    fopen(obj1);

    obj2=serial('com8');

    fclose(obj2);

    set(obj2, 'InputBufferSize', 24);

    set(obj2, 'OutputBufferSize', 24);

    set(obj2, 'BaudRate', 115200);

    set(obj2, 'Timeout', 1.0);

    fopen(obj2);

    代码2:回调函数

    进入回调函数后对数据进行读取和处理,然后发送给下位机

    接收数据帧的格式是:

    字节1:'D',10进制表示是68

    字节2:0x05,10进制表示是5,1-2是帧头,然后是数据位

    字节3-4:表示电机转速,16位数据

    字节5-6:表示负载扭矩,16位数据

    字节7-10:表示母线电压,32位数据

    字节11-14:表示母线电流,32位数据

    字节15-18:表示相电压,32位数据

    字节19-22:表示相电流,32位数据

    字节23:0X0D

    字节24:0X0A

    发送数据帧的格式:

    字节1-2:表示Kp,16位数据

    字节3-4:表示Ki,16位数据

    字节5-6:表示Kd,16位数据

    字节7:0X0D

    字节8:0X0A

    function my_callback1(obj1,event)

    global data;

    global obj2;

    global sendbuff;

    global data1;

    global data2;

    global data3;

    global data4;

    global data5;

    global data6;

    global n;

    n=n+1;

    data= fread(obj1, 24)

    if lenth(data)==24

    if data(1) == 68

    if data(2) == 5

    data1 = (data(3)*256+data(4))/1000;

    data2 = (data(5)*256+data(6))/1000;

    data3 = (data(7)*16777216+data(8)*65536+data(9)*256+data(10))/100;

    data4 = (data(11)*16777216+data(12)*65536+data(13)*256+data(14))/100;

    data5 = (data(15)*16777216+data(16)*65536+data(17)*256+data(18))/100;

    data6 = (data(19)*16777216+data(20)*65536+data(21)*256+data(22))/100;

    end

    end

    if data(1) == 5

    data1 = (data(2)*256+data(3))/1000;

    data2 = (data(4)*256+data(5))/1000;

    data3 = (data(6)*16777216+data(7)*65536+data(8)*256+data(9))/100;

    data4 = (data(10)*16777216+data(11)*65536+data(12)*256+data(13))/100;

    data5 = (data(14)*16777216+data(15)*65536+data(16)*256+data(17))/100;

    data6 = (data(18)*16777216+data(19)*65536+data(20)*256+data(21))/100;

    end

    sendbuff(1)=floor((data1+data4)/256);

    sendbuff(2)=floor((data1+data4));

    sendbuff(3)=floor((data2+data5)/256);

    sendbuff(4)=floor((data2+data5));

    sendbuff(5)=floor((data3+data6)/256);

    sendbuff(6)=floor((data4+data6));

    fwrite(obj2, sendbuff, 'uint8');

    %my_fwrite(obj2, sendbuff, 'uint8');

    end

    end

    关于串口速度的讨论

    在进行PID参数整定时,由于整个PID的调整周期较短,因此希望整个串口读取、处理、发送的时间最好在2ms以内完成。这个速度对于计算机来说其实是不容易实现的,因此如何提高整个回调函数的运算速度成为编写代码的关键。这里我发现了一些提高运行速度的方法:

    对于串口读写速度,主要是串口发送函数 fwrite(obj2, sendbuff, 'uint8')消耗了太多的时间,运行时这里耗费的时间有20ms+,这对于整个项目时不可接受的,笔者在思考和尝试的时候发现了一个方法可以让这个时间缩小到0.7ms左右。那就是用一个新的my_fwrite.m文件代替原来的内部函数fwrite.m。并将上述的代码改为my_fwrite(obj2, sendbuff, 'uint8')。

    新的文件如下:

    function my_fwrite(obj, varargin)

    %FWRITE Write binary data to instrument.

    %

    % FWRITE(OBJ, A) writes the data, A, to the instrument connected to

    % interface object, OBJ.

    %

    % The interface object must be connected to the instrument with the

    % FOPEN function before any data can be written to the instrument

    % otherwise an error will be returned. A connected interface object

    % has a Status property value of open.

    %

    % FWRITE(OBJ,A,'PRECISION') writes binary data translating MATLAB

    % values to the specified precision, PRECISION. The supported

    % PRECISION strings are defined below. By default the 'uchar'

    % PRECISION is used.

    %

    % MATLAB Description

    % 'uchar' unsigned character, 8 bits.

    % 'schar' signed character, 8 bits.

    % 'int8' integer, 8 bits.

    % 'int16' integer, 16 bits.

    % 'int32' integer, 32 bits.

    % 'uint8' unsigned integer, 8 bits.

    % 'uint16' unsigned integer, 16 bits.

    % 'uint32' unsigned integer, 32 bits.

    % 'single' floating point, 32 bits.

    % 'float32' floating point, 32 bits.

    % 'double' floating point, 64 bits.

    % 'float64' floating point, 64 bits.

    % 'char' character, 8 bits (signed or unsigned).

    % 'short' integer, 16 bits.

    % 'int' integer, 32 bits.

    % 'long' integer, 32 or 64 bits.

    % 'ushort' unsigned integer, 16 bits.

    % 'uint' unsigned integer, 32 bits.

    % 'ulong' unsigned integer, 32 bits or 64 bits.

    % 'float' floating point, 32 bits.

    %

    % FWRITE(OBJ, A, 'MODE')

    % FWRITE(OBJ, A, 'PRECISION', 'MODE') writes data asynchronously

    % to the instrument when MODE is 'async' and writes data synchronously

    % to the instrument when MODE is 'sync'. By default, the data is

    % written with the 'sync' MODE, meaning control is returned to

    % the MATLAB command line after the specified data has been written

    % to the instrument or a timeout occurs. When the 'async' MODE is

    % used, control is returned to the MATLAB command line immediately

    % after executing the FWRITE function.

    %

    % The byte order of the instrument can be specified with OBJ's

    % ByteOrder property.

    %

    % OBJ's ValuesSent property will be updated by the number of values

    % written to the instrument.

    %

    % If OBJ's RecordStatus property is configured to on with the RECORD

    % function, the data written to the instrument will be recorded in

    % the file specified by OBJ's RecordName property value.

    %

    % Example:

    % s = visa('ni', 'ASRL2::INSTR');

    % fopen(s);

    % fwrite(s, [0 5 5 0 5 5 0]);

    % fclose(s);

    %

    % See also ICINTERFACE/FOPEN, ICINTERFACE/FPRINTF, ICINTERFACE/RECORD,

    % ICINTERFACE/PROPINFO, INSTRHELP.

    %

    % MP 7-13-99

    % Copyright 1999-2012 The MathWorks, Inc.

    % Error checking.

    if ~isa(obj, 'icinterface')

    error(message('instrument:fwrite:invalidOBJInterface'));

    end

    if length(obj)>1

    error(message('instrument:fwrite:invalidOBJDim'));

    end

    % Parse the input.

    switch nargin

    case 1

    error(message('instrument:fwrite:invalidSyntaxA'));

    case 2

    cmd = varargin{1};

    precision = 'uchar';

    mode = 0;

    case 3

    % Original assumption: fwrite(obj, cmd, precision);

    [cmd, precision] = deal(varargin{1:2});

    mode = 0;

    if ~(isa(precision, 'char') || isa(precision, 'double'))

    error(message('instrument:fwrite:invalidArg'));

    end

    if strcmpi(precision, 'sync')

    % Actual: fwrite(obj, cmd, mode);

    mode = 0;

    precision = 'uchar';

    elseif strcmpi(precision, 'async')

    % Actual: fwrite(obj, cmd, mode);

    mode = 1;

    precision = 'uchar';

    end

    case 4

    % Ex. fprintf(obj, format, cmd, mode);

    [cmd, precision, mode] = deal(varargin{1:3});

    if ~ischar(mode)

    error(message('instrument:fwrite:invalidMODE'));

    end

    if strcmpi(mode, 'sync')

    mode = 0;

    elseif strcmpi(mode, 'async')

    mode = 1;

    else

    error(message('instrument:fwrite:invalidMODE'));

    end

    otherwise

    error(message('instrument:fwrite:invalidSyntaxArgv'));

    end

    % % % % Error checking.

    % % % if ~isa(precision, 'char')

    % % % error(message('instrument:fwrite:invalidPRECISIONstring'));

    % % % end

    % % % if ~(isnumeric(cmd) || ischar(cmd))

    % % % error(message('instrument:fwrite:invalidA'));

    % % % end

    % Convert the data to the requested precision.

    switch (precision)

    case {'uchar', 'char'}

    cmd = uint8(cmd);

    type = 5;

    signed = 0;

    case {'schar'}

    cmd = int8(cmd);

    type = 5;

    signed = 1;

    case {'int8'}

    cmd = int8(cmd);

    type = 0;

    signed = 1;

    case {'int16', 'short'}

    cmd = int16(cmd);

    type = 1;

    signed = 1;

    case {'int32', 'int', 'long'}

    cmd = int32(cmd);

    type = 2;

    signed = 1;

    case {'uint8'}

    cmd = uint8(cmd);

    type = 0;

    signed = 0;

    case {'uint16', 'ushort'}

    cmd = uint16(cmd);

    type = 1;

    signed = 0;

    case {'uint32', 'uint', 'ulong'}

    cmd = uint32(cmd);

    type = 2;

    signed = 0;

    import java.lang.Long

    for iLoop = 1:length(cmd)

    tmp(iLoop) = Long(cmd(iLoop)); %#ok

    end

    cmd=tmp;

    case {'single', 'float32', 'float'}

    cmd = single(cmd);

    type = 3;

    signed = 1;

    case {'double' ,'float64'}

    cmd = double(cmd);

    type = 4;

    signed = 1;

    otherwise

    error(message('instrument:fwrite:invalidPRECISION'));

    end

    % i2c does not support async mode

    if mode == 1

    error(message('instrument:fwrite:i2cAyncNotSupported'));

    end

    % Call the write java method.

    try

    fwrite(igetfield(obj, 'jobject'), cmd, length(cmd), type, mode, signed);

    catch aException

    newExc = MException('instrument:fwrite:opfailed', aException.message);

    throw(newExc);

    end

    除此之外,在进行数据的处理的时候,应尽量对数据进行乘除取整取余等运算而不是调用一些强制转换的函数或者移位函数。笔者考虑可能是在调用matlab系统内部函数花费了较多的时间。

    同时,这个运算速度和matlab版本以及电脑的CPU性能有关,同样的程序i5跑起来是2ms,i7跑起来0.8ms。

    通过以上方法将原来数十微妙的运行速度降到了运行一次在一微妙以内。所以还是有些效果的。

    展开全文
  • 串口接收: GPRMC格式的GPS信息,包含推荐最小定位信息。 消息格式为: $GPRMC,HHMMSS.SS,A,DDMM.MMM,N,DDDMM.MMM,W,Z.Z,Y.Y,DDMMYY,D.D,V *CC: @Override public void run() { bytes = mmInStream.read(buffer);
  • stm32串口的使用,可以说是这个芯片的灵魂了,DMA、IDLE空闲中断、不定长接收,这些都让stm32的串口产生异常强大的力量。如何用好这些特性,并避免踩坑,本文对几点主要配置和逻辑做了整理。感谢CSDN上各位大虾的...
  • #include #define TTY "/dev/ttyUSB0" //usb转串口工具的设备名#define RATE B9600 //波特率int ... //串口文件描述符int rnum=0; //接收字节数int wnum=0; //发送字节数struct termios newopt;struct termios oldop...
  • 在开发的过程中发现,发现给出的协议文档(即帧数据指定位对应的含义)和打码机上实际跑的程序有一些出入,所以在学习协议的过程中,使用了下图所示的串口工具,通过实际的输入输出来确定协议的变动情况。...
  • stc51单片机串口接收多字节数据

    千次阅读 2021-06-03 16:52:54
    stc51单片机串口接收多字节数据 简介 51单片机有2个定时器,一个做串口波特率,一个做数据截止帧延时检测,硬件平台测试使用的是stc8的单片机,但是可以往51移植 代码 #include "stc8.h" unsigned char flag=0; //...
  • 传输文件:要用到rz和sz命令,另外,使用putty也可以在提供的小工具psftp中传输,不过,还是推荐一站式解决问题,于是就上网翻一翻,找了些办法总结到这里,您没事儿的时候也可以抽空回回贴,说说更好的办法。...
  • 一、应用简介 本文介绍串口中断接收不定长字符串的方法。采用定时中断给串口2的接收提供定时,再一次中断内(1ms)接收数据的长度不变则认为... @brief 本文件是用于串口驱动 @author 青梅煮久 @version r0.1 @date.
  • 串口之间的文件传输

    2021-11-06 11:56:01
    串口之间的文件传输一、实验概述二、实验详情三、总结参考 一、实验概述 将两台笔记本电脑,借助 usb转rs232 模块和杜邦线,建立起串口连接。然后用串口助手等工具软件(带文件传输功能)将一台笔记本上的一个大...
  • STM32如何高效接收串口数据?

    千次阅读 2021-04-22 17:58:12
    目录USART3_DR的地址DMA的通道DMA的中断USART接收回调函数头文件源码DMA的基本配置环形队列接收数据函数原型参考用例总结硬件:stm32f103cbt6软件:STM32F1...
  • 串口通信实验 Printf支持 printf向串口发送一些字符串数据。如果使用串口2,可以修改while((USART1->SR&0X40)==0);和USART1->DR = (u8) ch; 中的USART1为USART2. //加入以下代码,支持printf函数,而不需要...
  • 1,电脑通过串口1给STM32F407芯片发送数据(不定长,按照645格式来),芯片根据串口接收中断接收到数据后,通过DMA将数据存储在内存。当检测到数据接受完毕,产生接收完成标志位置位。当407检测到这个中断标志位后从...
  • 这篇调试助手比较详细: 程序链接--喜欢的点个赞哦。...编译器会自动检测的,建议先安装Visual Studio,QT安装时会自动识别到。 三、创建项目工程 此处省略(不浪费大家时间)........不懂可以自行百
  • 串口接收中断程序配置过程(HAL) 初始化相关参数,使能串口: HAL_UART_Init(); 串口相关IO口配置,复用配置。在HAL_UART_MspInit中调用HAL_GPIO_Init函数 串口接收中断优先级配置和使能 HAL_NVIC_...
  • /**===========================================================================@file board_timer.c@brief 本文件是基本定时器TIM6的驱动@author 青梅煮久@version r0.1@date 2021/01/15-----------------------...
  • 需求:由于目前需要实现对于多个串口数据的收发,同时,或许可以利用多线程实现异步收发等; 问题:但是通过Windows平台的虚拟串口在一个Windows笔记本上没有实现和linux的通信;而且硬件设备来实现串口数据输入的...
  • 参考:串口 stm32 实现中断接收 打开板子上的led灯 作者:点灯小哥 发布时间: 2021-03-07 11:55:35 网址:https://blog.csdn.net/weixin_46016743/article/details/114481125 目录方法:在usart.c上增加配置NVIC...
  • HAL库实现将接收逻辑全写在中断服务函数里面而不重定义回调函数,并且不会不进中断!
  • 串口波特率115200
  • // 设置串口接收数据寄存器 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Rx_Buf; // 设置接收缓冲区首地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // 设置外设为数据源,外设寄存器 -> ...
  • 奇偶检验无,停止位1,接收和发送都使能 GPIO引脚设置 USART1_RX/USART_TX(这里一般自动设置好了) NVIC Settings 一栏使能接收中断 (3) 时钟设置 (4) 创建工程文件 2、代码添加 (1)在main.c和usart.c中添加...
  • 如题,使用多个传感器接发送不同数据比如'one-110' 'two-120' 'three-130' 'four-140',然后串口接收数据后在串口助手界面上显示传感器的数据。但是只能存储在strRec中,然后一起显示在一个窗口中。但是为了显示清晰...
  • 中断原理讲解例程流程详解库函数分析详解对应寄存器介绍对应函数介绍对应注释详解本篇文章提供两种方法:一种是 :IDLE 接收空闲中断+DMA一种是: IDLE 接收空闲中断+RXNE接收数据中断都可完成串口数据的收发知识点...
  • 打个基础先,DMA串口就是硬件自动接收数据,减少MCU的负荷,想要的时候读取下就行了 单片机:STM32L051C8T6 功能:DMA接收串口1数据,定时500ms打印串口数据 国际惯例上STM32CubeMx的相关配置图: 1.串口基础配置...
  • 1、根据传感器设备的自带资料,利用厂家存在的数据接收软件,测试设备是否和PC通信正常。 2、附上一组接收到的有效数据,方便后期回溯。 二、linux环境下传感设备串口通信的实现。 1、Ubuntu中查看串口信息。 2...
  • ARM 开发板嵌入式linux系统与主机PC通过串口传输文件本来以为按以下两篇文章就可以几步轻松搞定这个问题,没想到遇到两个小麻烦:1,我用的xp虚拟机下redhat9.0做主机,按照下面第一篇文章,我在Makefile中把CC=gcc...
  • //开启DMA接收中断 /* USER CODE END 2 */ 在stm32f1xx_it.c文件中 void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1);...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 46,718
精华内容 18,687
关键字:

串口文件自动接收