grbl学习之旅---serial篇

2023-05-16

serial.c和serial.h文件是实现了通过串行端口发送和接受字节的功能。

首先是serial.h中定义了基本函数和常量大小:

#ifndef RX_BUFFER_SIZE
  #define RX_BUFFER_SIZE 128//定义接受缓存的大小
#endif
#ifndef TX_BUFFER_SIZE
  #define TX_BUFFER_SIZE 64//定义发送缓存的大小
#endif

#define SERIAL_NO_DATA 0xff//串行无数据?

#ifdef ENABLE_XONXOFF
  #define RX_BUFFER_FULL 96 // XOFF high watermark
  #define RX_BUFFER_LOW 64 // XON low watermark
  #define SEND_XOFF 1
  #define SEND_XON 2
  #define XOFF_SENT 3
  #define XON_SENT 4
  #define XOFF_CHAR 0x13
  #define XON_CHAR 0x11
#endif

void serial_init();//serial初始化


//写入一个字节到TX串行缓冲区。主程序调用。
void serial_write(uint8_t data);

//获取串行读缓冲区中的第一个字节。主程序调用。
uint8_t serial_read();

/
//在读缓冲区中重置和清空数据。通过停止和复位的应用。
void serial_reset_read_buffer();

//返回RX串行缓冲区中使用的字节数。
uint8_t serial_get_rx_buffer_count();

// 返回TX串行缓冲区中使用的字节数。

//注:除用于调试和确保无TX阻碍外,未使用。
uint8_t serial_get_tx_buffer_count();

#endif
#ifdef ENABLE_XONXOFF
  #define RX_BUFFER_FULL 96 // XOFF high watermark
  #define RX_BUFFER_LOW 64 // XON low watermark
  #define SEND_XOFF 1
  #define SEND_XON 2
  #define XOFF_SENT 3
  #define XON_SENT 4
  #define XOFF_CHAR 0x13
  #define XON_CHAR 0x11
#endif

这段什么水印不是很懂,如果有大佬看到了,希望能告诉我下,不胜感激。

然后是对serial.c中函数具体实现方法的理解:

uint8_t serial_rx_buffer[RX_BUFFER_SIZE];
uint8_t serial_rx_buffer_head = 0;
volatile uint8_t serial_rx_buffer_tail = 0;

uint8_t serial_tx_buffer[TX_BUFFER_SIZE];
uint8_t serial_tx_buffer_head = 0;
volatile uint8_t serial_tx_buffer_tail = 0;

这里是定义了两个数组和分别的数组头和尾下标,实际上是定义的一个循环队列。

#ifdef ENABLE_XONXOFF
  volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
#endif

这段代码和上面的一个ENABLE_XONXOFF一样的,不理解,按字面是什么流控制状态变量?希望有看到理解的人能告诉我下。

uint8_t serial_get_rx_buffer_count()
{
  uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
  if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); }
  return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head));
}

该函数实现的功能是返回rx缓存中用到的字节数。因为写入数据时从头写入的,即写入数据,头下标前移,读出数据,尾下标前移的顺序操作,所以当尾下标在头下标之后直接相减就是数据大小,否则说明头下标已经转了一圈到了尾下标的后面要用整个数组大小减。

// 返回TX串行缓冲区中使用的字节数。
// 注:除用于调试和确保无TX阻碍外,未使用。
uint8_t serial_get_tx_buffer_count()
{
  uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile
  if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); }
  return (TX_BUFFER_SIZE - (ttail-serial_tx_buffer_head));
}

过程与返回rx缓冲区字节数类似。

