找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 36941|回复: 16
收起左侧

Arduino机械学习笔记04( 开始理解GRBL下位机)

  [复制链接]
ID:112317 发表于 2016-4-9 23:21 | 显示全部楼层 |阅读模式
因为在研究 成品 激光雕刻机 时, 发现 GRBL 代码基本上都改了,主要是 通讯接通 和上位机 界面。

本节笔记开始学习下位机,希望能够理解其控制基本原理。

下载winavr(http://sourceforge.net/projects/winavr/files/),是一个编译器,
打开软件如下,

130550rqxqx0337rqcv7wc.jpg

把GRBL下位机源文件中的 *.h 和 *.c 文件加入,再加入 makefile 文件, 点击 make all

130705am78de1d710n8zdw.jpg

即可生成 hex 文件。

*************************************************

GRBL 自带文档 的介绍,大致结构如下,

131134houzetpw2d2t8wpn.jpg

'protocol'        : Accepts command lines from the serial port and passes them to 'gcode' for execution.  Provides status responses for each command. Also manages run-time commands set by the serial interrupt.

'gcode'           : Recieves gcode from 'protocol', parses it according to the current state
                    of the parser and issues commands via '..._control' modules


'spindle_control' : Commands for controlling the spindle.

'motion_control'  : Accepts motion commands from 'gcode' and passes them to the 'planner'. This module
                    represents the public interface of the planner/stepper duo.

'planner'         : Receives linear motion commands from 'motion_control' and adds them to the plan of
                    prepared motions. It takes care of continuously optimizing the acceleration profile
                    as motions are added.

'stepper'         : Executes the motions by stepping the steppers according to the plan.


-------------------------------------------------------------

*****************************
对 serial.h 的理解

定义串口通讯底层函数
*****************************


#ifndef serial_h
#define serial_h

内容

#endif

标志头文件格式,防止 头文件 被重复引用。

******************************

内容1
#include "nuts_bolts.h"

包含头文件 nuts_bolts.h

nuts_bolts.h - Header file for shared definitions, variables, and functions.
共享 定义、变量和函数

************************

内容2
#ifndef RX_BUFFER_SIZE
  #define RX_BUFFER_SIZE 128
#endif


从字面意思可以看出,定义接收缓存 为 128。

同理,
#ifndef TX_BUFFER_SIZE
  #define TX_BUFFER_SIZE 64
#endif


***********************

内容3
#define SERIAL_NO_DATA 0xff

从字面意思,串口无数据
不知道要用在哪里?

***********************

内容4

#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


不知道怎么用? 也不知道 watermark 水印 是什么意思?

************************

内容5

void serial_init();


字面理解,串口初始化函数。

************************

内容6

void serial_write(uint8_t data);

字面理解,将数据 data 写入串口。

************************

内容7

uint8_t serial_read();

字面理解, 从串口读出数据。

***********************

内容8

void serial_reset_read_buffer();

Reset and empty data in read buffer.
Used by e-stop and reset.

清空读缓存。

***************************************************

winavr编程提示,
uint8_t 在 stdint.h 被定义,typedef unsigned char uint8_t

从 serial.h 上看, serial.h 定义了 串口通讯的底层函数,即使不理解如何实现关系也不大,只需记住函数功能就可以了,如何实现已经被 serial.c 封装了。

-------------------------------------------------------------------------

**************************
serial.c 的理解 上

只需要理解功能,怎样实现可以暂时简单了解
low level functions for sending and recieving bytes via the serial port
***************************


内容1

#include
#include "serial.h"
#include "config.h"
#include "motion_control.h"
#include "protocol.h"


需要的头文件,
1、 定义中断 ISR,

2、serial.h 好理解

3、config.h 该文件 compile time configuration

This file contains compile-time configurations for Grbl's internal system.
For the most part, users will not need to directly modify these, but they are here for specific needs, i.e. performance tuning or adjusting to non-typical machines.

这个文件很特殊,90%的内容是解释,只有10%是代码。
需要专门一篇文章来学习。

4、motion_control.h   

- high level interface for issuing motion commands

会另外开一篇文章学习

5、protocol.h

- the serial protocol master control unit

也会另外开一篇文章学习。

*****************************************


内容2

uint8_t rx_buffer[RX_BUFFER_SIZE];

uint8_t rx_buffer_head = 0;

uint8_t rx_buffer_tail = 0;

uint8_t tx_buffer[TX_BUFFER_SIZE];

uint8_t tx_buffer_head = 0;

volatile uint8_t tx_buffer_tail = 0;



目测是环形队列格式,输出 和 接收 都是 采用环形队列,感觉会有一定难度

080542lcfbbb44dh41qmby.jpg

tx_buffer_tail 前 加了一个 volatile 声明,说明 tx_buffer_tail 可能会被 几个 子程序 共享。

*******************************************

内容3

#ifdef ENABLE_XONXOFF
  volatile uint8_t flow_ctrl = XON_SENT; // Flow control state variable
  
  static uint8_t get_rx_buffer_count()
  {
    if (rx_buffer_head == rx_buffer_tail)
               { return(0); }

    if (rx_buffer_head < rx_buffer_tail)
              { return(rx_buffer_tail-rx_buffer_head); }

    return (RX_BUFFER_SIZE - (rx_buffer_head-rx_buffer_tail));
  }
#endif


Returns the number of bytes in the RX buffer.
This replaces a typical byte counter to prevent  the interrupt and main programs from writing to the counter at the same time.

082235obj0wew0jixxbkqz.jpg

用来判断 缓存的字节数量。

*****************************

内容4

void serial_init()
{

Set baud rate();

enable rx and tx();

enable interrupt on complete reception of a byte();

         
}


利用伪代码来理解整体结构,

然后,细化,
**************************

1、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;


这里需要查看 avr 串口设置资料,涉及到寄存器配置,我不是很熟悉,感觉和找到的代码有些区别,暂时先继续。


2、enable rx and tx();

  UCSR0B |= 1<<rxen0;
  UCSR0B |= 1<<txen0;

通过寄存器赋值实现,RXEN0 和 TXEN0 是寄存器。


3、enable interrupt on complete reception of a byte();

UCSR0B |= 1<<rxcie0;

如果对 avr 串口编程很熟悉的话,上面的代码应该是 格式化的,就是遇到类似情况,copy paste 就可以了。

****************************************

内容5

void serial_write(uint8_t data)
{
  Calculate next head();

  Wait until there is space in the buffer();

  Store data and advance head();
  
  Enable Data Register Empty Interrupt to make sure tx-streaming is running();
}


同样利用伪代码 熟悉结构,然后细化


1、Calculate next head();

  uint8_t next_head = tx_buffer_head + 1;

  if (next_head == TX_BUFFER_SIZE)
           { next_head = 0; }


当next_head == TX_BUFFER_SIZE 时,表面走了一圈了,要再 从 0 开始。

2、 Wait until there is space in the buffer();

  while (next_head == tx_buffer_tail)
{
    if (sys.execute & EXEC_RESET)
                { return; } // Only check for abort to avoid an endless loop.
  }


当next_head == tx_buffer_tail 时,说明环形队列中已经没有空间了,在这里循环等待。循环内部判断 sys状态,避免死循环。


3、Store data and advance head();

  tx_buffer[tx_buffer_head] = data;
  tx_buffer_head = next_head;


好理解


4、 Enable Data Register Empty Interrupt to make sure tx-streaming is running();

  UCSR0B |=  (1 << UDRIE0);

需要看avr 串口方面资料。

***************************************

内容太多了,将 serial.c 理解 分为 两部分。

--------------------------------------------------------------------------



**************************
serial.c 的理解 下

只需要理解功能,怎样实现可以暂时简单了解
low level functions for sending and recieving bytes via the serial port
***************************


内容6

ISR(SERIAL_UDRE)
{
  Temporary tx_buffer_tail();
  
   Send a byte from the buffer();

   Update tail position();
  
  Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer();

}


这是AVR的中断,//USART0接收中断 // Data Register Empty Interrupt handler

其中, #define SERIAL_UDRE USART_UDRE_vect (pin_map.h 中)
http://blog.sina.com.cn/s/blog_55c3ae71010005d8.html
可以参考http://blog.sina.com.cn/s/blog_694edd930100z24y.html

// 数据从发送寄存器完整移动到移位寄存器
// 中断或轮询模式,均是写数据清零
// 每一个字符character发生一次


Temporary tx_buffer_tail()

  uint8_t tail = tx_buffer_tail;

  #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
  {


#ifdef 和 #endif 之间的代码暂时不理解。


   Send a byte from the buffer();

    UDR0 = tx_buffer[tail];

UDR0是数据寄存器,用来放置缓存。


   Update tail position();

    tail++;
    if (tail == TX_BUFFER_SIZE)
              { tail = 0; }
  
    tx_buffer_tail = tail;
  }


好理解


  Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer();


if (tail == tx_buffer_head)
          { UCSR0B &= ~(1 << UDRIE0); }


  没有数据了,设置寄存器(下面为通用的,这里 n 为0,参考 http://www.amobbs.com/archiver/tid-3415797.html),
    //UCSRnB USART控制和状态寄存器B
    // -----------------------------------------------------------------
    // | RXCIEn| TXCIEn| UDRIEn| RXENn | TXENn | UCSZn2| RXB8n | TXB8n |
    // -----------------------------------------------------------------
    // RXCIEn:接收结束中断使能
    // TXCIEn:发送结束中断使能
    // UDRIEn:USART数据寄存器空中使能
    // RXENn:接收使能
    // TXENn:发送使能
    // UCSZn2:字符长度,具体见下面
    // RXB8n:接收数据位8
    // TXB8n:发送数据位8

可以看出,禁止了 USART数据寄存器空中使能。

理解程度不是很详细,因为暂时不需要,不影响 理解 整体。

***********************************************

内容7

uint8_t serial_read()
{
  if (rx_buffer_head == rx_buffer_tail)
{
    return SERIAL_NO_DATA;
  }

  else {
    uint8_t data = rx_buffer[rx_buffer_tail];
    rx_buffer_tail++;

    if (rx_buffer_tail == RX_BUFFER_SIZE)
         { rx_buffer_tail = 0; }

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


从缓存中读出数据。


*******************************

内容8

ISR(SERIAL_RX)
{

  uint8_t data = UDR0;
  uint8_t next_head;

  Pick off runtime command characters directly from the serial stream. These characters are not passed into the buffer, but these set system state flag bits for runtime execution();

Write character to buffer();

Write data to buffer unless it is full();
}


其中,#define SERIAL_RX USART_RX_vect
中断接受函数

1、Pick off runtime command characters directly from the serial stream. These characters are not passed into the buffer, but these set system state flag bits for runtime execution();

  switch (data) {
    case CMD_STATUS_REPORT: sys.execute |= EXEC_STATUS_REPORT; break; // Set as true
    case CMD_CYCLE_START:   sys.execute |= EXEC_CYCLE_START; break; // Set as true
    case CMD_FEED_HOLD:     sys.execute |= EXEC_FEED_HOLD; break; // Set as true
    case CMD_RESET:         mc_reset(); break; // Call motion control reset routine.

   
设置执行系统状态标志。


2、Write character to buffer();

    default: // Write character to buffer   
      next_head = rx_buffer_head + 1;

      if (next_head == RX_BUFFER_SIZE)
            { next_head = 0; }



3、Write data to buffer unless it is full()

      if (next_head != rx_buffer_tail) {
        rx_buffer[rx_buffer_head] = data;
        rx_buffer_head = next_head;   
        
        #ifdef ENABLE_XONXOFF
          if ((get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
            flow_ctrl = SEND_XOFF;
            UCSR0B |=  (1 << UDRIE0); // Force TX
          }
        #endif
        
      }
  }


看来 XONXOFF的很碍事,去掉就简洁很多了。

*****************************************

内容9

void serial_reset_read_buffer()
{
  rx_buffer_tail = rx_buffer_head;

  #ifdef ENABLE_XONXOFF
    flow_ctrl = XON_SENT;
  #endif
}


复位。


************************************

总体上,需要理解环形队列(这里没有分析,head 是用来写入数据的index,tail 是用来导出数据的index),就能理解50% 了,另外 对AVR 串口寄存器如果熟悉的话,就又能理解30%了。

剩下的20% 就是 XONXOFF 还没理解。


摘录AVR 串口寄存器

void init_uart(void)
{
    //UDRn USART I/O数据寄存器, 不可用读修改写命令操作, 否则会改变FIFO状态

    //UCSRnA USART控制和状态寄存器A
    // -----------------------------------------------------------------
    // |RXCn |TXCn | UDREn |FEn|DORn |UPEn |U2Xn | MPCMn |
    // -----------------------------------------------------------------
    // RXCn:USART接收结束标志
    // TXCn:USART发送结束标志,写1可清除
    // UDREn:USART数据寄存器为空标志,只有该标志为1才数据才可写入UDR0
    // FEn:帧错误,未正确收到停止位
    // DORn:数据过速
    // UPEn:奇偶效验错误
    // U2Xn:倍速发送,仅对异步操作有影响
    // MPCMn:多处理器通讯模式

    //UCSRnB USART控制和状态寄存器B
    // -----------------------------------------------------------------
    // | RXCIEn| TXCIEn| UDRIEn| RXENn | TXENn | UCSZn2| RXB8n | TXB8n |
    // -----------------------------------------------------------------
    // RXCIEn:接收结束中断使能
    // TXCIEn:发送结束中断使能
    // UDRIEn:USART数据寄存器空中使能
    // RXENn:接收使能
    // TXENn:发送使能
    // UCSZn2:字符长度,具体见下面
    // RXB8n:接收数据位8
    // TXB8n:发送数据位8

    //UCSRxC USART控制和状态寄存器C
    // -----------------------------------------------------------------
    // |   -   | UMSELn| UPMn1 | UPMn0 | USBSn | UCSZn1| UCXZn0| UCPOLn|
    // -----------------------------------------------------------------
    // UMSELn:模式选择,0为异步操作,1为同步操作
    // UPMn1,UPMn0:奇偶效验模式,00禁止,01保留,10偶效验,11奇校验
    // USBSn:停止位选择,0为1位停止位,1为2位停止位
    // UCSZn2,UCSZn0:字符长度,000为5位, 001为 6位,010为7位, 011为8位
    //                         100为保留,101为保留,110为保留,111为9位
    // UCPOLn:时钟极性,(异步模式应清零)
    //                              UCPOL0   发送数据位置   接收数据位置
    //                              0      XCK0上升沿    XCK0下降沿
    //                              1      XCK0下降沿    XCK0上升沿

    //UBRRnL和UBRRnH USART波特率寄存器, UBRRnH15:12为保留位:
    // -----------------------------------------------------------------
    // |   -   |   -   |   -   |   -   | BIT11 | BIT10 | BIT09 | BIT08 |
    // -----------------------------------------------------------------
    // -----------------------------------------------------------------
    // | BIT07 | BIT06 | BIT05 | BIT04 | BIT03 | BIT02 | BIT01 | BIT00 |
    // -----------------------------------------------------------------

}   



************************************
serial 中 环形队列

简化后
************************************


去除了 如果 队列 满了 或者 空了 等判断;
也忽略了 读入的是 某种命令;
也忽略 XONXOFF

哇~~~~,这个世界清净了,现在大家知道,

110141lnsu2neqscyvgtqe.gif

110205ly96rrnq2713gzbq.gif

TX 队列
*****************************

110258fno1sk71shzcs0bh.gif

110306e253b225foi5oogb.gif

RX 队列

--------------------------------------------------------------


******************************
config.h  上

几乎全是注释的文件
******************************


This file contains compile-time configurations for Grbl's internal system.

For the most part,users will not need to directly modify these, but they are here for specific needs, i.e. performance tuning or adjusting to non-typical machines.

说明了这个文件的用途。

1、 Default settings. Used when resetting EEPROM. Change to desired name in defaults.h

#define DEFAULTS_GENERIC

保存在 EEPROM 中的参数,默认值。

2、Serial baud rate

#define BAUD_RATE 9600

3、Default pin mappings. Grbl officially supports the Arduino Uno only. Other processor typesmay exist from user-supplied templates or directly user-defined in pin_map.h

#define PIN_MAP_ARDUINO_UNO

默认端口配置。

4、Define runtime command special characters. These characters are 'picked-off' directly from the serial read data stream and are not passed to the grbl line execution parser.
Select characters that do not and must not exist in the streamed g-code program. ASCII control characters may be used, if they are available per user setup.
Also, extended ASCII codes (>127), which are never in  g-code programs, maybe selected for interface programs.
NOTE: If changed, manually update help message in report.c.

#define CMD_STATUS_REPORT '?'
#define CMD_FEED_HOLD '!'
#define CMD_CYCLE_START '~'
#define CMD_RESET 0x18 // ctrl-x


命令符,区别 G CODE,用来控制程序。

5、The temporal resolution of the acceleration management subsystem. Higher number give smoother acceleration but may impact performance.
NOTE: Increasing this parameter will help any resolution related issues, especially with machines requiring very high accelerations and/or very fast feedrates.
In general, this will reduce the error between how the planner plans the motions and how the stepper program actually performs them.
However, at some point, the resolution can be high enough, where the errors related to numerical round-off can be great enough to cause problems and/or it's too fast for the Arduino.
The correct value for this parameter is machine dependent, so it's advised to set this only as high as needed.
Approximate successful values can range from 30L to 100L or more.

#define ACCELERATION_TICKS_PER_SECOND 50L

工作原理还不清楚,作用注释已经解释很清楚了。

6、Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end of the buffer and all stops. This should not be much greater than zero and should only be changed if unwanted behavior is observed on a user's machine when running at very slow speeds.

#define MINIMUM_PLANNER_SPEED 0.0 // (mm/min)

不清楚工作机理,看起来很厉害。

7、Minimum stepper rate. Sets the absolute minimum stepper rate in the stepper program and never runs slower than this value, except when sleeping. This parameter overrides the minimum planner speed.
This is primarily used to guarantee that the end of a movement is always reached and not stop to never reach its target. This parameter should always be greater than zero.

#define MINIMUM_STEPS_PER_MINUTE 800 // (steps/min) - Integer value only

8、Time delay increments performed during a dwell. The default value is set at 50ms, which provides a maximum time delay of roughly 55 minutes, more than enough for most any application.
Increasing this delay will increase the maximum dwell time linearly, but also reduces the responsiveness of run-time command executions, like status reports, since these are performed between each dwell time step.
Also, keep in mind that the Arduino delay timer is not very accurate for long delays.

define DWELL_TIME_STEP 50 // Integer (1-255) (milliseconds)

9、If homing is enabled, homing init lock sets Grbl into an alarm state upon power up.
This forces the user to perform the homing cycle (or override the locks) before doing anything else. This is mainly a safety feature to remind the user to home, since position is unknown to Grbl.

#define HOMING_INIT_LOCK // Comment to disable

不理解,用到时再来理解。

10、The homing cycle seek and feed rates will adjust so all axes independently move at the homing seek and feed rates regardless of how many axes are in motion simultaneously.
If disabled, rates are point-to-point rates, as done in normal operation. For example in an XY diagonal motion, the diagonal motion moves at the intended rate, but the individual axes move at 70% speed.
This option just moves them all at 100% speed.

#define HOMING_RATE_ADJUST // Comment to disable

11、Define the homing cycle search patterns with bitmasks. The homing cycle first performs a search to engage the limit switches.
HOMING_SEARCH_CYCLE_x are executed in order starting with suffix 0 and searches the enabled axes in the bitmask.
This allows for users with non-standard cartesian machines, such as a lathe (x then z), to configure the homing cycle behavior to their needs.
Search cycle 0 is required, but cycles 1 and 2 are both optional and may be commented to disable.
After the search cycle, homing then performs a series of locating about the limit switches to hone in on machine zero, followed by a pull-off maneuver. HOMING_LOCATE_CYCLE governs these final moves, and this mask must contain all axes in the search.
NOTE: Later versions may have this installed in settings.

#define HOMING_SEARCH_CYCLE_0 (1<<z_axis) [="" color][="" b]="" first="" move="" z="" to="" clear="" workspace.
#define HOMING_SEARCH_CYCLE_1 ((1<<x_axis)|(1<<y_axis)) [="" color][="" b]="" then="" move="" x,y="" at="" the="" same="" time.
// #define HOMING_SEARCH_CYCLE_2                         // Uncomment and add axes mask to enable
#define HOMING_LOCATE_CYCLE   ((1<<x_axis)|(1<<y_axis)|(1<<z_axis))[ color][="" b]="" must="" contain="" all="" search="" axes

12、Number of homing cycles performed after when the machine initially jogs to limit switches.
This help in preventing overshoot and should improve repeatability. This value should be one or greater.

#define N_HOMING_LOCATE_CYCLE 2 // Integer (1-128)

13、Number of blocks Grbl executes upon startup.
These blocks are stored in EEPROM, where the size and addresses are defined in settings.h. With the current settings, up to 5 startup blocks may be stored and executed in order.
These startup blocks would typically be used to set the g-code parser state depending on user preferences.

#define N_STARTUP_LINE 2 // Integer (1-5)


******************************************

感觉设置很复杂,大多数都不理解,先混个脸熟。


**********************
config.h  下

advanced users only
**********************


The number of linear motions in the planner buffer to be planned at any give time.
The vast majority of RAM that Grbl uses is based on this buffer size.
Only increase if there is extra available RAM, like when re-compiling for a Teensy or Sanguino. Or decrease if the Arduino begins to crash due to the lack of available RAM or if the CPU is having trouble keeping up with planning new incoming motions as they are executed.

// #define BLOCK_BUFFER_SIZE 18  // Uncomment to override default in planner.h.

默认 是注释掉的

Line buffer size from the serial input stream to be executed.
Also, governs the size of each of the startup blocks, as they are each stored as a string of this size.
Make sure to account for the available EEPROM at the defined memory address in settings.h and for the number of desired startup blocks.
NOTE: 70 characters is not a problem except for extreme cases, but the line buffer size can be too small and g-code blocks can get truncated.
Officially, the g-code standards support up to 256 characters. In future versions, this default will be increased, when we know how much extra memory space we can re-invest into this.

// #define LINE_BUFFER_SIZE 70  // Uncomment to override default in protocol.h


Serial send and receive buffer size.
The receive buffer is often used as another streaming buffer to store incoming blocks to be processed by Grbl when its ready. Most streaming interfaces will character count and track each block send to each block response.
So, increase the receive buffer if a deeper receive buffer is needed for streaming and avaiable memory allows. The send buffer primarily handles messages in Grbl.
Only increase if large messages are sent and Grbl begins to stall, waiting to send the rest of the message.

// #define RX_BUFFER_SIZE 128 // Uncomment to override defaults in serial.h
// #define TX_BUFFER_SIZE 64

这个已经在 serial.c 中理解过了。

Toggles XON/XOFF software flow control for serial communications.
Not officially supported due to problems involving the Atmega8U2 USB-to-serial chips on current Arduinos.
The firmware on these chips do not support XON/XOFF flow control characters and the intermediate buffer in the chips cause latency and overflow problems with standard terminal programs.
However, using specifically-programmed UI's to manage this latency problem has been confirmed to work.
As well as, older FTDI FT232RL-based Arduinos(Duemilanove) are known to work with standard terminal programs since their firmware correctly manage these XON/XOFF characters.

// #define ENABLE_XONXOFF // Default disabled. Uncomment to enable.


Creates a delay between the direction pin setting and corresponding step pulse by creating another interrupt (Timer2 compare) to manage it.
The main Grbl interrupt (Timer1 compare)  sets the direction pins, and does not immediately set the stepper pins, as it would in normal operation.
The Timer2 compare fires next to set the stepper pins after the step pulse delay time, and Timer2 overflow will complete the step pulse, except now delayed by the step pulse time plus the step pulse delay. (Thanks langwadt for the idea!)
This is an experimental feature that should only be used if your setup requires a longer delay between direction and step pin settings (some opto coupler based drivers), as it may adversely effect Grbl's high-end performance (>10kHz).
However, we suggest to first try our direction delay hack/solution posted in the Wiki involving inverting the stepper pin mask.
NOTE: Uncomment to enable. The recommended delay must be > 3us and the total step pulse time, which includes the Grbl settings pulse microseconds, must not exceed 127us.
Reported successful values for certain setups have ranged from 10 to 20us.

// #define STEP_PULSE_DELAY 10 // Step pulse delay in microseconds. Default disabled.


******************************

感觉 和 电机 加速度算法 之类的有关系。还没有找到适合的资料 来 帮助理解。

------------------------------------------------------------------------------

**********************
defaults.h 上

默认参数设置
**********************

112458nblb6spu3ppbkink.jpg

通讯一建立,这些参数就会显示出来

幸好有帮助文档,https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.8

The defaults.h file serves as a central default settings file for different machine  types, from DIY CNC mills to CNC conversions of off-the-shelf machines.
The settings  here are supplied by users, so your results may vary.
However, this should give you  a good starting point as you get to know your machine and tweak the settings for your our nefarious needs.

#ifdef DEFAULTS_GENERIC

参数设置

#endif

只理解默认的,

参数设置0、1、2
#define DEFAULT_X_STEPS_PER_MM 250.0


Grbl needs to know how far each step will take the tool in reality. To calculate steps/mm for an axis of your machine you need to know:

    The mm per revolution of the lead screw
    The full steps per revolution of your steppers (typically 200)
    The microsteps per step of your controller (typically 1, 2, 4, 8, or 16). Tip: Using high microstep values (e.g 16) can reduce your stepper motor torque, so use the lowest that gives you the desired axes resolution and comfortable running properties.

The steps/mm can then be calculated like this: steps_per_mm = (steps_per_revolution*microsteps)/mm_per_rev

Compute this value for every axis and write these settings to Grbl.

计算方法实例,
     Motor 200 steps/rev
    Driver 2 microsteps/step
    Screw 1.25 rev/mm

The resolution is: 2 microsteps/step * 200 step/rev * 1/1.25 rev/mm = 320 microstep/mm.

同理,
#define DEFAULT_Y_STEPS_PER_MM 250.0
  #define DEFAULT_Z_STEPS_PER_MM 250.0


应该丝杆尺寸不一致,这个数字基本更改。

*************************************

参数设置3
#define DEFAULT_STEP_PULSE_MICROSECONDS 10


Step pulse, microseconds

Stepper drivers are rated for a certain minimum step pulse length. Check the data sheet or just try some numbers. You want as short pulses as the stepper drivers can reliably recognize. If the pulses are too long you might run into trouble running the system at high feed rates. Generally something between 5 and 50 microseconds works fine.

*************************************

参数设置4、5

  #define DEFAULT_RAPID_FEEDRATE 500.0 // mm/min
  #define DEFAULT_FEEDRATE 250.0


Default feed and seek rates, mm/min

This setting sets the default seek(G0) and feed rates(G1,G2,G3) after Grbl powers on and initializes. The seek rate (aka rapids) is used for moving from point A to point B as quickly as possible, usually for traversing into position. The seek rate should be set at the maximum speed your machine can go in any axes movement. The default feed rate usually does not enter into the picture as feed rates will generally be specified in the g-code program, but if not, this default feed rate will be used.

*************************************

参数设置6

#define DEFAULT_JUNCTION_DEVIATION 0.05 // mm

  
Step port invert mask, int:binary

Some cnc-stepper controllers needs its high-low inputs inverted for both direction and steps.
Signal lines are normally held high or low to signal direction or held high and goes low for a couple of microseconds to signal a step event. To achieve this, Grbl can invert the output bits to accomodate particular needs.
The invert mask value is a byte that is xored with the step and direction data before it is sent down the stepping port. That way you can use this both to invert step pulses or to invert one or more of the directions of the axes.
The bits in this byte corresponds to the pins assigned to stepping in config.h. Note that bits 0 and 1 are not used for inversion. Per default bits are assigned like this:

ARDUINO 代码复制打印

  • #define X_STEP_BIT 2
  • #define Y_STEP_BIT 3
  • #define Z_STEP_BIT 4
  • #define X_DIRECTION_BIT 5
  • #define Y_DIRECTION_BIT 6
  • #define Z_DIRECTION_BIT 7



If you wanted to invert the X and Y direction in this setup you would calculate a value by bitshifting like this (in your favorite calculating environment):

> (1<<x_direction_bit)|(1<<y_direction_bit)

Which is equal to 96, so issuing this command would invert them:

$6=96

Now when you view the current settings, you should now see this in your invert mask line with the binary representation of the number (bits 5 and 6 should now show a 1 to indicate inversion.)

$6=96 (step port invert mask. int:1100000)

*****************************************


参数设置7

  #define DEFAULT_STEPPER_IDLE_LOCK_TIME 25 // msec (0-255)


Step idle delay, msec

Every time your steppers complete a motion and come to a stop, Grbl will disable the steppers by default.
The stepper idle lock time is the time length Grbl will keep the steppers locked before disabling.
Depending on the system, you can set this to zero and disable it. On others, you may need 25-50 milliseconds to make sure your axes come to complete stop before disabling. (My machine tends to drift just slightly if I don't have this enabled.) OR, you can always keep your axes enabled at all times by setting this value to the maximum 255 milliseconds. Again, just to repeat, you can keep all axes always enabled by setting $7=255.

*******************************************

参数设置8

  #define DEFAULT_ACCELERATION (10.0*60*60) // 10 mm/min^2


Acceleration, mm/sec^2

This is the acceleration in mm/second/second.
You don’t have to understand what that means, suffice it to say that a lower value gives smooooother acceleration while a higher value yields tighter moves and reach the desired feedrates much quicker.
In technical terms, this is the point to point acceleration of your machine, independent of axes. Set this acceleration value as high as your most limiting axes can let you without losing ANY steps.
Usually you'd like to give yourself a little buffer, because if you lose steps, Grbl has no idea this has happened (steppers are open-loop control) and will keep going.

**********************************************

参数设置9

  #define DEFAULT_JUNCTION_DEVIATION 0.05 // mm

  Junction deviation, mm

Cornering junction deviation is used by the acceleration manager to determine how fast it can move through a path.
The math is a bit complicated but in general, higher values gives generally faster, possibly jerkier motion. Lower values makes the acceleration manager more careful and will lead to careful and slower cornering.
So if you run into problems where your machine tries to take a corner too fast, decrease this value to make it slow down.
If you want your machine to move faster through junctions, increase this value to speed it up.
For technical people, hit this link to read about Grbl's cornering algorithm, which accounts for both velocity and junction angle with a very simple, efficient, and robust method.

*********************************************

***********************
defaults.h 下

************************


参数设置10

  #define DEFAULT_MM_PER_ARC_SEGMENT 0.1


Arc, mm/segment

Grbl renders circles and arcs by subdividing them into teeny tiny lines. You will probably never need to adjust this value – but if you find that your circles are too crude (really? one tenth of a millimeter is not precise enough for you? Are you in nanotech?) you may adjust this. Lower values gives higher precision but may lead to performance issues.

***************************

参数设置11

  #define DEFAULT_N_ARC_CORRECTION 25


N-arc correction, int

This is an advanced setting that shouldn't be changed unless there are circumstances that you need to. To make G02/03 arcs possible in Grbl, Grbl approximates the location of the next arc segment by a small angle approximation. N-arc correction is the number of approximate arc segments performed before Grbl computes an exact arc segment to correct for the approximation error drift. Computing these exact locations are computationally expensive, but there are some extreme cases where the small angle approximations can introduce enough error to be noticeable, like very very small arcs with a large arc segment length. Change this setting only if find strange problems with arcs, but it's not recommended to go below 3 because this may lead to buffer starvation, where the axes slow down and hiccup. But, results can vary.

****************************

参数设置12

  #define DEFAULT_DECIMAL_PLACES 3


N-decimal, int

Set how many decimal places all of the floating point values Grbl reports. Not much more complicated than that.

****************************

参数设置13

#define DEFAULT_REPORT_INCHES 0 // false


Report inches, bool

Grbl v0.8 has a real-time positioning reporting feature to provide a user feedback on where the machine is exactly at that time. By default it is set to report in mm, but by sending a $13=1 command, you send this boolean flag to true and the status reporting feature will now report in inches. $13=0 to set back to mm.

名字 是 report inches,真 时,单位 inch,假时 单位 mm。

******************************

参数设置14

  #define DEFAULT_AUTO_START 1 // true


Auto start, bool

In a more professional CNC environment, pros start a job by loading up their program and then pressing the 'cycle start' button on their machine. It begins the job. Grbl does the same thing, but not by default. As a learning tool, we 'auto-cycle start' any g-code command that user sends to Grbl, for just jogging the machine to see if move in the direction they think it should go, or just seeing with what their machine can do. This makes it easier to load up your machine with Grbl and pick up on how it works, rather than having to diligently hit 'cycle start' anytime you want to move any your axes. Once you learn your machine and get a good handle on g-code, you can disable the 'auto cycle-start' feature by sending Grbl $14=0 command. (May need a soft-reset or power cycle to load the change.)

Another way of saying that might be:

If $14=0 then some gcode commands like Xn, Yn etc. won't happen when you enter them at a serial terminal. If $14=1 then the gcode motion commands will happen.

Apparently big cnc's won't execute gcode until the operator presses a 'cycle start' button. The gcode commands were received but queued and awaiting the 'cycle start' button. Makes sense yes? When $14=0 then that's how grbl acts too. You need the button! (in which case attach the feed-hold button too!).

When $14=1 then the grbl software automatically presses a software version of the 'cycle start' for you each time you 'enter' a gcode command line via a serial terminal. This is done to make it more convenient for you to enter commands and see something happen without having to push the button.

081744jb6baabgdj9jjj6x.jpg
081002d9bokbc465cga25z.jpg

*********************************

参数设置15

  #define DEFAULT_INVERT_ST_ENABLE 0 // false


Invert step enable, bool

By default, the stepper enable pin is high to disable and low to enable. If your setup needs the opposite, just invert the stepper enable pin by typing $15=1. Disable with $15=0. (May need a power cycle to load the change.)

**********************************

参数设置16

  #define DEFAULT_HARD_LIMIT_ENABLE 0  // false


Hard limits, bool

Hard limit switches are a safety feature to help prevent your machine from traveling way too far off the ends of travel and crashing or breaking something expensive. Basically you wire up some switches (mechanical or optical) near the end of travel of each axes, or where ever you feel like there might be trouble if your program moves too far to where it shouldn't. When the switch triggers, it will immediately stop all motion, shutdown the coolant and spindle (if connected), and go into alarm mode, which forces you to check your machine and reset everything.

To use hard limits with Grbl, the limit pins are held high with an internal pull-up resistor, so all you have to do is wire in a normally-open switch with the pin and ground and enable hard limits with $16=1. That's it. (Disable with $16=0) If you want a limit for both ends of travel of one axes, just wire in two switches in parallel with the pin and ground, so if either one of them trips, it triggers the hard limit.

Just know, that a hard limit event is considered to be critical event, where steppers immediately stop and will have likely lost steps. Grbl doesn't have any feedback on position, so it can't guarantee it has any idea where it is. So, if a hard limit is triggered, Grbl will go into an infinite loop ALARM mode, giving you a chance to check your machine and forcing you to reset Grbl. Remember it's a purely a safety feature.

If you have issues with the hard limit switch constantly triggering after you reset, a soft-reset will reset Grbl into an alarm state, where you can access the settings and Grbl commands, but all g-codes and startup blocks will be locked out. So you can disable the hard limits setting and then $X unlock the alarm. Or, you can wire in a normally-closed switch in series with ground to all the limit switches to disconnect the switches temporarily so you can have Grbl move your axes off the switches.

******************************

参数设置17

  #define DEFAULT_HOMING_ENABLE 0  // false


Homing cycle, bool

Ahh, homing. Something that has been sorely needed in Grbl for a long time. It's now fully supported in v0.8. For those just initiated into CNC, the homing cycle is used to accurately and precisely locate position zero on a machine (aka machine zero) everytime you startup your Grbl between sessions. In other words, you know exactly where you are at any given time, every time. Say you start machining something or are about to start the next step in a job and the power goes out, you re-start Grbl and Grbl has no idea where it is. You're left with the task of figuring out where you are. If you have homing, you always have the machine zero reference point to locate from, so all you have to do is run the homing cycle and resume where you left off.

To set up the homing cycle for Grbl, you need to have limit switches in a fixed position that won't get bumped or moved, or else your reference point gets messed up. Usually they are setup in the farthest point in +x, +y, +z of each axes. Wire your limit switches in with the limit pins and ground, just like with the hard limits, and enable homing. If you're curious, you can use your limit switches for both hard limits AND homing. They play nice with each other.

By default, Grbl's homing cycle moves the Z-axis positive first to clear the workspace and then moves both the X and Y-axes at the same time in the positive direction. To set up how your homing cycle behaves, there are more Grbl settings down the page describing what they do (and compile-time options as well.)

Also, one more thing to note, when homing is enabled. Grbl will lock out all g-code commands until you perform a homing cycle. Meaning no axes motions, unless the lock is disabled ($X) but more on that later. Most, if not all CNC controllers, do something similar, as it mostly a safety feature to help users from making positioning mistake, which is very easy to do and be saddening when a mistake ruins a part. If you find this annoying or find any weird bugs, please let us know and we'll try to work on it so everyone is happy.

NOTE: Check out config.h for more homing options for advanced users. You can disable the homing lockout at startup, configure which axes move first during a homing cycle and in what order, and more.

***********************

参数设置18

#define DEFAULT_HOMING_DIR_MASK 0 // move positive dir


Homing dir invert mask, int:binary

By default, Grbl assumes your homing limit switches are in the positive direction, first moving the z-axis positive, then the x-y axes positive before trying to precisely locate machine zero by going back and forth slowly around the switch. If your machine has a limit switch in the negative direction, the homing direction mask can invert the axes direction. It works just like the invert stepper mask, where all you have to do set the axis direction pins to 1 that you want to invert and that axes will now search for the limit pin in the negative direction.

***************************

参数设置19

  #define DEFAULT_HOMING_FEEDRATE 25.0 // mm/min


Homing feed, mm/min

The homing cycle first searches for the limit switches at a higher seek rate, and after it finds them, it moves at a slower feed rate to hone into the precise location of machine zero. Homing feed rate is that slower feed rate. Set this to whatever rate value that provides repeatable and precise machine zero locating.

**************************

参数设置20

  #define DEFAULT_HOMING_RAPID_FEEDRATE 250.0 // mm/min


Homing seek, mm/min

Homing seek rate is the homing cycle search rate, or the rate at which it first tries to find the limit switches. Adjust to whatever rate gets to the limit switches in a short enough time without crashing into your limit switches if they come in too fast. This seek rate behaves a little differently than the main stepper driver. Instead of the rate from point to point, it just moves all of the axes at the same individual rate, regardless of how many axes are moving at the same time. So, the XY seek move will seem to move about 41% faster than if you would move it with a G1 command. (You can disable this in config.h if it bothers you. It's there to speed up the homing cycle.)

*******************************

参数设置21

  #define DEFAULT_HOMING_DEBOUNCE_DELAY 100 // msec (0-65k)


Homing debounce, ms

Whenever a switch triggers, some of them can have electrical/mechanical noise that actually 'bounce' the signal high and low for a few milliseconds before settling in. To solve this, you need to debounce the signal, either by hardware with some kind of signal conditioner or by software with a short delay to let the signal finish bouncing. Grbl performs a short delay only homing when locating machine zero. Set this delay value to whatever your switch needs to get repeatable homing. In most cases, 5-25 milliseconds is fine.

类似单片机中 常见的 键盘 消抖

*******************************

参数设置22

  #define DEFAULT_HOMING_PULLOFF 1.0 // mm


Homing pull-off, mm

To play nice with the hard limits feature, where homing can share the same limit switches, the homing cycle will move off the all of the limit switches by this pull-off travel after it completes. In other words, it helps to prevent accidental triggering of the hard limit after a homing cycle.

The homing seek rate setting controls how quickly the pull-off maneuver moves, like a G1 command.


******************************************

总共 22 个参数设置,对我这样第一次接触,内容量还是很大的。

------------------------------------------------------------------------------



*******************************
protocol.h

the serial protocol master control unit
*******************************


113933nxa8aaxn5933dtc3.jpg

serial 收到 gcode 后,要通过 protocal 来处理,

****************************

内容1

#include


睡眠模式,难道要省电?

****************************

内容2

#ifndef LINE_BUFFER_SIZE
  #define LINE_BUFFER_SIZE 70
#endif


Line buffer size from the serial input stream to be executed.
NOTE: Not a problem except for extreme cases, but the line buffer size can be too small and g-code blocks can get truncated. Officially, the g-code standards support up to 256 characters. In future versions, this will be increased, when we know how much extra memory space we can invest into here or we re-write the g-code parser not to have his buffer.

在 config.h 中 也有定义,不过默认 是 // 的

*****************************

内容3

void protocol_init();


Initialize the serial protocol
放在 这里 的函数 都可以 被 其它文件调用。

******************************

内容4

void protocol_process();


Read command lines from the serial port and execute them as they come in. Blocks until the serial buffer is emptied.

********************************

内容5

uint8_t protocol_execute_line(char *line);


Executes one line of input according to protocol

*******************************

内容6

void protocol_execute_runtime();


Checks and executes a runtime command at various stop points in main program

*********************************

内容7

void protocol_execute_startup();


Execute the startup script lines stored in EEPROM upon initialization

*******************************

函数数量不多,功能明确,下一步理解 .c 文件,理解如何实现。

------------------------------------------------------

************************
protocol.c  上

the serial protocol master control unit
************************


内容1 包含的头文件

#include

#include

#include "protocol.h"

#include "gcode.h"

#include "serial.h"

#include "print.h"

#include "settings.h"

#include "config.h"

#include "nuts_bolts.h"

#include "stepper.h"

#include "report.h"

#include "motion_control.h"


理解所需的头文件

和  是标准文件,输入输出 和 中断。

已经理解过的头文件:protocol.h 是 声明文件,serial.h 声明通讯函数,config.h —— compile time configuration

还没有接触的头文件
gcode.h —— rs274/ngc parser

print.h —— Functions for formatting output strings

settings.h —— eeprom configuration handling

nuts_bolts.h —— Header file for shared definitions, variables, and functions

stepper.h —— stepper motor driver: executes motion plans of planner.c using the stepper motors

report.h —— reporting and messaging methods

motion_control.h —— high level interface for issuing motion commands

这些头文件 被用到时再理解。

*****************************************

内容2 定义静态变量 数组

static char line[LINE_BUFFER_SIZE];

static uint8_t char_counter;

static uint8_t iscomment;


Line to be executed. Zero-terminated.  line[LINE_BUFFER_SIZE],复习一下,
在 protocol.h 中
#ifndef LINE_BUFFER_SIZE
  #define LINE_BUFFER_SIZE 70
#endif

Last character counter in line variable. char_counter

Comment/block delete flag for processor to ignore comment characters. iscomment

*****************************************

内容4 静态 函数

static void protocol_reset_line_buffer()
{
  char_counter = 0; // Reset line input
  iscomment = false;
}


为什么 静态? http://bbs.csdn.net/topics/350238100 7楼
定义静态函数的好处:
<1> 其他文件中可以定义相同名字的函数,不会发生冲突
<2> 静态函数不能被其他文件所用。

同样也可以理解 为什么 char_counter 静态变量

由于static变量的以上特性,可实现一些特定功能。
1. 统计次数功能
声明函数的一个局部变量,并设为static类型,作为一个计数器,这样函数每次被调用的时候就可以进行计数。这是统计函数被调用次数的最好的办法,因为这个变量是和函数息息相关的,而函数可能在多个不同的地方被调用,所以从调用者的角度来统计比较困难。

************************************************

内容 4 初始化 protocol

void protocol_init()
{
  复位();

  欢迎信息();

  设置端口();
}


利用伪代码理解结构,然后细化,

1、复位();

protocol_reset_line_buffer();

直接调用刚才定义的静态函数就可以了。


2、欢迎信息();

report_init_message();

就是输出欢迎的信息,声明在 report.h 中。非常简单。


3、设置端口();

PINOUT_DDR &= ~(PINOUT_MASK);

PINOUT_PORT |= PINOUT_MASK;

PINOUT_PCMSK |= PINOUT_MASK;

PCICR |= (1 << PINOUT_INT);


首先 需要看一下定义,

  #define PINOUT_DDR       DDRC
  #define PINOUT_PORT      PORTC
  #define PINOUT_PCMSK     PCMSK1
  #define PINOUT_MASK ((1<<pin_reset)|(1<<pin_feed_hold)|(1<<pin_cycle_start))
  #define PINOUT_INT       PCIE1

  #define PIN_RESET        0
  #define PIN_FEED_HOLD    1
  #define PIN_CYCLE_START  2

上面的定义 位于  pin_map.h 中,而被 nuts_bolts.h 引用,再被该文件引用

这里可能需要花些精力来确认 哪个是哪个端口?

首先 看具体定义端口的

  #define PIN_RESET        0  // Uno Analog Pin 0
  #define PIN_FEED_HOLD    1  // Uno Analog Pin 1
  #define PIN_CYCLE_START  2  // Uno Analog Pin 2

然后,看一张总图,

065153yqqy195gig6u5huh.jpg

然后,具体 到 上面 三个端口

065316jxcm8h5lzh9wurlh.jpg

然后,看

  #define PINOUT_MASK ((1<<pin_reset)|(1<<pin_feed_hold)|(1<<pin_cycle_start))

意思 就是 arduino上 A0 A1 A2 端口取1。(即 AVR PC0 PC1 PC2 端口 取1)

再来理解AVR中DDRC、PORTC什么意思?
http://zhidao.baidu.com/link?url ... SrQj_v1w1OLQYAnoMJq

AVR单片机的IO是3态门DDRC是C口的方向寄存器,PORTC是C口的数据寄存器,
DDRC为0时,C口为输入,IO的高低从PORTC可以读出
DDRC为1时,c为输出,输出高低有PORTC控制。

然后,再理解中断
PCMSK1 为引脚变化屏蔽寄存器 即决定某一I/O引脚变化中断是否使能。

再回到
PINOUT_DDR &= ~(PINOUT_MASK);

Set as input pins. 对 PINOUT_MASK 取反,就是 arduino上 A0 A1 A2 端口取0。

  PINOUT_PORT |= PINOUT_MASK;

Enable internal pull-up resistors. Normal high operation.

  PINOUT_PCMSK |= PINOUT_MASK;

Enable specific pins of the Pin Change Interrupt. A0 A1 A2 端口可以中断。

  PCICR |= (1 << PINOUT_INT);

Enable Pin Change Interrupt

这里需要 理解 AVR 中断机制。暂时理解为 设置哪些引脚可以中断。

***********************************************

可能还需要 找些 AVR 中断方面的资料。


***************************
protocol.c 中

****************************


内容5

Executes user startup script, if stored.


void protocol_execute_startup()
{
  uint8_t n;
  for (n=0; n < N_STARTUP_LINE; n++) {
     内容5A();
  }  
}


复习,
N_STARTUP_LINE 定义 在 config.h 中, #define N_STARTUP_LINE 2
These startup blocks would typically be used to set the g-code parser state depending on user preferences.
blocks :程序块?

内容5A();


if (!(settings_read_startup_line(n, line))) {
      report_status_message(STATUS_SETTING_READ_FAIL);
    } else {
      if (line[0] != 0) {
        printString(line); // Echo startup line to indicate execution.
        report_status_message(gc_execute_line(line));
      }
    }


其中,settings_read_startup_line 函数, 定义在 setting.c 文件中,
Reads startup line from EEPROM. Updated pointed line string data.
没有成功,返回 false

report_status_messages 函数 根据名字 就理解意思。

在 protocol.c 文件中,static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
只要不是零,就执行,
printString 函数 定义在print.c 中,串口写入
gc_execute_line 函数 定义在 gcode.c 中,执行命令

**************************************

内容6

Pin change interrupt for pin-out commands


ISR(PINOUT_INT_vect)
{
  // Enter only if any pinout pin is actively low.
内容6A();
}


首先,  #define PINOUT_INT_vect  PCINT1_vect
这里要再 学习 一下 AVR 的中断,

080005baxqleima9xkxlnb.jpg

PC0 through PC6 trigger PCINT1.

we’ll need to tell the AVR which pins we’d like for it to watch specifically.
This is done through the pin mask, which is just a normal 8-bit byte where the corresponding bit is set for each pin we’d like to be able to trigger the interrupt.

映射端口已经理解过,arduino上 A0 A1 A2 端口。(即 AVR上 PC0 PC1 PC2 端口)

内容 6A();

Enter only if any pinout pin is actively low.


if ((PINOUT_PIN & PINOUT_MASK) ^ PINOUT_MASK) {
            内容6B();
}


判断条件感觉有点复杂,PINOUT_PIN 先和  PINOUT_MASK 按位与,然后再 按位 异或。
只有 PINOUT_PIN 是 低电平,才能为真

然后,

内容6B();

    if (bit_isfalse(PINOUT_PIN,bit(PIN_RESET))) {
      mc_reset();
    } else if (bit_isfalse(PINOUT_PIN,bit(PIN_FEED_HOLD))) {
      sys.execute |= EXEC_FEED_HOLD;
    } else if (bit_isfalse(PINOUT_PIN,bit(PIN_CYCLE_START))) {
      sys.execute |= EXEC_CYCLE_START;
    }


首先,
bit_isfalse 是 个宏,定义在 nuts_bolts.h 中,
#define bit_isfalse(x,mask) ((x & mask) == 0)

mc_reset 定义在 motion_control.c 中,system reset。

sys 一个复杂的结构体,
// Define global system variables
typedef struct {
  uint8_t abort;                 // System abort flag. Forces exit back to main loop for reset.
  uint8_t state;                 // Tracks the current state of Grbl.
  volatile uint8_t execute;      // Global system runtime executor bitflag variable. See EXEC bitmasks.
  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 auto_start;            // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings.
} system_t;


--------------------------------------

***********************
protocol.c 下1

***********************

#define EXEC_STATUS_REPORT  bit(0) // bitmask 00000001

#define EXEC_CYCLE_START    bit(1) // bitmask 00000010

#define EXEC_CYCLE_STOP     bit(2) // bitmask 00000100

#define EXEC_FEED_HOLD      bit(3) // bitmask 00001000

#define EXEC_RESET          bit(4) // bitmask 00010000

#define EXEC_ALARM          bit(5) // bitmask 00100000

#define EXEC_CRIT_EVENT     bit(6) // bitmask 01000000


Define system executor bit map. Used internally by runtime protocol as runtime command flags,  which notifies the main program to execute the specified runtime command asynchronously.
NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.)
The default flags are always false, so the runtime protocol only needs to check for a non-zero value to know when there is a runtime command to execute.


内容7 ();

Executes run-time commands, when required.


This is called from various check points in the main program, primarily where there may be a while loop waiting for a buffer to clear space or any point where the execution time from the last check point may be more than a fraction of a second.
This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code parsing and planning functions. This function also serves as an interface for the interrupts to set the system runtime flags, where only the main program handles them, removing the need to define more computationally-expensive volatile variables. This also provides a controlled way to execute certain tasks without having two or more instances of the same task, such as the planner recalculating the buffer upon a feedhold or override.
NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts, limit switches, or the main program.

void protocol_execute_runtime()
{
  if (sys.execute) { // Enter only if any bit flag is true
    uint8_t rt_exec = sys.execute; // Avoid calling volatile multiple times

        内容7A();
       }
    }
}



System alarm. Everything has shutdown by something that has gone severely wrong.
Report the source of the error to the user. If critical, Grbl disables by entering an infinite loop until system reset/abort.

内容7A();


内容7B();

内容7C();

内容7D();

内容7E();

内容7F();

内容7G();


内容7B();

Critical event. Only hard limit qualifies. Update this as new critical events surface.


    if (rt_exec & (EXEC_ALARM | EXEC_CRIT_EVENT)) {      
      sys.state = STATE_ALARM; // Set system alarm state

      if (rt_exec & EXEC_CRIT_EVENT) {
        report_alarm_message(ALARM_HARD_LIMIT);
        report_feedback_message(MESSAGE_CRITICAL_EVENT);
        bit_false(sys.execute,EXEC_RESET); // Disable any existing reset
  
      } else {

        report_alarm_message(ALARM_ABORT_CYCLE);
      }

      bit_false(sys.execute,(EXEC_ALARM | EXEC_CRIT_EVENT));
    }



内容7C();

Execute system abort.


    if (rt_exec & EXEC_RESET) {
      sys.abort = true;  // Only place this is set true.
      return; // Nothing else to do but exit.
    }



内容7D();

Execute and serial print status


    if (rt_exec & EXEC_STATUS_REPORT) {
      report_realtime_status();
      bit_false(sys.execute,EXEC_STATUS_REPORT);
    }



内容7E();

Initiate stepper feed hold


    if (rt_exec & EXEC_FEED_HOLD) {
      st_feed_hold(); // Initiate feed hold.
      bit_false(sys.execute,EXEC_FEED_HOLD);
    }



内容7F();

Reinitializes the stepper module running state and, if a feed hold, re-plans the buffer.
NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.


    if (rt_exec & EXEC_CYCLE_STOP) {
      st_cycle_reinitialize();
      bit_false(sys.execute,EXEC_CYCLE_STOP);
    }



内容7G();

    if (rt_exec & EXEC_CYCLE_START) {
      st_cycle_start(); // Issue cycle start command to stepper subsystem
      if (bit_istrue(settings.flags,BITFLAG_AUTO_START)) {
        sys.auto_start = true; // Re-enable auto start after feed hold.
      }
      bit_false(sys.execute,EXEC_CYCLE_START);
    }



信息量太大,先整理,以后慢慢看。

*********************************

内容8

Directs and executes one line of formatted input from protocol_process.
While mostly incoming streaming g-code blocks, this also executes Grbl internal commands, such as settings, initiating the homing cycle, and toggling switch states.
This differs from the runtime command module by being susceptible to when Grbl is ready to execute the next line during a cycle, so for switches like block delete, the switch only effects the lines that are processed afterward, not necessarily real-time during a cycle, since there are motions already stored in the buffer.
However, this 'lag' should not be an issue, since these commands are not typically used during a cycle.

uint8_t protocol_execute_line(char *line)
{   
  // Grbl internal command and parameter lines are of the form '$4=374.3' or '$' for help  
  if(line[0] == '$') {
   
    uint8_t char_counter = 1;
    uint8_t helper_var = 0; // Helper variable
    float parameter, value;
    switch( line[char_counter] ) {
                内容8A();
    }
    return(STATUS_OK); // If '$' command makes it to here, then everything's ok.

  } else {
    return(gc_execute_line(line));    // Everything else is gcode
  }
}



内容8A();

case 0 : 内容8B();

case '$': 内容8C();

case '#' : 内容8D();

case 'G' : 内容8E();

case 'C' : 内容8F();

case 'X' : 内容8G();

case 'H' : 内容8H();

case 'N' : 内容8I();

default : 内容8J();



内容8B();

report_grbl_help(); break;



内容8C();

Prints Grbl settings $


        if ( line[++char_counter] != 0 )
                { return(STATUS_UNSUPPORTED_STATEMENT); }
        else
                { report_grbl_settings(); }
        break;



内容8D();

Print gcode parameters #


        if ( line[++char_counter] != 0 )
                { return(STATUS_UNSUPPORTED_STATEMENT); }
        else
                { report_gcode_parameters(); }
        break;



内容8E();

Prints gcode parser state G


        if ( line[++char_counter] != 0 )
               { return(STATUS_UNSUPPORTED_STATEMENT); }
        else
               { report_gcode_modes(); }
        break;



内容8F();

Set check g-code mode C

        if ( line[++char_counter] != 0 )
               { return(STATUS_UNSUPPORTED_STATEMENT); }

        if ( sys.state == STATE_CHECK_MODE ) {
          mc_reset();
          report_feedback_message(MESSAGE_DISABLED);
        } else {
          if (sys.state)
               { return(STATUS_IDLE_ERROR); }
          sys.state = STATE_CHECK_MODE;
          report_feedback_message(MESSAGE_ENABLED);
        }
        break;


Perform reset when toggling off. Check g-code mode should only work if Grbl is idle and ready, regardless of alarm locks.
This is mainly to keep things simple and consistent.


内容8G();

Disable alarm lock X


        if ( line[++char_counter] != 0 )
                { return(STATUS_UNSUPPORTED_STATEMENT); }
        if (sys.state == STATE_ALARM) {
          report_feedback_message(MESSAGE_ALARM_UNLOCK);
          sys.state = STATE_IDLE;
          // Don't run startup script. Prevents stored moves in startup from causing accidents.
        }
        break;     



内容8H();

Perform homing cycle H


        if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) {
          // Only perform homing if Grbl is idle or lost.
          if ( sys.state==STATE_IDLE || sys.state==STATE_ALARM ) {
            mc_go_home();
            if (!sys.abort)
                   { protocol_execute_startup(); } // Execute startup scripts after successful homing.
          } else
                   { return(STATUS_IDLE_ERROR); }
        } else
                 { return(STATUS_SETTING_DISABLED); }
        break;



内容8I();

Startup lines. N


        if ( line[++char_counter] == 0 ) { // Print startup lines
          for (helper_var=0; helper_var < N_STARTUP_LINE; helper_var++) {
            if (!(settings_read_startup_line(helper_var, line))) {
              report_status_message(STATUS_SETTING_READ_FAIL);
            } else {
              report_startup_line(helper_var,line);
            }
          }
          break;
        } else { // Store startup line
          helper_var = true;  // Set helper_var to flag storing method.
          // No break. Continues into default: to read remaining command characters.
        }



内容8J();

Storing setting methods


        if(!read_float(line, &char_counter, ¶meter))
              { return(STATUS_BAD_NUMBER_FORMAT); }
        if(line[char_counter++] != '=')
              { return(STATUS_UNSUPPORTED_STATEMENT); }
        if (helper_var) { // Store startup line
          // Prepare sending gcode block to gcode parser by shifting all characters
          helper_var = char_counter; // Set helper variable as counter to start of gcode block
          do {
            line[char_counter-helper_var] = line[char_counter];
          } while (line[char_counter++] != 0);
          // Execute gcode block to ensure block is valid.
          helper_var = gc_execute_line(line); // Set helper_var to returned status code.
          if (helper_var) { return(helper_var); }
          else {
            helper_var = trunc(parameter); // Set helper_var to int value of parameter
            settings_store_startup_line(helper_var,line);
          }
        } else { // Store global setting.
          if(!read_float(line, &char_counter, &value))
                   { return(STATUS_BAD_NUMBER_FORMAT); }
          if(line[char_counter] != 0)
                   { return(STATUS_UNSUPPORTED_STATEMENT); }
          return(settings_store_global_setting(parameter, value));
        }



上面的代码解释了下面的命令
https://github.com/grbl/grbl/wiki/Configuring-Grbl-v0.8

Grbl Internal Commands:

Technically, the remaining Grbl commands are not configuration commands, but we're going to explain them here for no good reason, other than for completeness.
The three current state commands: $G, $#, ?

Grbl provides three commands to report its current state and where it is. Of the three, one of them is real-time, responding with current position. The other two are not, but respond with how incoming blocks are going to be processed depending on the states set by g-code, settings, or switches. The reason they are not realtime is that Grbl has an internal buffer that stores and plans the buffered motions. The incoming gcode blocks just tack on a new motion at the end of the buffer when it has room. The buffer basically ensures Grbl motion move at the fastest and safest rates possible, constantly re-calculating it real-time as motions finish and new ones come in.
$G - View gcode parser state

This command prints all of the active gcode modes that the parser will interpret any incoming command. These modes include G20/G21 inches/mm mode, G54-G59 active work coordinate system, G0/G1/G2/G3 active motion mode, G17/G18/G19 active plane selection, G90/G91 absolute mode, G93/G94 inverse feed rate mode, M0/M1/M2 program flow, M3/M4/M5 spindle state, M8/M9 coolant state, T tool number, and F active feed rate. It will also list any active $Sx Grbl switches, like $S1 block delete. When called, Grbl will return a line like this:

[G0 G54 G17 G21 G90 G94 M0 M5 M9 T0 F500.000]

$# - View gcode parameters

G-code parameters generally store the G54-G59 work coordinate offsets and G28/G30 home positions (must not be confused with homing and machine zero. These home positions can be set anywhere in the machine space by the G28.1/G30.1 commands). Most of these parameters are directly written to EEPROM anytime they are changed and are persistent. Meaning that they will remain the same, regardless of power-down, until they are explicitly changed.

G54-G59 work coordinates can be changed via the G10 L2 Px or G10 L20 Px command defined by the NIST gcode standard and the EMC2 (linuxcnc.org) standard. G28/G30 home positions can be changed via the G28.1 and the G30.1 commands, respectively. Please note that G92 is not persistent or held in EEPROM per g-code standards, and will reset to zero when Grbl is reset. Please read these g-code standard to understand how they are used.

When $# is called, Grbl will respond with the stored offsets from machine coordinates for each system as follows.

[G54:4.000,0.000,0.000]
[G55:4.000,6.000,7.000]
[G56:0.000,0.000,0.000]
[G57:0.000,0.000,0.000]
[G58:0.000,0.000,0.000]
[G59:0.000,0.000,0.000]
[G28:1.000,2.000,0.000]
[G30:4.000,6.000,0.000]
[G92:0.000,0.000,0.000]

For the most part, these offset are not particularly useful unless you have homing enabled. If you do have homing enabled, these are wonderfully awesome, because once you home, you can always move back to same stored position by using the work coordinate systems within the accuracy of your machine. Or if you are making multiple parts and have a tool path for just one part, all you have to do is setup your work coordinate systems to the location where the next part is going to be made and re-run that same code.
? - Current status

The ? command immediately returns Grbl's active state and the real-time current position, both in machine coordinates and work coordinates. This may be sent at any time and works asynchronously with all other processes that Grbl is doing. The $13 Grbl setting determines whether it reports millimeters or inches. When ? is pressed, Grbl will immediately reply with something like the following:



The active states Grbl can be in are: Idle, Queue, Run, Hold, Home, Alarm, Check

    Idle: All systems are go and it's ready for anything.
    Queue: Motion(s) are queued in the planner buffer waiting for a cycle start command to be issued. Certain processes like check g-code mode can't run while something is queued. Reset to clear the queue.
    Run: Indicates a cycle is running.
    Hold: A feed hold is in process of executing, or slowing down to a stop. After the hold is complete, Grbl will enter a Queue state, waiting for a cycle start to resume the program.
    Home: In the middle of a homing cycle. NOTE: Positions are not updated live during the homing cycle, but they'll be set to [0,0,0] once done.
    Alarm: This indicates something has gone wrong or Grbl doesn't know its position. This state locks out all g-code commands, but allows you to interact with Grbl's settings if you need to. '$X' kill alarm lock releases this state and puts Grbl in the Idle state, which will let you move things again. As said before, be cautious of what you are doing after an alarm.
    Check: Grbl is in check g-code mode. It will process and respond to all g-code commands, but not motion or turn on anything. Once toggled off with another '$C' command, Grbl will reset itself.

Other Commands $C $X $H ~ ! Ctrl-X


$C - Check gcode mode

This toggles the Grbl's gcode parser to take all incoming blocks process them completely, as it would in normal operation, but it does not move any of the axes, ignores dwells, and powers off the spindle and coolant. This is intended as a way to provide the user a way to check how their new g-code program fares with Grbl's parser and monitor for any errors. (And eventually this will also check for soft limit violations.)

When toggled off, Grbl will perform an automatic soft-reset (^X). This is for two purposes. It simplifies the code management a bit. But, it also prevents users from starting a job when their g-code modes are not what they think they are. A system reset always gives the user a fresh, consistent start.

NOTE: Eventually, the check gcode mode could be re-factored to allow for a "program resume" feature in Grbl. This means that Grbl could start a g-code program anywhere. It would internally go through all of the g-code program up to the desired mid-program resume point to set all of the parser states and locations, move to that start point, and begin executing/moving from that point on. For example, say you had to E-stop in the middle of a program because you forgot something or you have the wrong tool in the spindle. Your part is fine and need to restart the program. Right now, you'll have to start the program from the beginning and let it physically move and run up to the point where you e-stopped. If you have a long program, this could take a while. Instead, a "program resume" will just go through the beginning of the program internally, without moving anything, and only begin moving from the resume point on.


$X - Kill alarm lock

Grbl's alarm mode is a state when something has gone critically wrong, like a hard limit or an abort during a cycle, or if Grbl doesn't know its position. By default, if you have homing enabled and power-up the Arduino, Grbl enters the alarm state, because it does not know its position. The alarm mode will lock all g-code blocks until the '$H' homing cycle has been performed. Or if a user needs to override the alarm lock to move their axes off their limit switches, for example, '$X' kill alarm lock will override the locks and allow g-code functions to work again.

But, tread carefully!! This should only be used in emergency situations. The position has likely been lost, and Grbl may not be where you think it is. So, it's advised to use G91 incremental mode to make short moves. Then, perform a homing cycle or reset immediately afterwards.


$H - Run homing cycle

This command is the only way to perform the homing cycle in Grbl. Previously, G28 and G30 would automatically start the homing cycle, but this is incorrect according to the g-code standards. Homing is a completely separate command handled by the controller. G28 and G30 only move to a 'home'/pre-defined position that is stored in the g-code parameters, which can be located anywhere in the machine.

TIP: After running a homing cycle, rather jogging manually all the time to a position in the middle of your workspace volume. You can set a G28 or G30 pre-defined position to be your post-homing position, closer to where you'll be machining. To set these, you'll first need to jog your machine to where you would want it to move to after homing. Type G28.1 (or G30.1) to have Grbl store that position. So then after '$H' homing, you could just enter 'G28' (or 'G30') and it'll move there auto-magically. In general, I would just move the XY axis to the center and leave the Z-axis up. This ensures that there isn't a chance the tool in the spindle doesn't catch on anything.


~ - Cycle start

This is the cycle start or resume command that can be issued at any time, as it is a real-time command. When Grbl has motions queued in its buffer and is ready to go, the ~ cycle start command will start executing the buffer and Grbl will begin moving the axes. However, by default, auto-cycle start is enabled, so new users will not need this command unless a feed hold is performed. When a feed hold is executed, cycle start will resume the program. Cycle start will only be effective when there are motions in the buffer ready to go and will not work with any other process like homing.


! - Feed hold

The feed hold command will bring the active cycle to a stop via a controlled deceleration, so not to lose position. It is also real-time and may be activated at any time. Once finished or paused, Grbl will wait until a cycle start command is issued to resume to program. Feed hold can only pause a cycle and will not affect homing or any other process.

If you need to stop a cycle mid-program and can't afford losing position, perform a feed hold to have Grbl bring everything to a controlled stop. Once finished, you can then issue a reset. Always try to execute a feed hold whenever the machine is running before hitting reset, except of course if there is some emergency situation.


Ctrl-x - Reset Grbl

This is Grbl's soft reset command. It's real-time and can be sent at any time. As the name implies, it resets Grbl, but in a controlled way, retains your machine position, and all done without powering down your Arduino. The only times a soft-reset could lose position is when problems like if the steppers were killed while they were moving. If so, it will report if Grbl's tracking of the machine position has been lost. This is because an uncontrolled deceleration can lead to lost steps, and Grbl has no feedback to how much it lost (this is the problem with steppers in general). Otherwise, Grbl will just re-initialize, run the startup lines, and continue on its merry way.

Please note that it's recommended to do a soft-reset before starting a job. This guarantees that there aren't any g-code modes active that should be from playing around or setting up your machine. So, your machine will always starts fresh and consistently, and your machine does what you expect it to.

----------------------------------------


*******************
protocol.c 下2

*******************

内容9

Process and report status one line of incoming serial data.


void protocol_process()
{
  uint8_t c;
  while((c = serial_read()) != SERIAL_NO_DATA) {
    if ((c == '') || (c == '')) {

       内容9A();

       }
    else{

        内容9B();

    }
  }
}


Performs an initial filtering by removing spaces and comments and capitalizing all letters.
Runtime command check point before executing line. Prevent any furthur line executions.
NOTE: If there is no line, this function should quickly return to the main program when the buffer empties of non-executable data.


内容9A();

      protocol_execute_runtime();

      if (sys.abort) { return; } // Bail to main program upon system abort   

      if (char_counter > 0) {// Line is complete. Then execute!
        line[char_counter] = 0; // Terminate string
        report_status_message(protocol_execute_line(line));
      }
       else {
        // Empty or comment line. Skip block.
        report_status_message(STATUS_OK); // Send status message for syncing purposes.
      }

      protocol_reset_line_buffer();



内容9B();

     if (iscomment) {
        // Throw away all comment characters
        if (c == ')') {
          // End of comment. Resume line.
          iscomment = false;
        }
      } else {
        if (c <= ' ') {
          // Throw away whitepace and control characters
        } else if (c == '/') {
          // Block delete not supported. Ignore character.
        } else if (c == '(') {
          // Enable comments flag and ignore all characters until ')' or EOL.
          iscomment = true;
        }
         else if (char_counter >= LINE_BUFFER_SIZE-1) {
          // Report line buffer overflow and reset
          report_status_message(STATUS_OVERFLOW);
          protocol_reset_line_buffer();
        } else if (c >= 'a' && c <= 'z') { // Upcase lowercase
          line[char_counter++] = c-'a'+'A';
        } else {
          line[char_counter++] = c;
        }
      }


********************************

protocol 的代码看完,还是糊里糊涂的。先继续。

--------------------------------------------------------------------------

**********************
nuts_bolts.h

Header file for shared definitions, variables, and functions
***********************************


nuts_bolts.h 被 protocol.c 文件 引用过,并且 定义了很多 重要 变量和结构。

****************

内容1

引用头文件


#include

#include

#include

#include "config.h"

#include "defaults.h"

#include "pin_map.h"



   是 标准 头文件,

网上搜一下,
085852gsosx4rrmprr9xq2.jpg

085852bkwe473qeh887kvz.jpg

085851yrpibpdcp3idwi00.jpg

config.h 已经理解过,compile time configuration
defaults.h 也理解过,defaults settings configuration file

pin_map.h - Pin mapping configuration file,端口功能定义,需要另外一篇文章理解。

********************************

内容2

利用宏定义常量


#define false 0
#define true 1

#define N_AXIS 3 // Number of axes
#define X_AXIS 0 // Axis indexing value
#define Y_AXIS 1
#define Z_AXIS 2

#define MM_PER_INCH (25.40)
#define INCH_PER_MM (0.0393701)

什么时候用 #define 什么时候用 const 定义常量,一般如下
1,对于数值和字符常量,用#define,注意添加必要注释;
2,对于其它类型常量,用 const 限定符

区别,
define宏仅仅是展开(替换),有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。) const常量会在内存中分配(可以是堆中也可以是栈中)。

宏定义很简单。

************************

内容3

Useful macros


#define clear_vector(a) memset(a, 0, sizeof(a))

#define clear_vector_float(a) memset(a, 0.0, sizeof(float)*N_AXIS)

#define max(a,b) (((a) > (b)) ? (a) : (b))

#define min(a,b) (((a) < (b)) ? (a) : (b))



memset(a, 0, sizeof(a)),用 零 清空 整型数组 a;(memset 被 string.h 声明)

memset(a, 0.0, sizeof(float)*N_AXIS),用 0.0 清空 浮点型 数组 a;

max() ,min() 很简单


***********************

内容4

Bit field and masking macros


#define bit(n) (1 << n)

#define bit_true(x,mask) (x |= mask)

#define bit_false(x,mask) (x &= ~mask)

#define bit_toggle(x,mask) (x ^= mask)

#define bit_istrue(x,mask) ((x & mask) != 0)

#define bit_isfalse(x,mask) ((x & mask) == 0)


<<:左移位 http://baike.baidu.com/link?url= ... 2S3cSwNbpmTSUmOh-jK

n = 3; //n二进制为11
例如,

[code]    n = 3; //n二进制为11
     
    b = n<<1; //n向左移动一位,b二进制为110
     
    b = n<<2; //n向左移动二位,b二进制为1100
     
    b = 1<<n; 1像左移动n位,相当于2的n次方,b二进制为1000[="" code]
这些位运算符号
&         按位与
~       按位取反
¦           按位或
< <     按位左移
∧         按位异或


http://blog.csdn.net/superdullwolf/article/details/4649080
位运算应用口诀
清零取反要用与,某位置一可用或
若要取反和交换,轻轻松松用异或

(1) 按位与-- &
1 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)
2 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)

(2) 按位或-- |
    常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)

