ARM: implement heater-arm.c partially.

Currently at a fixed frequency of 1 kHz and with a fixed duty
cycle of 10%, but PWM does work.

As it turns out, PIO0_11 is not usable for PWM, because its timer
is already in use for the Step timer, and had to be disabled for
Gen7-ARM.

Test: define a heater in board.gen7-arm.h and a square signal
of 1 kHz with 10% duty cycle should appear on the heater pin.
This commit is contained in:
Markus Hitter 2015-08-08 14:06:47 +02:00
parent ee43a86474
commit d7b59e2d33
9 changed files with 197 additions and 20 deletions

View File

@ -100,10 +100,10 @@ TARGET = $(PROGRAM).hex
SOURCES = mendel.c cpu.c serial.c sermsg.c sersendf.c delay.c SOURCES = mendel.c cpu.c serial.c sermsg.c sersendf.c delay.c
SOURCES += gcode_parse.c gcode_process.c pinio.c timer.c clock.c SOURCES += gcode_parse.c gcode_process.c pinio.c timer.c clock.c
SOURCES += dda_queue.c dda_maths.c dda_kinematics.c dda.c dda_lookahead.c SOURCES += dda_queue.c dda_maths.c dda_kinematics.c dda.c dda_lookahead.c
SOURCES += analog.c temp.c SOURCES += analog.c temp.c heater.c
# Sources left: # Sources left:
# home.c crc.c intercom.c debug.c spi.c usb_serial.c # home.c crc.c intercom.c debug.c spi.c usb_serial.c
# graycode.c pff.c watchdog.c heater.c pff_diskio.c # graycode.c pff.c watchdog.c pff_diskio.c
ifeq ($(MCU), lpc1114) ifeq ($(MCU), lpc1114)
SOURCES += cmsis-system_lpc11xx.c SOURCES += cmsis-system_lpc11xx.c

View File