void serial_init()
{
  // Set baud rate
  #if BAUD_RATE < 57600
    uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1)/2 ;
    UCSR0A &= ~(1 << U2X0); // baud doubler off  - Only needed on Uno XXX
  #else
    uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2;
    UCSR0A |= (1 << U2X0);  // baud doubler on for high baud rates, i.e. 115200
  #endif
  UBRR0H = UBRR0_value >> 8;
  UBRR0L = UBRR0_value;
            
  // enable rx and tx
  UCSR0B |= 1<<RXEN0;
  UCSR0B |= 1<<TXEN0;
	
  // enable interrupt on complete reception of a byte
  UCSR0B |= 1<<RXCIE0;
	  
  // defaults to 8-bit, no parity, 1 stop bit
}
这个初始化函数是关于对寄存器的设置和符号字有关,目前不是很懂,之后再看。希望有明白的人告诉我下。
// 写入一个字节到TX串行缓冲区。主程序调用。
// 检查我们是否可以加快写字符串的速度,而不是单字节。
void serial_write(uint8_t data) {
  // 将头下标前移一位
  uint8_t next_head = serial_tx_buffer_head + 1;
  if (next_head == TX_BUFFER_SIZE) { next_head = 0; }

  // 等待缓冲区中有空间
  while (next_head == serial_tx_buffer_tail) { 
    // TODO: 调整 st_prep_buffer() calls to be executed here during a long print.    
    if (sys.rt_exec_state & EXEC_RESET) { return; } // 只检查中止,以避免无休止的循环。.
  }

  // 存储数据和前进头
  serial_tx_buffer[serial_tx_buffer_head] = data;
  serial_tx_buffer_head = next_head;
  
  // 启用数据寄存器空中断以确保TX流正在运行。
  UCSR0B |=  (1 << UDRIE0); 
}
这个函数里面有个判断是
if (sys.rt_exec_state & EXEC_RESET) { return; } // 只检查中止,以避免无休止的循环。.

其中的sys是在system.h中定义的全局系统变量,结构如下:

typedef struct {
  uint8_t abort;                 // 系统中止标志。强制退出回主循环for复位。
  uint8_t state;                 // 当前grbl的状态
  uint8_t suspend;               // 系统延迟 bitflag 变量用来管理 保存,取消,安全门

  volatile uint8_t rt_exec_state;  // 全局实际时间执行者bitflag 变量对于状态管理. 看执行掩码
                                   //Global realtime executor bitflag variable for state management. See EXEC bitmasks.
  volatile uint8_t rt_exec_alarm;  // 全局实际时间执行者bitflag 变量对于设置各种报警
                                 
  int32_t position[N_AXIS];      // Real-time machine (aka home) position vector in steps. 
                                 //实时机位置矢量逐步地
                                 // NOTE: This may need to be a volatile variable, if problems arise.                             

  uint8_t homing_axis_lock;       // Locks axes when limits engage. Used as an axis motion mask in the stepper ISR.
  volatile uint8_t probe_state;   // Probing state value.  Used to coordinate the probing cycle with stepper ISR.
  int32_t probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
  uint8_t probe_succeeded;        // Tracks if last probing cycle was successful.
} system_t;

后面的变量具体意思也不是很清楚,rt_exec_state字面看是状态的感觉。

// 数据寄存器空中断处理程序
ISR(SERIAL_UDRE)
{
  uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
  
  #ifdef ENABLE_XONXOFF
    if (flow_ctrl == SEND_XOFF) { 
      UDR0 = XOFF_CHAR; 
      flow_ctrl = XOFF_SENT; 
    } else if (flow_ctrl == SEND_XON) { 
      UDR0 = XON_CHAR; 
      flow_ctrl = XON_SENT; 
    } else
  #endif
  { 
    //从缓冲区发送一个字节
    UDR0 = serial_tx_buffer[tail];
  
    // 更新尾下标的位置
    tail++;
    if (tail == TX_BUFFER_SIZE) { tail = 0; }
  
    serial_tx_buffer_tail = tail;
  }
  
  // 关闭数据寄存器空中断停止TX流,结束数据传输
  if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
}

这个函数是中断函数。还是里面的有一部分代码不是很清楚:

 #ifdef ENABLE_XONXOFF
    if (flow_ctrl == SEND_XOFF) { 
      UDR0 = XOFF_CHAR; 
      flow_ctrl = XOFF_SENT; 
    } else if (flow_ctrl == SEND_XON) { 
      UDR0 = XON_CHAR; 
      flow_ctrl = XON_SENT; 
    } else