(3) 位异或-- ^
1 使特定位的值取反 (mask中特定位置1,其它位为0 s=s^mask)

这些宏定义 是双刃剑,除非 宏的名字特别清晰已记,或者很熟悉这些宏定义,否则也容易混淆。尤其对新手。

********************************

内容5

Define system executor bit map.


在 protocol.c 中简单理解过,

#define EXEC_STATUS_REPORT  bit(0) // bitmask 00000001

#define EXEC_CYCLE_START    bit(1) // bitmask 00000010

#define EXEC_CYCLE_STOP     bit(2) // bitmask 00000100

#define EXEC_FEED_HOLD      bit(3) // bitmask 00001000

#define EXEC_RESET          bit(4) // bitmask 00010000

#define EXEC_ALARM          bit(5) // bitmask 00100000

#define EXEC_CRIT_EVENT     bit(6) // bitmask 01000000

// #define                  bit(7) // bitmask 10000000



Used internally by runtime protocol as runtime command flags,  which notifies the main program to execute the specified runtime command asynchronously.
NOTE: The system executor uses an unsigned 8-bit volatile variable (8 flag limit.)
The default flags are always false, so the runtime protocol only needs to check for a non-zero value to know when there is a runtime command to execute.

上面的宏 已经在 protocol.c 被应用了,而且一般 和 system_t 结构搭配,system_t 结构如下