@ -95,6 +95,7 @@
#define PIO0_1_PIN 1 #define PIO0_1_PIN 1
#define PIO0_1_PORT LPC_GPIO0 #define PIO0_1_PORT LPC_GPIO0
#define PIO0_1_OUTPUT 0x00 #define PIO0_1_OUTPUT 0x00
// Timer pin CT32B0_MAT2, but timer used for Step interrupt.
#define PIO0_2_CMSIS PIO0_2 #define PIO0_2_CMSIS PIO0_2
#define PIO0_2_PIN 2 #define PIO0_2_PIN 2
@ -130,22 +131,32 @@
#define PIO0_8_PIN 8 #define PIO0_8_PIN 8
#define PIO0_8_PORT LPC_GPIO0 #define PIO0_8_PORT LPC_GPIO0
#define PIO0_8_OUTPUT 0x00 #define PIO0_8_OUTPUT 0x00
#define PIO0_8_TIMER LPC_TMR16B0
#define PIO0_8_MATCH 1
#define PIO0_8_PWM (0x02 << 0)
#define PIO0_9_CMSIS PIO0_9 #define PIO0_9_CMSIS PIO0_9
#define PIO0_9_PIN 9 #define PIO0_9_PIN 9
#define PIO0_9_PORT LPC_GPIO0 #define PIO0_9_PORT LPC_GPIO0
#define PIO0_9_OUTPUT 0x00 #define PIO0_9_OUTPUT 0x00
#define PIO0_9_TIMER LPC_TMR16B0
#define PIO0_9_MATCH 1
#define PIO0_9_PWM (0x02 << 0)
#define PIO0_10_CMSIS SWCLK_PIO0_10 #define PIO0_10_CMSIS SWCLK_PIO0_10
#define PIO0_10_PIN 10 #define PIO0_10_PIN 10
#define PIO0_10_PORT LPC_GPIO0 #define PIO0_10_PORT LPC_GPIO0
#define PIO0_10_OUTPUT (0x01 << 0) #define PIO0_10_OUTPUT (0x01 << 0)
#define PIO0_10_TIMER LPC_TMR16B0
#define PIO0_10_MATCH 2
#define PIO0_10_PWM (0x03 << 0)
#define PIO0_11_CMSIS R_PIO0_11 #define PIO0_11_CMSIS R_PIO0_11
#define PIO0_11_PIN 11 #define PIO0_11_PIN 11
#define PIO0_11_PORT LPC_GPIO0 #define PIO0_11_PORT LPC_GPIO0
#define PIO0_11_OUTPUT ((0x01 << 0) | (0x01 << 7)) #define PIO0_11_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO0_11_ADC 0 #define PIO0_11_ADC 0
// Timer pin CT32B0_MAT3, but timer used for Step interrupt.
#define PIO1_0_CMSIS R_PIO1_0 #define PIO1_0_CMSIS R_PIO1_0
#define PIO1_0_PIN 0 #define PIO1_0_PIN 0
@ -158,24 +169,34 @@
#define PIO1_1_PORT LPC_GPIO1 #define PIO1_1_PORT LPC_GPIO1
#define PIO1_1_OUTPUT ((0x01 << 0) | (0x01 << 7)) #define PIO1_1_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_1_ADC 2 #define PIO1_1_ADC 2
#define PIO1_1_TIMER LPC_TMR32B1
#define PIO1_1_MATCH 0
#define PIO1_1_PWM (0x03 << 0)
#define PIO1_2_CMSIS R_PIO1_2 #define PIO1_2_CMSIS R_PIO1_2
#define PIO1_2_PIN 2 #define PIO1_2_PIN 2
#define PIO1_2_PORT LPC_GPIO1 #define PIO1_2_PORT LPC_GPIO1
#define PIO1_2_OUTPUT ((0x01 << 0) | (0x01 << 7)) #define PIO1_2_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_2_ADC 3 #define PIO1_2_ADC 3
#define PIO1_2_TIMER LPC_TMR32B1
#define PIO1_2_MATCH 1
#define PIO1_2_PWM (0x03 << 0)
#define PIO1_3_CMSIS SWDIO_PIO1_3 #define PIO1_3_CMSIS SWDIO_PIO1_3
#define PIO1_3_PIN 3 #define PIO1_3_PIN 3
#define PIO1_3_PORT LPC_GPIO1 #define PIO1_3_PORT LPC_GPIO1
#define PIO1_3_OUTPUT ((0x01 << 0) | (0x01 << 7)) #define PIO1_3_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_3_ADC 4 #define PIO1_3_ADC 4
#define PIO1_3_TIMER LPC_TMR32B1
#define PIO1_3_MATCH 2
#define PIO1_3_PWM (0x03 << 0)
#define PIO1_4_CMSIS PIO1_4 #define PIO1_4_CMSIS PIO1_4
#define PIO1_4_PIN 4 #define PIO1_4_PIN 4
#define PIO1_4_PORT LPC_GPIO1 #define PIO1_4_PORT LPC_GPIO1
#define PIO1_4_OUTPUT (0x01 << 7) #define PIO1_4_OUTPUT (0x01 << 7)
#define PIO1_4_ADC 5 #define PIO1_4_ADC 5
// Timer pin CT32B1_MAT3, but match used for PWM reset.
#define PIO1_5_CMSIS PIO1_5 #define PIO1_5_CMSIS PIO1_5
#define PIO1_5_PIN 5 #define PIO1_5_PIN 5
@ -186,11 +207,13 @@
#define PIO1_6_PIN 6 #define PIO1_6_PIN 6
#define PIO1_6_PORT LPC_GPIO1 #define PIO1_6_PORT LPC_GPIO1
#define PIO1_6_OUTPUT 0x00 #define PIO1_6_OUTPUT 0x00
// Timer pin CT32B0_MAT0, but timer used for Step interrupt.
#define PIO1_7_CMSIS PIO1_7 #define PIO1_7_CMSIS PIO1_7
#define PIO1_7_PIN 7 #define PIO1_7_PIN 7
#define PIO1_7_PORT LPC_GPIO1 #define PIO1_7_PORT LPC_GPIO1
#define PIO1_7_OUTPUT 0x00 #define PIO1_7_OUTPUT 0x00
// Timer pin CT32B0_MAT1, but timer used for Step interrupt.
#define PIO1_8_CMSIS PIO1_8 #define PIO1_8_CMSIS PIO1_8
#define PIO1_8_PIN 8 #define PIO1_8_PIN 8
@ -201,3 +224,6 @@
#define PIO1_9_PIN 9 #define PIO1_9_PIN 9
#define PIO1_9_PORT LPC_GPIO1 #define PIO1_9_PORT LPC_GPIO1
#define PIO1_9_OUTPUT 0x00 #define PIO1_9_OUTPUT 0x00
#define PIO1_9_TIMER LPC_TMR16B1
#define PIO1_9_MATCH 0
#define PIO1_9_PWM (0x01 << 0)