而且里面#ifdef...if...else...#endif,else的内容写在了#endif之后,可能接触的少了,没看过这样的写法,希望有人能指点下。

// 获取串行读缓冲区中的第一个字节。主程序调用。
uint8_t serial_read()
{
  uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile)
  if (serial_rx_buffer_head == tail) {//判断队列是否为空
    return SERIAL_NO_DATA;
  } else {
    uint8_t data = serial_rx_buffer[tail];
    
    tail++;
    if (tail == RX_BUFFER_SIZE) { tail = 0; }//尾下标指到了数组边界外,循环队列从零开始
    serial_rx_buffer_tail = tail;

    #ifdef ENABLE_XONXOFF
      if ((serial_get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) { 
        flow_ctrl = SEND_XON;
        UCSR0B |=  (1 << UDRIE0); // Force TX
      }
    #endif
    
    return data;
  }
}

#ifdef..#endif这之间的看不懂T.T

ISR(SERIAL_RX)
{
  uint8_t data = UDR0;
  uint8_t next_head;
  
  // 从串行流中直接提取实时命令字符。这些字符是
  // 不传递到缓冲区,但这些设置系统状态标记位用于实时执行。
  switch (data) {
    case CMD_STATUS_REPORT: bit_true_atomic(sys.rt_exec_state, EXEC_STATUS_REPORT); break; // Set as true
    case CMD_CYCLE_START:   bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); break; // Set as true
    case CMD_FEED_HOLD:     bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD); break; // Set as true
    case CMD_SAFETY_DOOR:   bit_true_atomic(sys.rt_exec_state, EXEC_SAFETY_DOOR); break; // Set as true
    case CMD_RESET:         mc_reset(); break; // 呼叫运动控制重置例程。
    default: // 将字符写入缓冲区    
      next_head = serial_rx_buffer_head + 1;
      if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
    
      // 将数据写入缓冲区,除非它已满。
      if (next_head != serial_rx_buffer_tail) {//判断是否队列满了
        serial_rx_buffer[serial_rx_buffer_head] = data;
        serial_rx_buffer_head = next_head;    
        
        #ifdef ENABLE_XONXOFF
          if ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
            flow_ctrl = SEND_XOFF;
            UCSR0B |=  (1 << UDRIE0); // Force TX
          } 
        #endif
        
      }
      //TODO: else alarm on overflow?
  }
}

中断函数判断读取字符的类型。

//复位操作,让尾下标指向头下标,循环队列代表空
void serial_reset_read_buffer() 
{
  serial_rx_buffer_tail = serial_rx_buffer_head;

  #ifdef ENABLE_XONXOFF
    flow_ctrl = XON_SENT;
  #endif
}

        总的来说,函数功能理解不难,主要是具体实现,特别是#ifdef...#endif里面的代码段不懂,然后就是寄存器设置的代码不是很清楚。希望有明白grbl的大佬能指点下。也可以相互学习,共同进步。



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