***********************************

内容6

Define global system variables


typedef struct {
  uint8_t abort;                 // System abort flag. Forces exit back to main loop for reset.

  uint8_t state;                 // Tracks the current state of Grbl.

  volatile uint8_t execute;      // Global system runtime executor bitflag variable. See EXEC bitmasks.

  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 auto_start;            // Planner auto-start flag. Toggled off during feed hold. Defaulted by settings.
} system_t;

extern system_t sys;


用来记录 系统 状态

*********************************

内容7

Define system state bit map.


#define STATE_IDLE       0 // Must be zero.

#define STATE_INIT       1 // Initial power up state.

#define STATE_QUEUED     2 // Indicates buffered blocks, awaiting cycle start.

#define STATE_CYCLE      3 // Cycle is running

#define STATE_HOLD       4 // Executing feed hold

#define STATE_HOMING     5 // Performing homing cycle

#define STATE_ALARM      6 // In alarm state. Locks out all g-code processes. Allows settings access.

#define STATE_CHECK_MODE 7 // G-code check mode. Locks out planner and motion only.

// #define STATE_JOG     8 // Jogging mode is unique like homing.


The state variable primarily tracks the individual functions of Grbl to manage each without overlapping.
It is also used as a messaging flag for critical events.

******************************

内容8