View File

@ -71,11 +71,8 @@ void clock_tick(void) {
called from clock_10ms(), do not call directly called from clock_10ms(), do not call directly
*/ */
static void clock_250ms(void) { static void clock_250ms(void) {
#ifndef __ARMEL_NOTYET__
if (heaters_all_zero()) { if (heaters_all_zero()) {
#else
if (1) {
#endif /* __ARMEL_NOTYET__, ! __ARMEL_NOTYET__ */
if (psu_timeout > (30 * 4)) { if (psu_timeout > (30 * 4)) {
power_off(); power_off();
} }

View File

@ -163,8 +163,8 @@
*/ */
//DEFINE_TEMP_SENSORS_START //DEFINE_TEMP_SENSORS_START
// name type pin additional // name type pin additional
DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, PIO1_1,THERMISTOR_EXTRUDER) DEFINE_TEMP_SENSOR(extruder, TT_THERMISTOR, PIO1_0,THERMISTOR_EXTRUDER)
DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, PIO1_0,THERMISTOR_BED) //DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, PIO1_1,THERMISTOR_BED)
// Beta algorithm r0 beta r2 vadc // Beta algorithm r0 beta r2 vadc
// Steinhart-Hart rp t0 r0 t1 r1 t2 r2 // Steinhart-Hart rp t0 r0 t1 r1 t2 r2
@ -220,11 +220,11 @@ DEFINE_TEMP_SENSOR(bed, TT_THERMISTOR, PIO1_0,THERMISTOR_BED)
*/ */
//DEFINE_HEATERS_START //DEFINE_HEATERS_START
// name port pwm // name port pwm
DEFINE_HEATER(extruder, PIO0_11, 1) DEFINE_HEATER(extruder, PIO0_10, 1)
DEFINE_HEATER(bed, PIO0_10, 1) //DEFINE_HEATER(bed, PIO0_11, 1)
#define HEATER_EXTRUDER HEATER_extruder #define HEATER_EXTRUDER HEATER_extruder
#define HEATER_BED HEATER_bed //#define HEATER_BED HEATER_bed
//DEFINE_HEATERS_END //DEFINE_HEATERS_END

View File

@ -42,3 +42,32 @@
Lookahead disabled. Lookahead disabled.
#undef LOOKAHEAD #undef LOOKAHEAD
#endif #endif
/**
Silently discard EECONFIG on ARM. Silently to not disturb regression tests.
TODO:
- EECONFIG is currently misplaced as a printer property. Move EECONFIG to
the board configuration or drop it entirely in favour of PID settings in
Configtool.
- Remove this silent discard in favour of the #error in heater-arm.c.
*/
#if defined __ARMEL__ && defined EECONFIG
#undef EECONFIG
#endif
/**
Silently discard BANG_BANG on ARM. Silently to not disturb regression tests.
TODO:
- BANG_BANG is currently misplaced as a printer property. Move BANG_BANG to
the board configuration.
- Remove this silent discard in favour of the #error in heater-arm.c.
*/
#if defined __ARMEL__ && defined BANG_BANG
#undef BANG_BANG
#endif

View File

@ -724,7 +724,6 @@ void process_gcode_command() {
serial_writechar('\n'); serial_writechar('\n');
break; break;
#ifndef __ARMEL_NOTYET__
#ifdef EECONFIG #ifdef EECONFIG
case 130: case 130:
//? --- M130: heater P factor --- //? --- M130: heater P factor ---
@ -787,7 +786,6 @@ void process_gcode_command() {
heater_save_settings(); heater_save_settings();
break; break;
#endif /* EECONFIG */ #endif /* EECONFIG */
#endif /* __ARMEL_NOTYET__ */
#ifdef DEBUG #ifdef DEBUG
case 136: case 136:
@ -800,9 +798,7 @@ void process_gcode_command() {
#else #else
next_target.P = 0; next_target.P = 0;
#endif #endif
#ifndef __ARMEL_NOTYET__
heater_print(next_target.P); heater_print(next_target.P);
#endif /* __ARMEL_NOTYET__ */
break; break;
#endif /* DEBUG */ #endif /* DEBUG */

132
heater-arm.c Normal file
View File

@ -0,0 +1,132 @@
/** \file
\brief Manage heaters, including PID and PWM, ARM specific part.
*/
#if defined TEACUP_C_INCLUDE && defined __ARMEL__
/**
Test configuration.
*/
#ifdef EECONFIG
#error EEPROM handling (EECONFIG) not yet supported on ARM.
#endif
#ifdef BANG_BANG
#error BANG_BANG not supported on ARM. You may set PWM frequency of one \
or all heater(s) to zero, which gives similar, better behaviour.
#endif
/** \def PWM_SCALE
G-code standard (if such a thing exists at all) gives a heater setting
range between 0 (off) and 255 (full on), so we let the PWM timers count up
to 255. Doing so allows to set the prescaler for these frequencies (all on
a 48 MHz CPU clock):
prescaler frequency prescaler frequency
0 188.2 kHz 4 37.6 kHz
1 91.1 kHz 5 31.4 kHz
2 62.7 kHz ... ...
3 47.0 kHz 65535 2.87 Hz
As one can see, frequency steps are rather coarse on the high end and
become finer grained the lower it gets.
If this range is generally too high for your purposes, you can set PWM_SCALE
to multiples of 255 to lower the range. Doubling it to 510 moves the
frequency range to 1.4 Hz...91.1 kHz, quadrupling it to 1020 moves the range
to 0.7 Hz...46.9 kHz and so on. The highest allowed number is 65535.
That said, code below calculates the best prescaler value for a configured
frequency, so you should bother about PWM_SCALE only of you need frequencies
below 3 Hz.
*/
#define PWM_SCALE 255
/** Initialise heater subsystem.
Initialise PWM timers, etc. Inspired by pwm.c in LPC1343CodeBase:
https://github.com/microbuilder/LPC1343CodeBase
Note that PWM is inversed, pins start at Low by chip design. When the pin's
counter is reached, they're set to High. Reaching the timer reset counter is
programmed to reset everything and start over. Thus, having both counter
matches similar gives a low duty, having the pin counter match zero gives
full on.
For simplicity we reset all timer counters always on Match 3 and always
at PWM_SCALE (255 per default), so setting a pin match to PWM_SCALE / 2
gives 50% duty, setting it to PWM_SCALE gives full off. This choice disallows
using a pin connected to a Match 3, working around this would make code much
more complicated (and still not allow to use more than 3 pins per timer).
On ARM we can define a PWM frequency pretty fine grained, so we take the
'pwm' value of DEFINE_HEATER() not only wether to use PWM at all, but also
to define the PWM frequency. Float values are allowed.
If there's more than one pin on a timer, they share the same PWM frequency;
the frequency choosen is the one of the pin defined last.
*/
void heater_init() {
/**
Pins on the LPC1114 are usable as following, capture pins not listed:
pin timer/match func for PWM other uses
PIO0_1 CT32B0_MAT2 0x2 ---
PIO0_8 CT16B0_MAT0 0x2 MISO0
PIO0_9 CT16B0_MAT1 0x2 MOSI0
PIO0_10 CT16B0_MAT2 0x3 SCK0 (also on PIO0_6)
PIO0_11 CT32B0_MAT3 0x3 AD0, Step timer
PIO1_1 CT32B1_MAT0 0x3 AD2
PIO1_2 CT32B1_MAT1 0x3 AD3
PIO1_3 CT32B1_MAT2 0x3 AD4
PIO1_4 CT32B1_MAT3 0x2 AD5, PWM reset
PIO1_6 CT32B0_MAT0 0x2 RXD, Step timer
PIO1_7 CT32B0_MAT1 0x2 TXD, Step timer
PIO1_9 CT16B1_MAT0 0x1 ---
*/
if (NUM_HEATERS) { // At least one channel in use.
// For simplicity, turn on all timers, not only the used ones.
// TODO: turn on only the used ones. we know pin ## TIMER.
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 7); // Turn on CT16B0 power.
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 8); // Turn on CT16B1 power.
LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 10); // Turn on CT32B1 power.
// Auto-generate pin setup.
#undef DEFINE_HEATER
#define DEFINE_HEATER(name, pin, pwm) \
LPC_IOCON->pin ## _CMSIS = pin ## _PWM; /* Connect to timer. */ \
/*pin ## _TIMER->IR = 0; ( = reset value) No interrupts. */ \
pin ## _TIMER->TCR = (1 << 0); /* Enable counter. */ \
/* TODO: set PWM frequency here according to configuration. */ \
/* TODO: check wether configured freq. is within range (1..65536). */ \
pin ## _TIMER->PR = /* Prescaler to */ \
F_CPU / PWM_SCALE / 1000 - 1; /* 1000 Hz. */ \
pin ## _TIMER->MCR = (1 << 10); /* Reset on Match 3. */ \
/* PWM_SCALE - 1, so match = 255 is full off. */ \
pin ## _TIMER->MR[3] = PWM_SCALE - 1; /* Match 3 at 254. */ \
/* TODO: Heaters should start at zero, of course. 10% is as demo. */ \
pin ## _TIMER->MR[pin ## _MATCH] = /* Match pin = duty. */ \
(PWM_SCALE * 0.9); \
/*pin ## _TIMER->CCR = 0; ( = reset value) No pin capture. */ \
pin ## _TIMER->EMR |= ((1 << pin ## _MATCH) /* Connect to pin. */ \
| (0x03 << ((pin ## _MATCH * 2) + 4))); /* Toggle pin on match.*/ \
/*pin ## _TIMER->CTCR = 0; ( = reset value) Timer mode. */ \
pin ## _TIMER->PWMC |= ((1 << 3) /* 3 to PWM mode. */ \
| (1 << pin ## _MATCH)); /* Pin to PWM mode. */
#include "config_wrapper.h"
#undef DEFINE_HEATER
} /* NUM_HEATERS */
#if 0
pid_init(i);
#endif /* 0 */
}
#endif /* defined TEACUP_C_INCLUDE && defined __ARMEL__ */

View File

@ -7,7 +7,7 @@
#define TEACUP_C_INCLUDE #define TEACUP_C_INCLUDE
#include "heater-avr.c" #include "heater-avr.c"
//#include "heater-arm.c" #include "heater-arm.c"
#undef TEACUP_C_INCLUDE #undef TEACUP_C_INCLUDE
#include <stdlib.h> #include <stdlib.h>

View File

@ -38,8 +38,8 @@
#ifndef __ARMEL_NOTYET__ #ifndef __ARMEL_NOTYET__
#include "watchdog.h" #include "watchdog.h"
#include "debug.h" #include "debug.h"
#include "heater.h"
#endif /* __ARMEL_NOTYET__ */ #endif /* __ARMEL_NOTYET__ */
#include "heater.h"
#include "analog.h" #include "analog.h"
#include "pinio.h" #include "pinio.h"
#include "clock.h" #include "clock.h"
@ -97,10 +97,7 @@ void init(void) {
// set up timers // set up timers
timer_init(); timer_init();
#ifndef __ARMEL_NOTYET__
// read PID settings from EEPROM
heater_init(); heater_init();
#endif /* __ARMEL_NOTYET__ */
// set up dda // set up dda
dda_init(); dda_init();