grbl学习之旅---serial篇 的相关文章

  • linux 内存查看方法:meminfo\maps\smaps\status 文件解析

    linux 下面查看内存有多种渠道 xff0c 比如通过命令 ps top free 等 xff0c 比如通过 proc系统 xff0c 一般需要比较详细和精确地知道整机内存 某个进程内存的使用情况 xff0c 最好通过 proc 系统 x
  • 数值型模板参数

    本篇文章学习记录 xff1a 数值型模板参数 实现C 43 43 数组类模板 1 模板中的数值型参数 模板参数可以是数值型参数 也就是非类型参数 如下图所示 xff1a 我们可以像上面定义一个局部数组 xff0c 但是却不能这样定义 xff
  • 矩阵分解 (加法篇)

    转自简书 xff1a https www jianshu com p fc89d92bbc24 引言 分解的思想其实并不古老 xff0c 而且大家都熟悉的 xff0c 把复杂的分而治之 xff0c 然后再组合起来 分解有什么好处 xff1f
  • 矩阵分解 (乘法篇)

    引自简书 xff1a https www jianshu com p 0741789ffd06 引言 前面我们在矩阵分解 加法篇 里面分析的加法下的矩阵分解 这里我们来看看乘法下矩阵分解的要点 对角和三角矩阵 首先 xff0c 我们总结下
  • openwrt 编译 问题

    xfeff xfeff 问题一 xff1a OpenWrt can only be built on a case sensitive filesystem 原因是文件解压保存在windows的文件夹 xff0c 应该要解压到linux环境
  • stm32 RTC_WaitForSynchro()死循环

    1 RTC WaitForSynchro 死循环 xff0c 发现是没有执行RTC Configuration 增加函数 xff0c 但不知道对之后的时钟准确性有什么影响 Function Name RTC Configuration De
  • 用docker创建ubuntu VNC桌面

    docker ubuntu vnc desktop from xff1a http wiki ros org docker Tutorials GUI 1 image 地址 https github com fcwu docker ubun
  • Win10如何安装VC6

    这里我们不真的运行setup安装VC6 xff0c 因为在win10安装程序有很大的概率会被卡住 xff0c 就算安装成功也是各种问题 xff0c 包括你设置了兼容性 这里有一个不用安装也能直接运行的办法 xff0c 并且可以支持C 43
  • 在 VMware 虚拟机中安装 open-vm-tools

    什么是 open vm tools xff1f open vm tools 是 VMware Tools 的开源实施 xff0c 由一套虚拟化实用程序组成 xff0c 这些程序可增强虚拟机在 VMware 环境中的功能 xff0c 使管理更
  • FreeRTOS任务管理与控制

    Task c文件 xff1a 全局变量 xff1a static xList pxReadyTasksLists configMAX PRIORITIES static xListxDelayedTaskList1 PRIVILEGED D
  • stm32 烧写下载失败 Error: Flash Download failed - "Cortex-M3"

    问题 xff1a Error Flash Download failed 34 Cortex M3 34 Load 34 10 10 axf 34 Set JLink Project File to 34 G vs keil test 10
  • 查看GIT项目在哪个路径下,查看GIT项目是从GIT的哪个分支上拉下来的命令

    转自 xff1a https www cnblogs com jishan coder p 8554782 html 1 根据路径找到本地存储地址 右键 如图 右键后可以看到有git Bash Here 前提是安装了git 点击 即可进入到
  • C++调用Python(混合编程)函数整理总结

    文章目录 C 43 43 调用python概述相关官方文档相关函数1 初始化python解释器环境2 调用python脚本的静态简单方式3 动态加载python模块并执行函数3 1不带参数和返回值的举例说明3 2带参数和返回值的举例说明 4
  • Markdown写作工具-Typora

    工具 Typora详解 零 文章目录 一 MarkDown 1 MarkDown是什么 Markdown 是一种轻量级标记语言 xff0c 它允许人们使用易读易写的纯文本格式编写文档 Markdown 语言在 2004 由约翰 格鲁伯 xf
  • git pull 强制覆盖本地的代码

    git pull 强制覆盖本地的代码方式 xff0c 下面是正确的方法 xff1a git fetch all 然后 xff0c 你有两个选择 xff1a git reset hard origin master 或者如果你在其他分支上 x
  • SMPL可视化大杀器,你并不需要下载SMPL就能可视化你的3D Pose

    SMPL 是一种3D人体建模方法 xff0c 现在几乎所有的元宇宙人体建模都是基于此类方法 xff0c 包括但不限于元宇宙 xff0c 自动驾驶等领域 它能估计出比较准确的人体3D姿态 xff0c 得益于海量数据训练的人体3D先验 不仅仅是
  • 程序员要多跳巢才能涨工资

    不要一辈子呆死在一家公司 都是打工高薪才是王道 fs xff1a 这 篇文章的本意 xff0c 是告诉大家如何识别公司 而不是鼓励大家无脑跳槽 只有当你在一个公司略有所成的时候 xff0c 你才能有所积累 跳槽更多时候 xff0c 应该看到
  • C++ 用libcurl库进行http通讯网络编程

    转自 http www cnblogs com moodlxs archive 2012 10 15 2724318 html 目录索引 xff1a 一 LibCurl基本编程框架 二 一些基本的函数 三 curl easy setopt函
  • 群晖NAS教程(一) 、利用Docker安装MySQL8并远程访问

    为了更好的浏览体验 欢迎光顾勤奋的凯尔森同学个人博客 群晖NAS教程 一 利用Docker安装MySQL8并远程访问 做为一个NAS发烧友玩家 在追求极致硬件配置的同时 也想在各个方面压榨一下自己的黑群晖 要不就对不起自己投入那么多的毛爷爷
  • 群晖NAS教程(二)、利用Docker安装Ubuntu并远程访问

    为了更好的浏览体验 欢迎光顾勤奋的凯尔森同学个人博客 群晖NAS教程 二 利用Docker安装Ubuntu并远程访问 作者 小景哥哥 一 下载镜像ubuntu upstart 一定要选这个镜像 双击运行 二 设置Ubuntu启动参数和端口号

