#define HEATSINK_FAN_PIN PA15 /**< pin to switch the nMOS to control the fan cooling the bad heatsink, low to disable, pulled up externally, must be 5V tolerant */
#define heatsink_fan_on() gpio_set(GPIO_PORT(HEATSINK_FAN_PIN), GPIO_PIN(HEATSINK_FAN_PIN)) /**< switch fan on, cooling the bed heat sink */
#define heatsink_fan_off() gpio_clear(GPIO_PORT(HEATSINK_FAN_PIN), GPIO_PIN(HEATSINK_FAN_PIN)) /**< switch fan off, when the bed is not used */
staticboolled_power_blink=false;/**< remember we are blinking the power LED */
staticboolled_heat_blink=false;/**< remember we are blinking the orange play/pause LED */
staticboolled_cool_blink=false;/**< remember we are blinking the green play/pause LED */
constuint8_tchannels[]={ADC_CHANNEL17,ADC_CHANNEL(LID_TEC_CHANNEL)};/**< voltages to convert (channel 17 = internal voltage reference) */
staticboolds18b20_present=false;/**< if DS18B20 temperature sensor is present */
/** target temperature to be reached by the lid */
rcc_periph_clock_enable(GPIO_RCC(LID_HEATER_PIN));// enable clock for GPIO port peripheral
gpio_set_mode(GPIO_PORT(LID_HEATER_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_OPENDRAIN,GPIO_PIN(LID_HEATER_PIN));// set pin back as output open-drain
gpio_set(GPIO_PORT(LID_HEATER_PIN),GPIO_PIN(LID_HEATER_PIN));// don't sink current, not powering the opto-coupler and triac
heatsink_fan_off();// bed is not active, so we can stop the fan, since we want to stop drawing power and having spinning things
led_heat_blink=false;// stop blinking LED
led_heat_off();// switch off LED
led_cool_blink=false;// stop blinking LED
led_cool_off();// switch off LED
oled_text_clear();// clear screen
oled_text_line("safe",0);// indicate state
oled_text_update();// display text
if(error){
led_power_blink=false;// start blinking red LED to indicate error
printf("device serial: %08x%08x%04x%04x\n",DESIG_UNIQUE_ID2,DESIG_UNIQUE_ID1,DESIG_UNIQUE_ID0&0xffff,DESIG_UNIQUE_ID0>>16);// not that the half-works are reversed in the first word
rtc_auto_awake(RCC_HSE,8000000/128/RTC_TICKS_SECOND-1);// use High Speed External oscillator (8 MHz / 128) as RTC clock (VBAT can't be used to keep the RTC running)
rcc_periph_clock_enable(GPIO_RCC(CONTROL_PLAY_GREEN_LED_PIN));// enable clock for GPIO port peripheral
gpio_clear(GPIO_PORT(CONTROL_PLAY_GREEN_LED_PIN),GPIO_PIN(CONTROL_PLAY_GREEN_LED_PIN));// switch LED off
gpio_set_mode(GPIO_PORT(CONTROL_PLAY_GREEN_LED_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_PUSHPULL,GPIO_PIN(CONTROL_PLAY_GREEN_LED_PIN));// set pin as output push-pull to be able to power LED
rcc_periph_clock_enable(GPIO_RCC(CONTROL_PLAY_ORANGE_LED_PIN));// enable clock for GPIO port peripheral
gpio_clear(GPIO_PORT(CONTROL_PLAY_ORANGE_LED_PIN),GPIO_PIN(CONTROL_PLAY_ORANGE_LED_PIN));// switch LED off
gpio_set_mode(GPIO_PORT(CONTROL_PLAY_ORANGE_LED_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_PUSHPULL,GPIO_PIN(CONTROL_PLAY_ORANGE_LED_PIN));// set pin as output push-pull to be able to power LED
rcc_periph_clock_enable(GPIO_RCC(CONTROL_POWER_RED_LED_PIN));// enable clock for GPIO port peripheral
gpio_clear(GPIO_PORT(CONTROL_POWER_RED_LED_PIN),GPIO_PIN(CONTROL_POWER_RED_LED_PIN));// switch LED off
gpio_set_mode(GPIO_PORT(CONTROL_POWER_RED_LED_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_PUSHPULL,GPIO_PIN(CONTROL_POWER_RED_LED_PIN));// set pin as output push-pull to be able to power LED
// play/pause button is configured by global
puts("OK\n");
puts("setup heating bed pins: ");
rcc_periph_clock_enable(GPIO_RCC(BED_PIN_393A));// enable clock for GPIO port peripheral
gpio_set_mode(GPIO_PORT(BED_PIN_393A),GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO_PIN(BED_PIN_393A));// set pin to input to read state
rcc_periph_clock_enable(GPIO_RCC(BED_PIN_3393));// enable clock for GPIO port peripheral
gpio_set_mode(GPIO_PORT(BED_PIN_3393),GPIO_MODE_INPUT,GPIO_CNF_INPUT_FLOAT,GPIO_PIN(BED_PIN_3393));// set pin to input to read state
rcc_periph_clock_enable(GPIO_RCC(BED_PIN_LK1));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(BED_PIN_LK1),GPIO_PIN(BED_PIN_LK1));// pull up
gpio_set_mode(GPIO_PORT(BED_PIN_LK1),GPIO_MODE_INPUT,GPIO_CNF_INPUT_PULL_UPDOWN,GPIO_PIN(BED_PIN_LK1));// set pin to input to read state
rcc_periph_clock_enable(GPIO_RCC(BED_PIN_LK2));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(BED_PIN_LK2),GPIO_PIN(BED_PIN_LK2));// pull up
gpio_set_mode(GPIO_PORT(BED_PIN_LK2),GPIO_MODE_INPUT,GPIO_CNF_INPUT_PULL_UPDOWN,GPIO_PIN(BED_PIN_LK2));// set pin to input to read state
rcc_periph_clock_enable(GPIO_RCC(BED_PIN_LK3));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(BED_PIN_LK3),GPIO_PIN(BED_PIN_LK3));// pull up
gpio_set_mode(GPIO_PORT(BED_PIN_LK3),GPIO_MODE_INPUT,GPIO_CNF_INPUT_PULL_UPDOWN,GPIO_PIN(BED_PIN_LK3));// set pin to input to read state
rcc_periph_clock_enable(GPIO_RCC(BED_PIN_LK4));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(BED_PIN_LK4),GPIO_PIN(BED_PIN_LK4));// pull up
gpio_set_mode(GPIO_PORT(BED_PIN_LK4),GPIO_MODE_INPUT,GPIO_CNF_INPUT_PULL_UPDOWN,GPIO_PIN(BED_PIN_LK4));// set pin to input to read state
if(gpio_get(GPIO_PORT(BED_PIN_LK1),GPIO_PIN(BED_PIN_LK1))&&gpio_get(GPIO_PORT(BED_PIN_LK2),GPIO_PIN(BED_PIN_LK2))&&gpio_get(GPIO_PORT(BED_PIN_LK3),GPIO_PIN(BED_PIN_LK3))&&gpio_get(GPIO_PORT(BED_PIN_LK4),GPIO_PIN(BED_PIN_LK4))){// nothing is connected
error="heating bed board not connected";// set error
puts("KO\n");
}elseif(gpio_get(GPIO_PORT(BED_PIN_LK1),GPIO_PIN(BED_PIN_LK1))&&gpio_get(GPIO_PORT(BED_PIN_LK2),GPIO_PIN(BED_PIN_LK2))&&!gpio_get(GPIO_PORT(BED_PIN_LK3),GPIO_PIN(BED_PIN_LK3))&&!gpio_get(GPIO_PORT(BED_PIN_LK4),GPIO_PIN(BED_PIN_LK4))){// the LK jumper setting is correct
puts("OK\n");
}else{// the jumper setting is unknown
error="not heating bed board detected";// set error
puts("KO\n");
}
puts("setup MAX1247 to read bed thermistors: ");
sensor_max1247_setup();// setup communication with MAX1247 ADC
puts("OK\n");
puts("setup ADC to read lid thermistor: ");
rcc_periph_clock_enable(RCC_ADC1);// enable clock for ADC domain
adc_power_off(ADC1);// switch off ADC while configuring it
adc_set_right_aligned(ADC1);// ensure it is right aligned to get the actual value in the 16-bit register
adc_disable_scan_mode(ADC1);// ensure scan mode is disabled
adc_enable_discontinuous_mode_regular(ADC1,1);// use discontinuous mode (to go through all channels of the group, one after another)
adc_set_single_conversion_mode(ADC1);// ensure continuous mode is not used (that's not the same as discontinuous)
adc_set_sample_time_on_all_channels(ADC1,ADC_SMPR_SMP_239DOT5CYC);// use 239.5 cycles to sample (17.1 us are required for the internal voltage reference, (239.5 + 12.5) cycles @ 14 MHz max = 18 us)
adc_set_regular_sequence(ADC1,LENGTH(channels),(uint8_t*)channels);// set channel to convert
adc_enable_external_trigger_regular(ADC1,ADC_CR2_EXTSEL_SWSTART);// use software trigger to start the conversion (of the regular group)
adc_enable_temperature_sensor();// enable internal voltage reference
adc_power_on(ADC1);// switch on ADC
sleep_us(1);// wait t_stab for the ADC to stabilize
adc_calibrate(ADC1);// calibrate ADC for less accuracy errors
rcc_periph_clock_enable(RCC_ADC12_IN(LID_TEC_CHANNEL));// enable clock for GPIO domain for lid thermistor channel
gpio_set_mode(ADC12_IN_PORT(LID_TEC_CHANNEL),GPIO_MODE_INPUT,GPIO_CNF_INPUT_ANALOG,ADC12_IN_PIN(LID_TEC_CHANNEL));// set lid thermistor channel as analogue input for the ADC
puts("OK\n");
puts("setup lid heater: ");
// verify if it is connected (the pin should be pulled up to 5V)
rcc_periph_clock_enable(GPIO_RCC(LID_HEATER_PIN));// enable clock for GPIO port peripheral
gpio_clear(GPIO_PORT(LID_HEATER_PIN),GPIO_PIN(LID_HEATER_PIN));// pull down
gpio_set_mode(GPIO_PORT(LID_HEATER_PIN),GPIO_MODE_INPUT,GPIO_CNF_INPUT_PULL_UPDOWN,GPIO_PIN(LID_HEATER_PIN));// set pin as input
sleep_us(100);// let signal settle
if(!gpio_get(GPIO_PORT(LID_HEATER_PIN),GPIO_PIN(LID_HEATER_PIN))){// signal is not pulled up
error="power board not connected";
puts("KO\n");
}else{// power board is connected
// set up PWM output
rcc_periph_clock_enable(RCC_TIM_CH(LID_HEATER_TIMER,LID_HEATER_CHANNEL));// enable clock for GPIO peripheral
gpio_set(TIM_CH_PORT(LID_HEATER_TIMER,LID_HEATER_CHANNEL),TIM_CH_PIN(LID_HEATER_TIMER,LID_HEATER_CHANNEL));// don't sink current (e.g. not powering the opto-coupler/triac))
gpio_set_mode(TIM_CH_PORT(LID_HEATER_TIMER,LID_HEATER_CHANNEL),GPIO_MODE_OUTPUT_10_MHZ,GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN,TIM_CH_PIN(LID_HEATER_TIMER,LID_HEATER_CHANNEL));// set pin as output
rcc_periph_clock_enable(RCC_AFIO);// enable clock for alternate function (PWM)
rcc_periph_clock_enable(RCC_TIM(LID_HEATER_TIMER));// enable clock for timer peripheral
rcc_periph_reset_pulse(RST_TIM(LID_HEATER_TIMER));// reset timer state
timer_set_mode(TIM(LID_HEATER_TIMER),TIM_CR1_CKD_CK_INT,TIM_CR1_CMS_EDGE,TIM_CR1_DIR_UP);// set timer mode, use undivided timer clock, edge alignment (simple count), and count up
// since we are controlling a triac, but we don't know the zero-crossing point, we can only switch on/off on half AC waves, e.g. 100 Hz
timer_set_prescaler(TIM(LID_HEATER_TIMER),1099-1);// set period to 1 Hz ((72E6/(1099)) / 2**16 = 0.9997)
timer_set_period(TIM(LID_HEATER_TIMER),UINT16_MAX);// use the whole range as period, even if we can only control up to 100 Hz
timer_set_oc_value(TIM(LID_HEATER_TIMER),LID_HEATER_OC,0);// duty cycle to 0%, to switch off heater
timer_set_oc_mode(TIM(LID_HEATER_TIMER),LID_HEATER_OC,TIM_OCM_PWM2);// set timer to generate PWM (heater switched of as long as CNT < CCR)
timer_enable_oc_output(TIM(LID_HEATER_TIMER),LID_HEATER_OC);// enable output to generate the PWM signal
timer_enable_break_main_output(TIM(LID_HEATER_TIMER));// required to enable timer, even when no dead time is used
rcc_periph_clock_enable(GPIO_RCC(MBLK019_CH26_PIN));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(MBLK019_CH26_PIN),GPIO_PIN(MBLK019_CH26_PIN));// don't sink current (e.g. not powering the opto-coupler/transistor)
gpio_set_mode(GPIO_PORT(MBLK019_CH26_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_OPENDRAIN,GPIO_PIN(MBLK019_CH26_PIN));// set pin as output open-drain
rcc_periph_clock_enable(GPIO_RCC(MBLK019_CH14_PIN));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(MBLK019_CH14_PIN),GPIO_PIN(MBLK019_CH14_PIN));// don't sink current (e.g. not powering the opto-coupler/transistor)
gpio_set_mode(GPIO_PORT(MBLK019_CH14_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_OPENDRAIN,GPIO_PIN(MBLK019_CH14_PIN));// set pin as output open-drain
rcc_periph_clock_enable(GPIO_RCC(MBLK019_CH35_PIN));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(MBLK019_CH35_PIN),GPIO_PIN(MBLK019_CH35_PIN));// don't sink current (e.g. not powering the opto-coupler/transistor)
gpio_set_mode(GPIO_PORT(MBLK019_CH35_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_OPENDRAIN,GPIO_PIN(MBLK019_CH35_PIN));// set pin as output open-drain
rcc_periph_clock_enable(GPIO_RCC(MBLK019_PRESENCE_PIN));// enable clock for GPIO port peripheral
gpio_set(GPIO_PORT(MBLK019_PRESENCE_PIN),GPIO_PIN(MBLK019_PRESENCE_PIN));// pull up
gpio_set_mode(GPIO_PORT(MBLK019_PRESENCE_PIN),GPIO_MODE_INPUT,GPIO_CNF_INPUT_PULL_UPDOWN,GPIO_PIN(MBLK019_PRESENCE_PIN));// set pin to input to read state
gpio_clear(GPIO_PORT(TEC_POWER_ORANGE),GPIO_PIN(TEC_POWER_ORANGE));// don't connect wire to VCC
gpio_set_mode(GPIO_PORT(TEC_POWER_ORANGE),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_PUSHPULL,GPIO_PIN(TEC_POWER_ORANGE));// set pin as output
// set up PWM output
rcc_periph_clock_enable(RCC_TIM_CH(TEC_POWER_TIMER,TEC_POWER_CHANNEL));// enable clock for GPIO peripheral
gpio_clear(TIM_CH_PORT(TEC_POWER_TIMER,TEC_POWER_CHANNEL),TIM_CH_PIN(TEC_POWER_TIMER,TEC_POWER_CHANNEL));// don't let power trough
gpio_set_mode(TIM_CH_PORT(TEC_POWER_TIMER,TEC_POWER_CHANNEL),GPIO_MODE_OUTPUT_10_MHZ,GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,TIM_CH_PIN(TEC_POWER_TIMER,TEC_POWER_CHANNEL));// set pin as output
rcc_periph_clock_enable(RCC_AFIO);// enable clock for alternate function (PWM)
rcc_periph_clock_enable(RCC_TIM(TEC_POWER_TIMER));// enable clock for timer peripheral
rcc_periph_reset_pulse(RST_TIM(TEC_POWER_TIMER));// reset timer state
timer_set_mode(TIM(TEC_POWER_TIMER),TIM_CR1_CKD_CK_INT,TIM_CR1_CMS_EDGE,TIM_CR1_DIR_UP);// set timer mode, use undivided timer clock, edge alignment (simple count), and count up
// peltier elements can safely be PWMed at 300 Hz to 3000 Hz, we will keep it under 2 kHz to avoid the audible range
timer_set_prescaler(TIM(TEC_POWER_TIMER),rcc_ahb_frequency/1500000-1);// set the clock frequency to 1.5 kHz
timer_set_period(TIM(TEC_POWER_TIMER),UINT16_MAX);// use the whole range as period, even if we can only control up to 100 Hz
timer_set_oc_value(TIM(TEC_POWER_TIMER),TEC_POWER_OC,0);// duty cycle to 0%, to switch off heater
timer_set_oc_mode(TIM(TEC_POWER_TIMER),TEC_POWER_OC,TIM_OCM_PWM1);// set timer to generate PWM (heater switched of as long as CNT < CCR)
timer_enable_oc_output(TIM(TEC_POWER_TIMER),TEC_POWER_OC);// enable output to generate the PWM signal
timer_enable_break_main_output(TIM(TEC_POWER_TIMER));// required to enable timer, even when no dead time is used
// we can't test if it is connected (we only control the MOSFET directly powering the fan)
rcc_periph_clock_enable(GPIO_RCC(HEATSINK_FAN_PIN));// enable clock for GPIO port peripheral
gpio_clear(GPIO_PORT(HEATSINK_FAN_PIN),GPIO_PIN(HEATSINK_FAN_PIN));// switch off fan
gpio_set_mode(GPIO_PORT(HEATSINK_FAN_PIN),GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_OPENDRAIN,GPIO_PIN(HEATSINK_FAN_PIN));// set pin as output open-drain, gate of nMOS it pulled up externally
puts("OK\n");
puts("setup display: ");
if(oled_text_setup()){// setup OLED display with default slave address
oled_text_clear();// clear buffer (else last state is displayed)
oled_text_line("PCR 3000",0);
oled_text_line("system ready",1);
oled_text_update();
puts("OK\n");
}else{
puts("KO\n");
}
puts("setup DS18B20 temperature sensor: ");
sensor_ds18b20_setup();// configure 1-Wire bus to read from sensor
if(1==sensor_ds18b20_number()){// check number of devices available
sensor_ds18b20_precision(0,12);// set precision to 12 bits
ds18b20_present=true;// remember the sensor is present (and there is only one)
sensor_ds18b20_convert(0);// start conversion (it takes almost 1 s)
if(lid_temp<5.0){// voltage is at the upper limit (3.3V), meaning it is directly connected to the 3.3V pull-up resistor, and the lid thermistor does not pull it to ground