声明 函数


int read_float(char *line, uint8_t *char_counter, float *float_ptr);

void delay_ms(uint16_t ms);

void delay_us(uint32_t us);

void sys_sync_current_position();



******************************

GRBL 的内容量 很多,只是浏览 一下 就很费力气,更别说理解要费多少精力。

-----------------------------------------------------------------

**********************
nuts_bolts.c

暂时只了解功能即可
***********************


不得不说,如果第一遍就把所有代码理解,对我来说是不可能完成的任务。必须要有取舍,这个文件就可以暂时不求理解其如何实现,只了解功能即可。

nuts_bolts.c - Shared functions

*********************

内容1

引用头文件


#include

#include "nuts_bolts.h"

#include "gcode.h"

#include "planner.h"


gcode.h - rs274/ngc parser. 一个重量级头文件

planner.h - buffers movement commands and manages the acceleration profile plan
  
*************************

内容2

#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
extern float __floatunsisf (unsigned long);


************************

内容3

Extracts a floating point value from a string.


The following code is based loosely on the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely available conversion method examples, but has been highly optimized for Grbl.
For known CNC applications, the typical decimal value is expected to be in the range of E0 to E-4.
Scientific notation is officially not supported by g-code, and the 'E' character may be a g-code word on some CNC systems.
So, 'E' notation will not be recognized.
NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().