随机推荐

  • 群晖NAS教程(五)、利用Docker安装Ubuntu-21.04并在Ubuntu上安装Redis进行远程访问

    为了更好的浏览体验 欢迎光顾勤奋的凯尔森同学个人博客 群晖NAS教程 五 利用Docker安装Ubuntu 21 04并在Ubuntu上安装Redis进行远程访问 由于上节我们安装的Ubuntu完全可以当做一个服务器来使用 这个可以完全替代
  • 群晖NAS教程(七)、利用Docker安装elasticsearch并进行远程访问

    为了更好的浏览体验 欢迎光顾勤奋的凯尔森同学个人博客 群晖NAS教程 七 利用Docker安装elasticsearch并进行远程访问 一 下载elasticsearch官方镜像 然后直接下载elasticsearch镜像即可 二 配置el
  • Airsim通过ros发布激光雷达数据+Lego-loam仿真测试(2)

    上篇博客只是简单跑通了流程 xff0c 存在的问题将在这篇进行修正 一 Lego loam里话题订阅 雷达点云话题为 xff1a velodyne points xff0c frame id为velodyne IMU话题为 xff1a Im
  • [控制算法]

    常用控制算法 0 博览众长 0 1 视频 1 DR CAN b站 0 2 文章 1 控制算法整理 0 3 传统 VS 现代控制算法 1 传统 传统控制算法 xff1a PID xff0c 模糊 xff0c 神经网络控制算法 2 现代 现代控
  • 七、输入/输出流--streambuffer类介绍--

    缓冲区类 类模板定义为basic streambuf xff0c 由 lt iostream gt 给出 xff1a 1 stream缓冲区 通常stream不负责实际读写操作 xff0c 而是stream buffer实现streambu
  • 七、输入/输出流--streambuffer类介绍--自定义缓冲区

    基本上没看懂 xff0c 那个大神如果可以的话 xff0c 推荐一点相关资料 xff0c 真的不太明白这个缓冲区的内部原理 3 自定义缓冲区 缓冲区有basic streambuf定义 xff0c 针对字型为char和wchar标准库提供了
  • 串口通信学习(GPS模块)2021.5.10

    GPS串口通信学习实践 2021 5 10 1 串口通信简介1 1 波特率1 2 数据位1 3 停止位1 4 奇偶校验位 2 GPS模块串口通信配置2 1 驱动安装2 2 插入GPS模块2 3 GPS模块串口通信数据简介 3 Java实现G
  • Lambda表达式的使用

    对于jdk1 8其实并不是那么熟悉 xff0c 但是要学习这一点 xff0c 对以后工作有好处 xff0c 接下来开始学习jdk1 8在Android studio的配置以及lambda表达式的使用吧 Lambda表达式 jdk1 8中新增
  • ROS实现无人机目标跟踪/物体跟随/循迹

    无人机自主物体跟随 循迹 1 物体跟踪1 1 实现思路1 2 代码示例 2 自主寻线 本实验采用ROS和OpenCV实现功能 xff0c 实验平台采用Parrot的Bebop2无人机ROS部分的学习可以参考我的专栏 xff1a ROS学习记
  • vs code中项目的基本配置--include路径、运行参数、debug配置

    1 安装C C 43 43 for Visual Studio Code 点击左边扩展栏图标 gt 搜索C C 43 43 gt 安装 gt Reload xff1a 安装完成之后 xff0c 打开你的包含c 43 43 的文件夹 xff0
  • CMakeLists.txt编写规范模板及CMake常用变量

    文章目录 基本语法规则常见CMakeLists txt中指令剖析从VS项目配置过程理解CMakeLists内容CMake中常用变量汇总常用CMakeLists文件模板 基础模板使用OpenCV库CMakeLists文件模板使用PCL库CMa
  • C++ 多线程detach()操作的坑以及传参

    detach 的作用是将子线程和主线程的关联分离 xff0c 也就是说detach 后子线程在后台独立继续运行 xff0c 主线程无法再取得子线程的控制权 xff0c 即使主线程结束 xff0c 子线程未执行也不会结束 当主线程结束时 xf
  • 条件变量中的唤醒丢失问题分析

    本文是在其他作者博文的基础上进行了部分补充 原文 xff1a https zhuanlan zhihu com p 55123862 0 前言 条件变量 xff08 condition variable xff09 和互斥锁 xff08 m
  • 无人车传感器 IMU与GPS数据融合进行定位机制

    前言 上一次的分享里 xff0c 我介绍了GPS的原理 xff08 三角定位 xff09 及特性 xff08 精度 频率 xff09 xff0c 同时也从无人车控制的角度 xff0c 讨论了为什么仅有GPS无法满足无人车的定位要求 为了能让
  • C++类对象的赋值与=运算符重载

    本文主要介绍C 43 43 中的赋值运算符重载函数 xff08 operator 61 xff09 的相关知识 1 概述 1 1 why 首先介绍为什么要对赋值运算符 61 进行重载 某些情况下 xff0c 当我们编写一个类的时候 xff0
  • 微信小程序开发入门(一)

    所有示例的完整代码 xff0c 都可以从 GitHub 的代码仓库下载 一 小程序是什么 xff1f 学习小程序之前 xff0c 先简单说一下 xff0c 它到底是什么 字面上讲 xff0c 小程序就是微信里面的应用程序 xff0c 外部代
  • rtp载荷H264解包过程分析,ffmpeg解码qt展示

    网络抽象层单元 NALU NALU头 NALU 头 由1个byte组成 它的语法如下 43 43 0 1 2 3 4 5 6 7 43 43 43 43 43 43 43 43 43 F NRI Type 43 43 F 1 个比特 for
  • CPU上下文切换、进程上下文、中断上下文

    由于Linux是一个多任务操作系统 xff0c 能够支持远大于CPU数量的任务同时运行 当然 xff0c 这些任务实际上并不是真的在同时运行 xff0c 而是由CPU进行调度 xff0c 将时间分片 xff0c 每个任务占用1个时间片 xf
  • Gstreamer概述

    1 什么是GStreamer GStreamer 是用来构建流媒体应用的开源多媒体框架 framework xff0c 其基本设计思想来自于俄勒冈 Oregon 研究生学院有关视频管道的创意 同时也借鉴了DirectShow的设计思想 其目
  • grbl学习之旅---serial篇

    serial c和serial h文件是实现了通过串行端口发送和接受字节的功能 首先是serial h中定义了基本函数和常量大小 xff1a ifndef RX BUFFER SIZE define RX BUFFER SIZE 128 定