久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 39974|回復: 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 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 激情欧美一区二区三区中文字幕 | 午夜欧美一区二区三区在线播放 | 日韩影院一区 | 国产精品一区二区视频 | 国产精品久久久久av | 国产成在线观看免费视频 | 中文字幕日韩一区二区 | 一区二区三区电影在线观看 | 亚洲综合激情 | 激情网五月天 | 欧美日韩一区二区视频在线观看 | 成人精品一区二区三区中文字幕 | av国产在线观看 | 久久国产精品99久久久大便 | 性国产丰满麻豆videosex | 久久久久国产一区二区三区不卡 | 久久免费精品 | 在线激情视频 | 91精品国产91久久久久久吃药 | 日韩成人在线观看 | 免费在线精品视频 | 久久久免费电影 | 久久国产综合 | 久久精品国产v日韩v亚洲 | 99re在线视频观看 | com.国产| 国产精品成人一区二区三区夜夜夜 | 久久99精品视频 | 久久中文字幕一区 | 精品欧美乱码久久久久久1区2区 | 一区在线视频 | 日韩午夜一区二区三区 | 超碰免费在线观看 | 久久精品免费看 | re久久 | 中文字幕国产第一页 | 亚洲欧美精品国产一级在线 | 日韩高清一区 | 日韩精品一区二区在线观看 | 国产成人精品久久二区二区 | 亚洲精品2区 |