int read_float(char *line, uint8_t *char_counter, float *float_ptr)                  
{
  char *ptr = line + *char_counter;
  unsigned char c;
   
  // Grab first character and increment pointer. No spaces assumed in line.
  c = *ptr++;
  
  // Capture initial positive/minus character
  bool isnegative = false;
  if (c == '-') {
    isnegative = true;
    c = *ptr++;
  } else if (c == '+') {
    c = *ptr++;
  }
  
  // Extract number into fast integer. Track decimal in terms of exponent value.
  uint32_t intval = 0;
  int8_t exp = 0;
  uint8_t ndigit = 0;
  bool isdecimal = false;
  while(1) {
    c -= '0';
    if (c <= 9) {
      ndigit++;
      if (ndigit <= MAX_INT_DIGITS) {
        if (isdecimal) { exp--; }
        intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c
      } else {
        if (!(isdecimal)) { exp++; }  // Drop overflow digits
      }
    } else if (c == (('.'-'0') & 0xff)  &&  !(isdecimal)) {
      isdecimal = true;
    } else {
      break;
    }
    c = *ptr++;
  }
  
  // Return if no digits have been read.
  if (!ndigit) { return(false); };
  
  // Convert integer into floating point.
  float fval;
  fval = __floatunsisf(intval);
  
  // Apply decimal. Should perform no more than two floating point multiplications for the
  // expected range of E0 to E-4.
  if (fval != 0) {
    while (exp <= -2) {
      fval *= 0.01;
      exp += 2;
    }
    if (exp < 0) {
      fval *= 0.1;
    } else if (exp > 0) {
      do {
        fval *= 10.0;
      } while (--exp > 0);
    }
  }

  // Assign floating point value with correct sign.   
  if (isnegative) {
    *float_ptr = -fval;
  } else {
    *float_ptr = fval;
  }

  *char_counter = ptr - line - 1; // Set char_counter to next statement
  
  return(true);
}


