#include "libs/ADC/adc.h"
#include "libs/Pin.h"
+// This is an interface to the mbed.org ADC library you can find in libs/ADC/adc.h
+// TODO : Having the same name is confusing, should change that
+
Adc::Adc(){
this->adc = new ADC(1000, 1);
}
+// Enables ADC on a given pin
void Adc::enable_pin(Pin* pin){
PinName pin_name = this->_pin_to_pinname(pin);
this->adc->burst(1);
this->adc->interrupt_state(pin_name,1);
}
+// Read the last value ( burst mode ) on a given pin
unsigned int Adc::read(Pin* pin){
return this->adc->read(this->_pin_to_pinname(pin));
}
+// Convert a smoothie Pin into a mBed Pin
PinName Adc::_pin_to_pinname(Pin* pin){
if( pin->port == LPC_GPIO0 && pin->pin == 23 ){
return p15;
You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
*/
-
using namespace std;
#include <vector>
#include <string>
#include "libs/ConfigSources/FileConfigSource.h"
#include "libs/ConfigSources/FirmConfigSource.h"
+// Add various config sources. Config can be fetched from several places.
+// All values are read into a cache, that is then used by modules to read their configuration
Config::Config(){
this->config_cache_loaded = false;
void Config::on_console_line_received( void* argument ){}
+// Set a value in the config cache, but not in any config source
void Config::set_string( string setting, string value ){
ConfigValue* cv = new ConfigValue;
cv->found = true;
this->kernel->call_event(ON_CONFIG_RELOAD);
}
+// Get a list of modules, used by module "pools" that look for the "enable" keyboard to find things like "moduletype.modulename.enable" as the marker of a new instance of a module
void Config::get_module_list(vector<uint16_t>* list, uint16_t family){
for( unsigned int i=1; i<this->config_cache.size(); i++){
ConfigValue* value = this->config_cache.at(i);
// Command to load config cache into buffer for multiple reads during init
void Config::config_cache_load(){
+ // First clear the cache
this->config_cache_clear();
// First element is a special empty ConfigValue for values not found
this->config_cache_loaded = false;
}
-
+// Three ways to read a value from the config, depending on adress length
ConfigValue* Config::value(uint16_t check_sum_a, uint16_t check_sum_b, uint16_t check_sum_c ){
uint16_t check_sums[3];
check_sums[0] = check_sum_a;
}
#include "Hook.h"
+// Hook is just a glorified FPointer
+
Hook::Hook(){}
#define HOOK_H
#include "libs/FPointer.h"
+// Hook is just a glorified FPointer
+
class Hook : public FPointer {
public:
Hook();
#define baud_rate_setting_checksum CHECKSUM("baud_rate")
#define uart0_checksum CHECKSUM("uart0")
-static int isDebugMonitorUsingUart0()
-{
+// This is used to configure UARTs depending on the MRI configuration, see Kernel::Kernel()
+static int isDebugMonitorUsingUart0(){
return NVIC_GetPriority(UART0_IRQn) == 0;
}
this->streams = new StreamOutputPool();
// Configure UART depending on MRI config
+ // If MRI is using UART0, we want to use UART1, otherwise, we want to use UART0. This makes it easy to use only one UART for both debug and actual commands.
NVIC_SetPriorityGrouping(0);
if( !isDebugMonitorUsingUart0() ){
this->serial = new SerialConsole(USBTX, USBRX, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(9600)->as_number());
}else{
this->serial = new SerialConsole(p13, p14, this->config->value(uart0_checksum,baud_rate_setting_checksum)->by_default(9600)->as_number());
}
- this->add_module( this->config );
+ this->add_module( this->config );
this->add_module( this->serial );
// HAL stuff
this->step_ticker = new StepTicker();
this->adc = new Adc();
+ // TODO : These should go into platform-specific files
// LPC17xx-specific
NVIC_SetPriorityGrouping(0);
NVIC_SetPriority(TIMER0_IRQn, 2);
int base_stepping_frequency = this->config->value(base_stepping_frequency_checksum )->by_default(100000)->as_number();
double microseconds_per_step_pulse = this->config->value(microseconds_per_step_pulse_checksum )->by_default(5 )->as_number();
+ // Configure the step ticker ( TODO : shouldnt this go into stepticker's code ? )
this->step_ticker->set_reset_delay( microseconds_per_step_pulse / 1000000L );
-
this->step_ticker->set_frequency( base_stepping_frequency );
// Core modules
}
+// Add a module to Kernel. We don't actually hold a list of modules, we just tell it where Kernel is
void Kernel::add_module(Module* module){
module->kernel = this;
module->on_module_loaded();
}
+// Adds a hook for a given module and event
void Kernel::register_for_event(_EVENT_ENUM id_event, Module* module){
this->hooks[id_event].push_back(module);
}
+// Call a specific event without arguments
void Kernel::call_event(_EVENT_ENUM id_event){
for (Module* current : hooks[id_event]) {
(current->*kernel_callback_functions[id_event])(this);
}
}
+// Call a specific event with an argument
void Kernel::call_event(_EVENT_ENUM id_event, void * argument){
for (Module* current : hooks[id_event]) {
(current->*kernel_callback_functions[id_event])(argument);
#include <sLPC17xx.h>
#include <mri.h>
+// This is used by MRI to turn pins on and off when entering and leaving MRI. Useful for not burning everything down
+// See http://smoothieware.org/mri-debugging
+
extern "C" {
static uint32_t _set_high_on_debug[5] = {
// (1 << 4) | (1 << 10) | (1 << 19) | (1 << 21), // smoothieboard stepper EN pins
#include "libs/Module.h"
#include "libs/Kernel.h"
+// Events are the basic building blocks of Smoothie. They register for events, and then do stuff when those events are called.
+// You add things to Smoothie by making a new class that inherits the Module class. See http://smoothieware.org/moduleexample for a crude introduction
+
const ModuleCallback kernel_callback_functions[NUMBER_OF_DEFINED_EVENTS] = {
#define EVENT(name, func) &Module::func ,
#include "Event.h"
#include <string>
using namespace std;
+// The Pauser module is the core of the pausing subsystem in smoothie. Basically we want several modules to be able to pause smoothie at the same time
+// ( think both the user with a button, and the temperature control because a temperature is not reached ). To do that, modules call the take() methode,
+// a pause event is called, and the pause does not end before all modules have called the release() method.
+// Please note : Modules should keep track of their pause status themselves
Pauser::Pauser(){}
void Pauser::on_module_loaded(){
this->counter = 0;
}
+// Pause smoothie if nobody else is currently doing so
void Pauser::take(){
this->counter++;
//this->kernel->streams->printf("take: %u \r\n", this->counter );
}
}
+// Unpause smoothie unless something else is pausing it too
void Pauser::release(){
this->counter--;
//this->kernel->streams->printf("release: %u \r\n", this->counter );
}
}
-bool Pauser::paused()
-{
+// Return wether smoothie is paused
+bool Pauser::paused(){
return (counter != 0);
}
Pin::Pin(){}
-Pin* Pin::from_string(std::string value)
-{
+// Make a new pin object from a string
+// TODO : Comment this more, how does it work ?
+// TODO : Make this able to configure pull-up, pull-down, and open-drain
+Pin* Pin::from_string(std::string value){
LPC_GPIO_TypeDef* gpios[5] ={LPC_GPIO0,LPC_GPIO1,LPC_GPIO2,LPC_GPIO3,LPC_GPIO4};
// cs is the current position in the string
char* cn = NULL;
this->port_number = strtol(cs, &cn, 10);
- if ((cn > cs) && (port_number <= 4))
- {
+ if ((cn > cs) && (port_number <= 4)){
this->port = gpios[(unsigned int) this->port_number];
- if (*cn == '.')
- {
+ if (*cn == '.'){
cs = ++cn;
this->pin = strtol(cs, &cn, 10);
- if ((cn > cs) & (pin < 32))
- {
+ if ((cn > cs) & (pin < 32)){
while (is_whitespace(*cn)) cn++;
this->inverting = (*cn == '!');
return this;
}
-Pin* Pin::as_open_drain()
-{
+// Configure this pin as OD
+Pin* Pin::as_open_drain(){
if (this->pin >= 32) return this;
if( this->port_number == 0 ){ LPC_PINCON->PINMODE_OD0 |= (1<<this->pin); }
if( this->port_number == 1 ){ LPC_PINCON->PINMODE_OD1 |= (1<<this->pin); }
return this;
}
-Pin* Pin::pull_up()
-{
+// Configure this pin as a pullup
+Pin* Pin::pull_up(){
if (this->pin >= 32) return this;
// Set the two bits for this pin as 00
if( this->port_number == 0 && this->pin < 16 ){ LPC_PINCON->PINMODE0 &= ~(3<<( this->pin *2)); }
return this;
}
-Pin* Pin::pull_down()
-{
+// Configure this pin as a pulldown
+Pin* Pin::pull_down(){
if (this->pin >= 32) return this;
// Set the two bits for this pin as 11
if( this->port_number == 0 && this->pin < 16 ){ LPC_PINCON->PINMODE0 |= (3<<( this->pin *2)); }
#define PID_PWM_MAX 256
+// What ?
+
Pwm::Pwm()
{
_max = PID_PWM_MAX - 1;
#include <mri.h>
+// This module uses a Timer to periodically call hooks
+// Modules register with a function ( callback ) and a frequency, and we then call that function at the given frequency.
+
SlowTicker* global_slow_ticker;
SlowTicker::SlowTicker(){
max_frequency = 0;
global_slow_ticker = this;
+
+ // Configure the actual timer
LPC_SC->PCONP |= (1 << 22); // Power Ticker ON
- LPC_TIM2->MR0 = 10000; // Initial dummy value for Match Register
+ LPC_TIM2->MR0 = 10000; // Initial dummy value for Match Register
LPC_TIM2->MCR = 3; // Match on MR0, reset on MR0
LPC_TIM2->TCR = 1; // Enable interrupt
NVIC_EnableIRQ(TIMER2_IRQn); // Enable interrupt handler
+ // ISP button
ispbtn.from_string("2.10")->as_input()->pull_up();
+ // TODO: What is this ??
flag_1s_flag = 0;
flag_1s_count = SystemCoreClock;
register_for_event(ON_GCODE_EXECUTE);
}
+// Set the base frequency we use for all sub-frequencies
void SlowTicker::set_frequency( int frequency ){
this->interval = (SystemCoreClock >> 2) / frequency; // SystemCoreClock/4 = Timer increments in a second
LPC_TIM2->MR0 = this->interval;
LPC_TIM2->TCR = 1; // Reset
}
+// The actual interrupt being called by the timer, this is where work is done
void SlowTicker::tick(){
- _isr_context = true;
-
+
+ // Call all hooks that need to be called ( bresenham )
for (uint32_t i=0; i<this->hooks.size(); i++){
Hook* hook = this->hooks.at(i);
hook->countdown -= this->interval;
g4_ticks = 0;
}
- LPC_GPIO1->FIOCLR = 1<<20;
-
+ // Enter MRI mode if the ISP button is pressed
+ // TODO : This should have it's own module
if (ispbtn.get() == 0)
__debugbreak();
- _isr_context = false;
}
bool SlowTicker::flag_1s(){
}
}
+// When a G4-type gcode is received, add it to the queue so we can execute it in time
void SlowTicker::on_gcode_received(void* argument){
Gcode* gcode = static_cast<Gcode*>(argument);
// Add the gcode to the queue ourselves if we need it
}
}
}
-
+
+// When a G4-type gcode is executed, start the pause
void SlowTicker::on_gcode_execute(void* argument){
Gcode* gcode = static_cast<Gcode*>(argument);
- if (gcode->has_g)
- {
- if (gcode->g == 4)
- {
+ if (gcode->has_g){
+ if (gcode->g == 4){
bool updated = false;
if (gcode->has_letter('P')) {
updated = true;
updated = true;
g4_ticks += gcode->get_int('S') * (SystemCoreClock >> 2);
}
- if (updated)
- {
+ if (updated){
// G4 Smm Pnn should pause for mm seconds + nn milliseconds
// at 120MHz core clock, the longest possible delay is (2^32 / (120MHz / 4)) = 143 seconds
- if (!g4_pause)
- {
+ if (!g4_pause){
g4_pause = true;
kernel->pauser->take();
}
// They then do Bresenham stuff themselves
StepTicker* global_step_ticker;
-int debug_counter;
StepTicker::StepTicker(){
global_step_ticker = this;
+
+ // Configure the timer
LPC_TIM0->MR0 = 10000000; // Initial dummy value for Match Register
LPC_TIM0->MCR = 3; // Match on MR0, reset on MR0, match on MR1
LPC_TIM0->TCR = 0; // Disable interrupt
}
this->active_motor_bm = 0;
- debug_counter = 0;
-
NVIC_EnableIRQ(TIMER0_IRQn); // Enable interrupt handler
NVIC_EnableIRQ(TIMER1_IRQn); // Enable interrupt handler
}
global_step_ticker->reset_tick();
}
-
-//#pragma GCC push_options
-//#pragma GCC optimize ("O0")
-
-
// The actual interrupt handler where we do all the work
extern "C" void TIMER0_IRQHandler (void){
LPC_TIM0->IR |= 1 << 0;
// Step pins
- //global_step_ticker->tick();
- _isr_context = true;
uint16_t bitmask = 1;
for (uint8_t motor = 0; motor < 12; motor++, bitmask <<= 1){
if (global_step_ticker->active_motor_bm & bitmask){
global_step_ticker->active_motors[motor]->tick();
}
}
- _isr_context = false;
// We may have set a pin on in this tick, now we start the timer to set it off
if( global_step_ticker->reset_step_pins ){
}
-//#pragma GCC pop_options
-
// We make a list of steppers that want to be called so that we don't call them for nothing
void StepTicker::add_motor_to_active_list(StepperMotor* motor)
{
#include "StepperMotor.h"
#include "MRI_Hooks.h"
+// A StepperMotor represents an actual stepper motor. It is used to generate steps that move the actual motor at a given speed
+// TODO : Abstract this into Actuator
+
StepperMotor::StepperMotor(){
this->moving = false;
this->paused = false;
set_high_on_debug(en->port_number, en->pin);
}
+// This is called ( see the .h file, we had to put a part of things there for obscure inline reasons ) when a step has to be generated
+// we also here check if the move is finished etc ...
void StepperMotor::step(){
-
// output to pins 37t
this->step_pin->set( 1 );
this->step_ticker->reset_step_pins = true;
this->step_signal_hook->call();
}
- // is this move finished ? 11t
+ // Is this move finished ?
if( this->stepped == this->steps_to_move ){
+ // Mark it as finished, then StepTicker will call signal_mode_finished()
+ // This is so we don't call that before all the steps have been generated for this tick()
this->is_move_finished = true;
this->step_ticker->moves_finished = true;
}
-
}
}
-//#pragma GCC push_options
-//#pragma GCC optimize ("O0")
-
// Set the speed at which this steper moves
void StepperMotor::set_speed( double speed ){
}
-//#pragma GCC pop_options
-
+// Pause this stepper motor
void StepperMotor::pause(){
this->paused = true;
this->update_exit_tick();
}
+// Unpause this stepper motor
void StepperMotor::unpause(){
this->paused = false;
this->update_exit_tick();
#include <cstdarg>
#include <cstring>
+// This is a base class for all StreamOutput objects.
+// StreamOutputs are basically "things you can sent strings to". They are passed along with gcodes for example so modules can answer to those gcodes.
+// They are usually associated with a command source, but can also be a NullStreamOutput if we just want to ignore whatever is sent
+
class NullStreamOutput;
class StreamOutput : public mbed::Stream {
#include <mri.h>
+// TODO : comment this
+// Basically, when stuff stop answering, reset, or enter MRI mode, or something
+
Watchdog::Watchdog(uint32_t timeout, WDT_ACTION action)
{
WDT_Init(WDT_CLKSRC_IRC, (action == WDT_MRI)?WDT_MODE_INT_ONLY:WDT_MODE_RESET);