有点复杂。

***************************************

内容4

Delays variable defined milliseconds.


Compiler compatibility fix for _delay_ms(), which only accepts constants in future compiler releases.

void delay_ms(uint16_t ms)
{
  while ( ms-- ) { _delay_ms(1); }
}


**********************************

内容5

Delays variable defined microseconds.


Compiler compatibility fix for _delay_us(), which only accepts constants in future compiler releases.
Written to perform more efficiently with larger delays, as the counter adds parasitic time in each iteration.

void delay_us(uint32_t us)
{
  while (us) {
    if (us < 10) {
      _delay_us(1);
      us--;
    } else if (us < 100) {
      _delay_us(10);
      us -= 10;
    } else if (us < 1000) {
      _delay_us(100);
      us -= 100;
    } else {
      _delay_ms(1);
      us -= 1000;
    }
  }
}


*************************************

内容6

Syncs all internal position vectors to the current system position.


void sys_sync_current_position()
{
  plan_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]);
  gc_set_current_position(sys.position[X_AXIS],sys.position[Y_AXIS],sys.position[Z_AXIS]);
}


*************************************

大概可以看出,这个文件是起辅助作用的,不直接操作CNC 相关设置。


</pin_reset)|(1<<pin_feed_hold)|(1<<pin_cycle_start))
</pin_reset)|(1<<pin_feed_hold)|(1<<pin_cycle_start))
</x_direction_bit)|(1<<y_direction_bit)
</x_axis)|(1<<y_axis)|(1<</x_axis)|(1<</rxcie0;
</txen0;
</rxen0;
回复

使用道具 举报

ID:112317 发表于 2016-4-9 23:37 | 显示全部楼层
************************
gcode.h

rs274/ngc parser
*************************


这里需要大量 CNC背景知识,对我来说也是一个大挑战。

内容1

引入头文件


#include
#include "nuts_bolts.h"


****************************

内容2

Define modal group internal numbers for checking multiple command violations and tracking the type of command that is called in the block.


A modal group is a group of g-code commands that are mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute a unique motion.
These are defined in the NIST RS274-NGC v3 g-code standard, available online, and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc).

#define MODAL_GROUP_NONE 0

#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal

#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion

#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection

#define MODAL_GROUP_3 4 // [G90,G91] Distance mode

#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping

#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode

#define MODAL_GROUP_6 7 // [G20,G21] Units

#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning

#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection


Modal commands are arranged in sets called “modal groups”, and only one member of a modal group may be in force at any given time.
In general, a modal group contains commands for which it is logically impossible for two members to be in effect at the same time — like measure in inches vs. measure in millimeters.

***************************************

内容3

Define command actions for within execution-type modal groups (motion, stopping, non-modal).
Used internally by the parser to know which command to execute.


#define MOTION_MODE_SEEK 0 // G0
#define MOTION_MODE_LINEAR 1 // G1
#define MOTION_MODE_CW_ARC 2  // G2
#define MOTION_MODE_CCW_ARC 3  // G3
#define MOTION_MODE_CANCEL 4 // G80

#define PROGRAM_FLOW_RUNNING 0
#define PROGRAM_FLOW_PAUSED 1 // M0, M1
#define PROGRAM_FLOW_COMPLETED 2 // M2, M30

#define NON_MODAL_NONE 0
#define NON_MODAL_DWELL 1 // G4
#define NON_MODAL_SET_COORDINATE_DATA 2 // G10
#define NON_MODAL_GO_HOME_0 3 // G28
#define NON_MODAL_SET_HOME_0 4 // G28.1
#define NON_MODAL_GO_HOME_1 5 // G30
#define NON_MODAL_SET_HOME_1 6 // G30.1
#define NON_MODAL_SET_COORDINATE_OFFSET 7 // G92
#define NON_MODAL_RESET_COORDINATE_OFFSET 8 //G92.1


实现的命令。

****************************

内容4

g代码状态 结构体


typedef struct {
  uint8_t status_code;             // Parser status for current block

  uint8_t motion_mode;             // {G0, G1, G2, G3, G80}

  uint8_t inverse_feed_rate_mode;  // {G93, G94}

  uint8_t inches_mode;             // 0 = millimeter mode, 1 = inches mode {G20, G21}

  uint8_t absolute_mode;           // 0 = relative motion, 1 = absolute motion {G90, G91}

  uint8_t program_flow;            // {M0, M1, M2, M30}

  int8_t spindle_direction;        // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5}

  uint8_t coolant_mode;            // 0 = Disable, 1 = Flood Enable {M8, M9}

  float feed_rate;                 // Millimeters/min
//  float seek_rate;                 // Millimeters/min. Will be used in v0.9 when axis independence is installed
  float position[3];               // Where the interpreter considers the tool to be at this point in the code

  uint8_t tool;
//  uint16_t spindle_speed;          // RPM/100

  uint8_t plane_axis_0,
          plane_axis_1,
          plane_axis_2;            // The axes of the selected plane  
  uint8_t coord_select;            // Active work coordinate system number. Default: 0=G54.
  float coord_system[N_AXIS];      // Current work coordinate system (G54+). Stores offset from absolute machine
                                   // position in mm. Loaded from EEPROM when called.
  float coord_offset[N_AXIS];      // Retains the G92 coordinate offset (work coordinates) relative to
                                   // machine zero in mm. Non-persistent. Cleared upon reset and boot.        
} parser_state_t;
extern parser_state_t gc;


应该会遇到很多应用的情况。

******************************

内容5

声明函数


// Initialize the parser
void gc_init();

// Execute one block of rs275/ngc/g-code
uint8_t gc_execute_line(char *line);

// Set g-code parser position. Input in steps.
void gc_set_current_position(int32_t x, int32_t y, int32_t z);


------------------------------------------------------




********************
gcode.c 上

rs274/ngc parser

*********************


如果熟悉 gcode 编程,应该还好理解。我发现,GRBL 任何一个文件 对我 都是个 挑战,我以前没有接触过CNC 相关内容。

内容1

引入头文件


#include "gcode.h"

#include

#include "nuts_bolts.h"

#include

#include "settings.h"

#include "motion_control.h"

#include "spindle_control.h"

#include "coolant_control.h"

#include "errno.h"

#include "protocol.h"

#include "report.h"


********************************

内容2

定义变量和宏


// Declare gc extern struct
parser_state_t gc;

#define FAIL(status) gc.status_code = status;


*********************************

内容3

static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter);


Parses the next statement and leaves the counter on the first character following the statement.
Returns 1 if there was a statements, 0 if end of string was reached or there was an error (check state.status_code).

static int next_statement(char *letter, float *float_ptr, char *line, uint8_t *char_counter)
{
  if (line[*char_counter] == 0) {
    return(0); // No more statements
  }
  
  *letter = line[*char_counter];
  if((*letter < 'A') || (*letter > 'Z')) {
    FAIL(STATUS_EXPECTED_COMMAND_LETTER);
    return(0);
  }
  (*char_counter)++;
  if (!read_float(line, char_counter, float_ptr)) {
    FAIL(STATUS_BAD_NUMBER_FORMAT);
    return(0);
  };
  return(1);
}


****************************

内容4

static void select_plane(uint8_t axis_0, uint8_t axis_1, uint8_t axis_2)
{
  gc.plane_axis_0 = axis_0;
  gc.plane_axis_1 = axis_1;
  gc.plane_axis_2 = axis_2;
}


*****************************

内容5

Initialize the parser


void gc_init()
{
  memset(&gc, 0, sizeof(gc));

  gc.feed_rate = settings.default_feed_rate; // Should be zero at initialization.

//  gc.seek_rate = settings.default_seek_rate;

  select_plane(X_AXIS, Y_AXIS, Z_AXIS);

  gc.absolute_mode = true;
  
  // Load default G54 coordinate system.

  if (!(settings_read_coord_data(gc.coord_select,gc.coord_system))) {
    report_status_message(STATUS_SETTING_READ_FAIL);
  }
}


***************************

内容6

Sets g-code parser position in mm. Input in steps.
Called by the system abort and hard limit pull-off routines.


void gc_set_current_position(int32_t x, int32_t y, int32_t z)
{
  gc.position[X_AXIS] = x/settings.steps_per_mm[X_AXIS];
  gc.position[Y_AXIS] = y/settings.steps_per_mm[Y_AXIS];
  gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS];
}


***************************

内容7

static float to_millimeters(float value)
{
  return(gc.inches_mode ? (value * MM_PER_INCH) : value);
}


***************************

只剩下 一个 占据gcode.c  80% 内容的函数了。


-------------------------------------------------------------------


************************
gocde.c 下

**************************


内容8

Executes one line of 0-terminated G-Code.


The line is assumed to contain only uppercase characters and signed floating point values (no whitespace).
Comments and block delete characters have been removed.
All units and positions are converted and exported to grbl's internal functions in terms of (mm, mm/min) and absolute machine coordinates, respectively.

uint8_t gc_execute_line(char *line)
{

  //If in alarm state, don't process. Immediately return with error.
  //NOTE: Might not be right place for this, but also prevents $N storing during alarm.

  if (sys.state == STATE_ALARM)
           { return(STATUS_ALARM_LOCK); }

  内容8A();

   /* Pass 1: Commands and set all modes. Check for modal group violations.
     NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */

  内容8B();

/* Pass 2: Parameters. All units converted according to current block commands. Position
     parameters are converted and flagged to indicate a change. These can have multiple connotations
     for different commands. Each will be converted to their proper value upon execution. */

  内容8C();

/* Execute Commands: Perform by order of execution defined in NIST RS274-NGC.v3, Table 8, pg.41.
     NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency
     and simplicity purposes, but this should not affect proper g-code execution. */

  内容8D();


}


***************************

内容8A();

定义变量 和 赋值


  uint8_t char_counter = 0;  
  char letter;
  float value;
  int int_value;

  uint16_t modal_group_words = 0;  // Bitflag variable to track and check modal group words in block
  uint8_t axis_words = 0;          // Bitflag to track which XYZ(ABC) parameters exist in block

  float inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified
  uint8_t absolute_override = false; // true(1) = absolute motion for this block only {G53}
  uint8_t non_modal_action = NON_MODAL_NONE; // Tracks the actions of modal group 0 (non-modal)
  
  float target[3], offset[3];  
  clear_vector(target); // XYZ(ABC) axes parameters.
  clear_vector(offset); // IJK Arc offsets are incremental. Value of zero indicates no change.
   
  gc.status_code = STATUS_OK;

  
****************************

变量涉及到 具体的 CNC 知识。



----------------------------------------------------------


************************
gcode.c 下

内容8B

************************


内容8B();

Check for modal group violations.


  uint8_t group_number = MODAL_GROUP_NONE;

  while(next_statement(&letter, &value, line, &char_counter)) {
    int_value = trunc(value);
    switch(letter) {
      case 'G':
             内容8E();

      case 'M':
             内容8F();

    }   

    // Check for modal group multiple command violations in the current block
    if (group_number) {
      if ( bit_istrue(modal_group_words,bit(group_number)) ) {
        FAIL(STATUS_MODAL_GROUP_VIOLATION);
      } else {
        bit_true(modal_group_words,bit(group_number));
      }
      group_number = MODAL_GROUP_NONE; // Reset for next command.
    }
  }

  // If there were any errors parsing this line, we will return right away with the bad news
  if (gc.status_code)
             { return(gc.status_code); }

  
******************************************

内容8E();

Set modal group values


        switch(int_value) {
          case 4: case 10: case 28: case 30: case 53: case 92: group_number = MODAL_GROUP_0; break;
          case 0: case 1: case 2: case 3: case 80: group_number = MODAL_GROUP_1; break;
          case 17: case 18: case 19: group_number = MODAL_GROUP_2; break;
          case 90: case 91: group_number = MODAL_GROUP_3; break;
          case 93: case 94: group_number = MODAL_GROUP_5; break;
          case 20: case 21: group_number = MODAL_GROUP_6; break;
          case 54: case 55: case 56: case 57: case 58: case 59: group_number = MODAL_GROUP_12; break;
        }         

        // Set 'G' commands
        switch(int_value) {
          case 0: gc.motion_mode = MOTION_MODE_SEEK; break;
          case 1: gc.motion_mode = MOTION_MODE_LINEAR; break;
          case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break;
          case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break;
          case 4: non_modal_action = NON_MODAL_DWELL; break;
          case 10: non_modal_action = NON_MODAL_SET_COORDINATE_DATA; break;
          case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break;
          case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break;
          case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break;
          case 20: gc.inches_mode = true; break;
          case 21: gc.inches_mode = false; break;
          case 28: case 30:
            int_value = trunc(10*value); // Multiply by 10 to pick up Gxx.1
            switch(int_value) {
              case 280: non_modal_action = NON_MODAL_GO_HOME_0; break;
              case 281: non_modal_action = NON_MODAL_SET_HOME_0; break;
              case 300: non_modal_action = NON_MODAL_GO_HOME_1; break;
              case 301: non_modal_action = NON_MODAL_SET_HOME_1; break;
              default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
            }
            break;
          case 53: absolute_override = true; break;
          case 54: case 55: case 56: case 57: case 58: case 59:
            gc.coord_select = int_value-54;
            break;
          case 80: gc.motion_mode = MOTION_MODE_CANCEL; break;
          case 90: gc.absolute_mode = true; break;
          case 91: gc.absolute_mode = false; break;
          case 92:
            int_value = trunc(10*value); // Multiply by 10 to pick up G92.1
            switch(int_value) {
              case 920: non_modal_action = NON_MODAL_SET_COORDINATE_OFFSET; break;        
              case 921: non_modal_action = NON_MODAL_RESET_COORDINATE_OFFSET; break;
              default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
            }
            break;
          case 93: gc.inverse_feed_rate_mode = true; break;
          case 94: gc.inverse_feed_rate_mode = false; break;
          default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
        }
        break;   


根据Gxx 设置相应的参数。

*******************************

内容8F();

Set modal group values


        switch(int_value) {
          case 0: case 1: case 2: case 30: group_number = MODAL_GROUP_4; break;
          case 3: case 4: case 5: group_number = MODAL_GROUP_7; break;
        }   
   
        // Set 'M' commands
        switch(int_value) {
          case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause
          case 1: break; // Optional stop not supported. Ignore.
          case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset
          case 3: gc.spindle_direction = 1; break;
          case 4: gc.spindle_direction = -1; break;
          case 5: gc.spindle_direction = 0; break;
          #ifdef ENABLE_M7
            case 7: gc.coolant_mode = COOLANT_MIST_ENABLE; break;
          #endif
          case 8: gc.coolant_mode = COOLANT_FLOOD_ENABLE; break;
          case 9: gc.coolant_mode = COOLANT_DISABLE; break;
          default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
        }            
        break;


*********************

好理解,开了个好头。

---------------------------------------------------------------

*******************
gcode.c 下

内容8C();

*******************


内容8C();

Pass 2: Parameters.


All units converted according to current block commands. Position
     parameters are converted and flagged to indicate a change. These can have multiple connotations
     for different commands. Each will be converted to their proper value upon execution.

  float p = 0, r = 0;
  uint8_t l = 0;
  char_counter = 0;
  while(next_statement(&letter, &value, line, &char_counter)) {
    switch(letter) {
      
         内容8G();

  }
  
  // If there were any errors parsing this line, we will return right away with the bad news
  if (gc.status_code) { return(gc.status_code); }

  

***********************************

内容8G();

      case 'G': case 'M': case 'N': break; // Ignore command statements and line numbers
      case 'F':
        if (value <= 0) { FAIL(STATUS_INVALID_STATEMENT); } // Must be greater than zero
        if (gc.inverse_feed_rate_mode) {
          inverse_feed_rate = to_millimeters(value); // seconds per motion for this motion only
        } else {         
          gc.feed_rate = to_millimeters(value); // millimeters per minute
        }
        break;
      case 'I': case 'J': case 'K': offset[letter-'I'] = to_millimeters(value); break;
      case 'L': l = trunc(value); break;
      case 'P': p = value; break;                    
      case 'R': r = to_millimeters(value); break;
      case 'S':
        if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative
        // TBD: Spindle speed not supported due to PWM issues, but may come back once resolved.
        // gc.spindle_speed = value;
        break;
      case 'T':
        if (value < 0) { FAIL(STATUS_INVALID_STATEMENT); } // Cannot be negative
        gc.tool = trunc(value);
        break;
      case 'X': target[X_AXIS] = to_millimeters(value); bit_true(axis_words,bit(X_AXIS)); break;
      case 'Y': target[Y_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Y_AXIS)); break;
      case 'Z': target[Z_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Z_AXIS)); break;
      default: FAIL(STATUS_UNSUPPORTED_STATEMENT);
    }


*******************************

暂时可以这样理解, 这个执行的命令类似 X10 之类的。(前面的 G01 类似命令在 PASS 1中)

-----------------------------------------------------


*************************
gcode.c 下

内容8D()
*************************


内容8D();                                                                                       

Execute Commands: Perform by order of execution defined in NIST RS274-NGC.v3, Table 8, pg.41.


NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency
     and simplicity purposes, but this should not affect proper g-code execution.
  
  // ([F]: Set feed and seek rates.)
  // TODO: Seek rates can change depending on the direction and maximum speeds of each axes. When
  // max axis speed is installed, the calculation can be performed here, or maybe in the planner.

内容8H();

    // [G54,G55,...,G59]: Coordinate system selection

  内容8I();

  // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets.
  // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this
  // modal group and do not effect these actions.

  内容8J();

  // [G0,G1,G2,G3,G80]: Perform motion modes.
  // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes.
  // Enter motion modes only if there are axis words or a motion mode command word in the block.

   if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_1)) || axis_words ) {
  内容8K();

    // Convert all target position data to machine coordinates for executing motion. Apply
    // absolute mode coordinate offsets or incremental mode offsets.
    // NOTE: Tool offsets may be appended to these conversions when/if this feature is added.

  内容8L();

    switch (gc.motion_mode) {

          内容8M();
     }

    // Report any errors.

   内容8N();

    // As far as the parser is concerned, the position is now == target. In reality the
    // motion control system might still be processing the action and the real tool position
    // in any intermediate location.

    内容8O();
}  // if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_1)) || axis_words )

  // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may
  // refill and can only be resumed by the cycle start run-time command.
   
    内容8P();                                                                          

  return(gc.status_code);
}


*****************************************

内容8H();                                                                             

  if (sys.state != STATE_CHECK_MODE) {
    //  ([M6]: Tool change should be executed here.)

    // [M3,M4,M5]: Update spindle state
    spindle_run(gc.spindle_direction);
  
    // [*M7,M8,M9]: Update coolant state
    coolant_run(gc.coolant_mode);
  }


只要不是 check mode,就执行 主轴功能,冷却功能。

*****************************************

内容8I();                                                                                    

// [G54,G55,...,G59]: Coordinate system selection


  if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_12)) ) { // Check if called in block

    float coord_data[N_AXIS];

    if (!(settings_read_coord_data(gc.coord_select,coord_data))) { return(STATUS_SETTING_READ_FAIL); }

    memcpy(gc.coord_system,coord_data,sizeof(coord_data));
  }


Read selected coordinate data from EEPROM. Updates pointed coord_data value. settings_read_coord_data 函数定义在setting.c中。

*********************

内容8J();                                                                                                                                                        

  // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets.
  // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this
  // modal group and do not effect these actions.




  switch (non_modal_action) {
    case NON_MODAL_DWELL:
         
               内容8X();
      break;
    case NON_MODAL_SET_COORDINATE_DATA:

               内容8Y();
      break;
    case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1:

               内容8Z();
      break;
    case NON_MODAL_SET_HOME_0: case NON_MODAL_SET_HOME_1:

              内容8AA();
      break;   
    case NON_MODAL_SET_COORDINATE_OFFSET:

             内容8AB();
      break;
    case NON_MODAL_RESET_COORDINATE_OFFSET:

             内容8AC();
      break;
  }



内容8X();                                                                                          


      if (p < 0) { // Time cannot be negative.
        FAIL(STATUS_INVALID_STATEMENT);
      } else {
        // Ignore dwell in check gcode modes
        if (sys.state != STATE_CHECK_MODE) { mc_dwell(p); }
      }


内容8Y();                                                                                          

      int_value = trunc(p); // Convert p value to int.
      if ((l != 2 && l != 20) || (int_value < 0 || int_value > N_COORDINATE_SYSTEM)) { // L2 and L20. P1=G54, P2=G55, ...
        FAIL(STATUS_UNSUPPORTED_STATEMENT);
      } else if (!axis_words && l==2) { // No axis words.
        FAIL(STATUS_INVALID_STATEMENT);
      } else {
        if (int_value > 0) { int_value--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
        else { int_value = gc.coord_select; } // Index P0 as the active coordinate system
        float coord_data[N_AXIS];
        if (!settings_read_coord_data(int_value,coord_data)) { return(STATUS_SETTING_READ_FAIL); }
        uint8_t i;
        // Update axes defined only in block. Always in machine coordinates. Can change non-active system.
        for (i=0; i<n_axis; i++)="" {="" axes="" indices="" are="" consistent,="" so="" loop="" may="" be="" used.
          if (bit_istrue(axis_words,bit(i)) ) {
            if (l == 20) {
              coord_data = gc.position-target; // L20: Update axis current position to target
            } else {
              coord_data = target; // L2: Update coordinate system axis
            }
          }
        }
        settings_write_coord_data(int_value,coord_data);
        // Update system coordinate system if currently active.
        if (gc.coord_select == int_value) { memcpy(gc.coord_system,coord_data,sizeof(coord_data)); }
      }
      axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags.


内容8Z();                                                                                         

      // Move to intermediate position before going home. Obeys current coordinate system and offsets
      // and absolute and incremental modes.
      if (axis_words) {
        // Apply absolute mode coordinate offsets or incremental mode offsets.
        uint8_t i;
        for (i=0; i<n_axis; i++)="" {="" axes="" indices="" are="" consistent,="" so="" loop="" may="" be="" used.
          if ( bit_istrue(axis_words,bit(i)) ) {
            if (gc.absolute_mode) {
              target += gc.coord_system + gc.coord_offset;
            } else {
              target += gc.position;
            }
          } else {
            target = gc.position;
          }
        }
        mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false);
      }
      // Retreive G28/30 go-home position data (in machine coordinates) from EEPROM
      float coord_data[N_AXIS];
      if (non_modal_action == NON_MODAL_GO_HOME_1) {
        if (!settings_read_coord_data(SETTING_INDEX_G30 ,coord_data)) { return(STATUS_SETTING_READ_FAIL); }     
      } else {
        if (!settings_read_coord_data(SETTING_INDEX_G28 ,coord_data)) { return(STATUS_SETTING_READ_FAIL); }     
      }      
      mc_line(coord_data[X_AXIS], coord_data[Y_AXIS], coord_data[Z_AXIS], settings.default_seek_rate, false);
      memcpy(gc.position, coord_data, sizeof(coord_data)); // gc.position[] = coord_data[];
      axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags.


内容8AA();                                                                                       

      if (non_modal_action == NON_MODAL_SET_HOME_1) {
        settings_write_coord_data(SETTING_INDEX_G30,gc.position);
      } else {
        settings_write_coord_data(SETTING_INDEX_G28,gc.position);
      }


内容8AB();                                                                                      

      if (!axis_words) { // No axis words
        FAIL(STATUS_INVALID_STATEMENT);
      } else {
        // Update axes defined only in block. Offsets current system to defined value. Does not update when
        // active coordinate system is selected, but is still active unless G92.1 disables it.
        uint8_t i;
        for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used.
          if (bit_istrue(axis_words,bit(i)) ) {
            gc.coord_offset = gc.position-gc.coord_system-target;
          }
        }
      }
      axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags.


内容8AC();                                                                                    

      clear_vector(gc.coord_offset); // Disable G92 offsets by zeroing offset vector.


****************************************


----------------------------------------------


**********************
gcode.c 下

**********************

内容8K();                                                   

    // G1,G2,G3 require F word in inverse time mode.  

    if ( gc.inverse_feed_rate_mode ) {
      if (inverse_feed_rate < 0 && gc.motion_mode != MOTION_MODE_CANCEL) {
        FAIL(STATUS_INVALID_STATEMENT);
      }
    }

    // Absolute override G53 only valid with G0 and G1 active.

    if ( absolute_override && !(gc.motion_mode == MOTION_MODE_SEEK || gc.motion_mode == MOTION_MODE_LINEAR)) {
      FAIL(STATUS_INVALID_STATEMENT);
    }

    // Report any errors.  
    if (gc.status_code) { return(gc.status_code); }



内容8L();                                                   

    uint8_t i;
    for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used to save flash space.
      if ( bit_istrue(axis_words,bit(i)) ) {
        if (!absolute_override) { // Do not update target in absolute override mode
          if (gc.absolute_mode) {
            target += gc.coord_system + gc.coord_offset; // Absolute mode
          } else {
            target += gc.position; // Incremental mode
          }
        }
      } else {
        target = gc.position; // No axis word in block. Keep same axis position.
      }
    }


*********************
头疼。

-------------------------------------------------------------


*****************************
gcode.c 下

****************************


内容8M();                                                              


      case MOTION_MODE_CANCEL:
               
               内容G80();
        break;
      case MOTION_MODE_SEEK:

               内容G00();
        break;
      case MOTION_MODE_LINEAR:
               
              内容G01();
        break;
      case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC:

              内容G02G03();
       break;


可以看出,执行的是 GROUP1 中的命令

内容G80();                                                            

        if (axis_words) { FAIL(STATUS_INVALID_STATEMENT); } // No axis words allowed while active.

内容G00();                                                           

        if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);}
        else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); }

mc_line 定义在 motion_control.c 文件中

内容G01();                                                         

        // TODO: Inverse time requires F-word with each statement. Need to do a check. Also need
        // to check for initial F-word upon startup. Maybe just set to zero upon initialization
        // and after an inverse time move and then check for non-zero feed rate each time. This
        // should be efficient and effective.
        if (!axis_words) { FAIL(STATUS_INVALID_STATEMENT);}
        else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS],
          (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); }

内容G02G03();                                                         

        // Check if at least one of the axes of the selected plane has been specified. If in center
        // format arc mode, also check for at least one of the IJK axes of the selected plane was sent.
        if ( !( bit_false(axis_words,bit(gc.plane_axis_2)) ) ||
             ( !r && !offset[gc.plane_axis_0] && !offset[gc.plane_axis_1] ) ) {
          FAIL(STATUS_INVALID_STATEMENT);
        } else {
          if (r != 0) { // Arc Radius Mode

            // Calculate the change in position along each selected axis
            float x = target[gc.plane_axis_0]-gc.position[gc.plane_axis_0];
            float y = target[gc.plane_axis_1]-gc.position[gc.plane_axis_1];
            
            clear_vector(offset);
            // First, use h_x2_div_d to compute 4*h^2 to check if it is negative or r is smaller
            // than d. If so, the sqrt of a negative number is complex and error out.
            float h_x2_div_d = 4 * r*r - x*x - y*y;
            if (h_x2_div_d < 0) { FAIL(STATUS_ARC_RADIUS_ERROR); return(gc.status_code); }
            // Finish computing h_x2_div_d.
            h_x2_div_d = -sqrt(h_x2_div_d)/hypot(x,y); // == -(h * 2 / d)
            // Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
            if (gc.motion_mode == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }

            if (r < 0) {
                h_x2_div_d = -h_x2_div_d;
                r = -r; // Finished with r. Set to positive for mc_arc
            }        
            // Complete the operation by calculating the actual center of the arc
            offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d));
            offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d));

          } else { // Arc Center Format Offset Mode            
            r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc
          }
         
          // Set clockwise/counter-clockwise sign for mc_arc computations
          uint8_t isclockwise = false;
          if (gc.motion_mode == MOTION_MODE_CW_ARC) { isclockwise = true; }
   
          // Trace the arc
          mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2,
            (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode,
            r, isclockwise);

**************************************

圆弧插补 的方法看起来有些复杂,需要其它资料来辅助理解。

----------------------------------------------



******************************
gcode.c 下

******************************

内容8N();                                                            

Report any errors


    if (gc.status_code) { return(gc.status_code); }   

    // As far as the parser is concerned, the position is now == target. In reality the
    // motion control system might still be processing the action and the real tool position
    // in any intermediate location.

memcpy(gc.position, target, sizeof(target)); // gc.position[] = target[];

内容8O();                                                            

M0,M1,M2,M30: Perform non-running program flow actions.

  if (gc.program_flow) {
    plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete.
    sys.auto_start = false; // Disable auto cycle start. Forces pause until cycle start issued.


内容8P();                                                            

If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9).

    if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { mc_reset(); }
    else { gc.program_flow = PROGRAM_FLOW_RUNNING; }
  }

******************************

gcode.c 应该是目前最重要的一个文件,遗憾的是,它同时也是最复杂和最难理解的。

---------------------------------------------------


*************************
main.c

An embedded CNC Controller with rs274/ngc (g-code) support
可以提前理解的函数
*************************


内容1 引用的头文件                                                                                                    

#include
#include
#include "config.h"
#include "planner.h"
#include "nuts_bolts.h"
#include "stepper.h"
#include "spindle_control.h"
#include "coolant_control.h"
#include "motion_control.h"
#include "gcode.h"
#include "protocol.h"
#include "limits.h"
#include "report.h"
#include "settings.h"
#include "serial.h"

唯一需要理解的头文件,pgmspace.h

在程序中访问FLASH 程序存储器,avr-libc 支持头文件:pgmspace.h,FLASH 区整数常量和数组应用
http://zhidao.baidu.com/link?url=dBUvav4dbX7xjYwSkJZ87KQKo2dvoG2WOTitK5XreXwL0jyd2iZIaKgIJke2FK7EPs_gy65ivdIANFe9bmNYIq

************************************
内容2 定义全局变量                                                                                    

system_t sys;

system_t 结构定义在nuts_bolts.h 文件中,表示GRBL 状态

**************************************
内容3 主函数                                                                                    

int main(void)
{
           初始化系统();

           系统变量设定();

  for(;;) {

         系统是否需要重启判断并执行();

        protocol_execute_runtime();

        protocol_process(); // ... process the serial protocol
   
       }
    return 0;   /* never reached */
}

简直不敢相信,main 函数是 这么容易理解。

------------------------------------------------------


*******************************
主函数 初始化系统

*******************************


// Setup serial baud rate and interrupts

serial_init();

// Load grbl settings from EEPROM

settings_init();


// Setup stepper pins and interrupt timers

st_init();

// Enable interrupts

sei();


Setup serial baud rate and interrupts                                                                       

serial_init() 是定义在 serial.c 中的函数,并且 设置 波特率 和使能 tx rx 字节接收中断

具体用法曾经理解过,不完整,放在 avr 里理解。


Load grbl settings from EEPROM                                                      

Load grbl settings from EEPROM 定义在 setting.c 中,引用了很多函数,但目的就是 读出 EEPROM中保存的系统设置数据。

Setup stepper pins and interrupt timers                                         

st_init() Initialize and start the stepper motor subsystem,定义在 stepper.c 中,使用了定时器配置。

Enable interrupts                                                                                                    

sei()

************************************************************

----------------------------------------------

******************
系统变量设定

*****************


// Clear all system variables

memset(&sys, 0, sizeof(sys));

// Set abort to complete initialization

sys.abort = true;

// Set alarm state to indicate unknown initial position

sys.state = STATE_INIT;

120419r03y31xua03o9s70.gif

也是比较容易理解的。

**********************
再来看 protocol_execute_runtime

**********************


Executes run-time commands, when required.
This is called from various check points in the main program, primarily where there may be a while loop waiting for a buffer to clear space or any point where the execution time from the last check point may be more than a fraction of a second.

This is a way to execute runtime commands asynchronously (aka multitasking) with grbl's g-code parsing and planning functions. This function also serves as an interface for the interrupts to set the system runtime flags, where only the main program handles them, removing the need to define more computationally-expensive volatile variables.

This also provides a controlled way to execute certain tasks without having two or more instances of the same task, such as the planner recalculating the buffer upon a feedhold or override.

NOTE: The sys.execute variable flags are set by any process, step or serial interrupts, pinouts, limit switches, or the main program.

System alarm. Everything has shutdown by something that has gone severely wrong.
141835qaboexqou6xbaryo.gif

Execute system abort.                              
141946hcn8z8c6nq8w05ca.gif


Execute and serial print status                                   
142041kfnarjrjiqiji117.gif


Initiate stepper feed hold                           
142125od995i6i6956m55d.gif


Reinitializes the stepper module running state                     

142226nl5rmpmmp8cs9319.gif

Issue cycle start command to stepper subsystem
142319kao7b73y1q7917an.gif


-----------------------------------------------------------------





*******************************
再来看 protocol_process

*******************************


Process and report status one line of incoming serial data.
Performs an initial filtering by removing spaces and comments and capitalizing all letters.

理解代码,其实前面干扰很多(出于理解目的),最核心的就是最后两句话,前面的都是检查是否符合输入要求;
就像安全检查。

132648fhym35puwphwwtg5.jpg

**********************************

至此,学习思路要发生变化了。以主函数的 双雄 为导向。
141621vs7nfrfjjk8nj68h.jpg


--------------------------------------------------




**************************
protocol_execute_runtime

调用了 stepper 相关的函数

**************************


在学习stepper motor driver 之前,先做一下准备工作,pin_map.h 中的设置

131435tcwk4sf4zskturud.jpg

  // NOTE: All step bit and direction pins must be on the same port.
  #define STEPPING_DDR      DDRA
  #define STEPPING_PORT     PORTA
  #define STEPPING_PIN      PINA
  #define X_STEP_BIT        2 // MEGA2560 Digital Pin 24
  #define Y_STEP_BIT        3 // MEGA2560 Digital Pin 25
  #define Z_STEP_BIT        4 // MEGA2560 Digital Pin 26
  #define X_DIRECTION_BIT   5 // MEGA2560 Digital Pin 27
  #define Y_DIRECTION_BIT   6 // MEGA2560 Digital Pin 28
  #define Z_DIRECTION_BIT   7 // MEGA2560 Digital Pin 29


然后,
需要了解一个结构(stepper.c中),

131531h6i0as20crk2mazz.jpg

需要学习的外部函数

// Initialize and setup the stepper motor subsystem
void st_init();

// Enable steppers, but cycle does not start unless called by motion control or runtime command.
void st_wake_up();

// Immediately disables steppers
void st_go_idle();

// Reset the stepper subsystem variables      
void st_reset();
            
// Notify the stepper subsystem to start executing the g-code program in buffer.
void st_cycle_start();

// Reinitializes the buffer after a feed hold for a resume.
void st_cycle_reinitialize();

// Initiates a feed hold of the running program
void st_feed_hold();

看起来,像是一个状态机, init, wakeup, go idle, reset, cycle start, cycle reinitialize, feed hold.


---------------------------------------------------------




*************************
stepper.c

stepper motor driver: executes motion plans using stepper motors
*************************


内容1

Initialize and start the stepper motor subsystem


void st_init()
{
  Configure directions of interface pins();

  waveform generation = 0100 = CTC();

  output mode = 00 (disconnected)();

  Configure Timer 2();

  Start in the idle state();

}



Configure directions of interface pins()                                               

  STEPPING_DDR |= STEPPING_MASK;
  STEPPING_PORT = (STEPPING_PORT & ~STEPPING_MASK) | settings.invert_mask;
  STEPPERS_DISABLE_DDR |= 1<<steppers_disable_bit;

其中,
  #define STEPPERS_DISABLE_DDR    DDRB
  #define STEPPERS_DISABLE_BIT    0  // Uno Digital Pin 8

124025ki75jn422jqd4vcz.jpg

waveform generation = 0100 = CTC()                                       

  TCCR1B &= ~(1<<wgm13);
  TCCR1B |=  (1<<wgm12);
  TCCR1A &= ~(1<<wgm11);
  TCCR1A &= ~(1<<wgm10);

In Clear Timer on Compare Match mode, the counter resets itself automatically when it reaches the value that’s stored in the OCRnA register instead of waiting until it hits 255 or 65,535.
So by writing to the output compare register, we control the frequency of the cycles.

// WGM13,WGM12,WGM11,WGM10:波型发生模式:
    //            比较输出模式(CTC模式),非PWM
    //                  00普通端口操作,OC1A/OC1B/OC1C未连接
    //                  01比较匹配时OC1A/OC1B/OC1C电平取反
    //                  10比较匹配时清零OC1A/OC1B/OC1C(输出低电平)
    //                  11比较匹配时置位OC1A/OC1B/OC1C(输出高电平)
    //            比较输出模式(CTC模式),快速PWM
    //                  00普通端口操作,OC1A/OC1B/OC1C未连接
    //                  01WGM13为0时同上,为1时 比较匹配时 OC1A电平取反,OC1B/OC1C保留
    //                  10比较匹配时OC1A/OC1B/OC1C清零,在TOP时OC1A/OC1B/OC1C置位
    //                  11比较匹配时OC1A/OC1B/OC1C置位,在TOP时OC1A/OC1B/OC1C清零
    //            比较输出模式(CTC模式),相位修正及相频修正PWM
    //                  00普通端口操作,OC1A/OC1B/OC1C未连接
    //                  01WGM13为0:同上,为1时 比较匹配时 OC1A电平取反,OC1B/OC1C保留
    //                  10升序计数匹配时将OC1A/OC1B/OC1C清零,降序计数匹配时将OC1A/OC1B/OC1C置位
    //                  11升序计数匹配时将OC1A/OC1B/OC1C置位,降序计数匹配时将OC1A/OC1B/OC1C清零


output mode = 00 (disconnected)                                                                     

  TCCR1A &= ~(3<<com1a0);
  TCCR1A &= ~(3<<com1b0);

    // COM1A1,COM1A0:通道A的比较输出模式
    // COM1B1,COM1B0:通道B的比较输出模式


Configure Timer 2()                                                                                          

  TCCR2A = 0; // Normal operation
  TCCR2B = 0; // Disable timer until needed.
  TIMSK2 |= (1<<toie2); enable="" timer2="" overflow="" interrupt=""
  #ifdef STEP_PULSE_DELAY
    TIMSK2 |= (1<<ocie2a); enable="" timer2="" compare="" match="" a="" interrupt
  #endif

Start in the idle state()                                                                                       

but first wake up to check for keep steppers enabled option.

  st_wake_up();
  st_go_idle();


--------------------------------------------------------------------




*********************
st_wake_up()

Stepper state initialization.
*********************


Cycle should only start if the st.cycle_start flag is enabled.
Startup init and limits call this function but shouldn't start the cycle.

void st_wake_up()
{
  
  if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) {
    STEPPERS_DISABLE_PORT |= (1<<steppers_disable_bit);
  } else {
    STEPPERS_DISABLE_PORT &= ~(1<<steppers_disable_bit);
  }

  if (sys.state == STATE_CYCLE) {
   
    out_bits = (0) ^ (settings.invert_mask);
  
    #ifdef STEP_PULSE_DELAY
   
      step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
     
      OCR2A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
    #else // Normal operation

      step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
    #endif

    TIMSK1 |= (1<<ocie1a);
  }
}


pin8 是使能 stepper 的控制点
142036qe4vmgmytolugj56.jpg

Initialize step pulse timing from settings.
Enable stepper driver interrupt


-----------------------------------------------------------


********************
void st_go_idle()

Stepper shutdown
********************


void st_go_idle()
{

  TIMSK1 &= ~(1<<ocie1a);

  if ((settings.stepper_idle_lock_time != 0xff) || bit_istrue(sys.execute,EXEC_ALARM)) {

    delay_ms(settings.stepper_idle_lock_time);
    if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) {
      STEPPERS_DISABLE_PORT &= ~(1<<steppers_disable_bit);
    } else {
      STEPPERS_DISABLE_PORT |= (1<<steppers_disable_bit);
    }   
  }
}

  Disable stepper driver interrupt
  Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete stop and not drift from residual inertial forces at the end of the last movement.


------------------------------------------------------------






</ocie1a);
</steppers_disable_bit);
</com1b0);
</wgm10);
</wgm12);
</wgm13);
</steppers_disable_bit;
回复

使用道具 举报

ID:98618 发表于 2016-4-9 23:41 | 显示全部楼层
楼主,我按教程把GRBL下位机源文件中的 *.h 和 *.c 文件加入,再加入 makefile 文件, 点击 make all报错.同时那个make all文件也不在grbl的第一位而是按顺序排列的,按网上的教程也弄好几天了,都是报错就是生不成.HEX文件,百忙之中帮看看?
7.jpg

回复

使用道具 举报

ID:112317 发表于 2016-4-9 23:42 | 显示全部楼层
参考
https://github.com/grbl/grbl/wiki/Compiling-Grbl

把GRBL目录放在根目录下,不要带中文,
然后,打开命令行界面,进入GRBL目录下,make clean,然后make grbl.hex 就可以了。
163354f0m6ih0lfl0h3l38.gif

如果,make clean这些命令不能识别,
那是因为 环境变量 path 没有把 这些命令的路径加入,

例如,C:\arduino-00xx\hardware\tools\avr\bin;C:\arduino-00xx\hardware\tools \avr\avr\bin;C:\arduino-00xx\hardware\tools\avr\utils\bin

*****************************
第一页的方式,也可以。确保路径没有中文,多试几次应该可以。

162001lkzmb2khqkfbbssp.jpg




回复

使用道具 举报

ID:136509 发表于 2016-8-11 12:20 | 显示全部楼层
学习了 谢谢楼主分享!! 专门注册了一个账号感谢
回复

使用道具 举报

ID:158981 发表于 2017-3-9 23:26 | 显示全部楼层
目前看不懂,太复杂了!希望以后还有机会能看懂吧!感谢楼主
回复

使用道具 举报

ID:167214 发表于 2017-3-10 10:52 | 显示全部楼层
学习学习
回复

使用道具 举报

ID:188207 发表于 2017-4-14 21:37 | 显示全部楼层
受教了。谢谢
回复

使用道具 举报

ID:151710 发表于 2017-11-10 21:02 | 显示全部楼层
感谢楼主分享,刚刚接触雕刻机,前来学习
回复

使用道具 举报

ID:252134 发表于 2017-11-22 14:25 | 显示全部楼层
大神膜拜,好东西get
回复

使用道具 举报

ID:255376 发表于 2017-12-6 22:07 | 显示全部楼层
很长,必须慢慢领会。
回复

使用道具 举报

ID:228524 发表于 2017-12-9 23:59 来自手机 | 显示全部楼层
学习了
回复

使用道具 举报

ID:280474 发表于 2018-1-30 16:34 | 显示全部楼层
感谢楼主,正好在做个写字机的项目,想用舵机控制抬笔,需要在底层代码改动
回复

使用道具 举报

ID:408496 发表于 2019-6-8 23:25 | 显示全部楼层
做个记号
回复

使用道具 举报

ID:156302 发表于 2019-6-9 13:40 | 显示全部楼层
很长,必须慢慢领会。
正好要用到了,标记一下
回复

使用道具 举报

ID:302850 发表于 2019-12-3 00:02 来自手机 | 显示全部楼层
标记一下,长文慢慢看
回复

使用道具 举报

ID:656472 发表于 2019-12-5 09:23 | 显示全部楼层
太长了,慢慢看
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表