#include "stm32c0xx_hal.h"
// Define pins for digits and segments
#define DIGIT_1_PIN GPIO_PIN_0
#define DIGIT_2_PIN GPIO_PIN_1
#define DIGIT_3_PIN GPIO_PIN_2
#define DIGIT_4_PIN GPIO_PIN_3
#define DIGIT_PORT GPIOD
#define SEG_A_PIN GPIO_PIN_0
#define SEG_B_PIN GPIO_PIN_1
#define SEG_C_PIN GPIO_PIN_2
#define SEG_D_PIN GPIO_PIN_3
#define SEG_E_PIN GPIO_PIN_4
#define SEG_F_PIN GPIO_PIN_5
#define SEG_G_PIN GPIO_PIN_6
#define SEG_DP_PIN GPIO_PIN_7
#define SEGMENT_PORT GPIOA
// NTC parameters
const float BETA = 3950.0f; // Beta coefficient of the thermistor
// Segment patterns for digits 0-9 (common anode - inverted logic)
const uint8_t digit_patterns[10] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90
};
// Pattern for 'C' character
const uint8_t char_c = 0xC6;
// Pattern for '-' character
const uint8_t char_minus = 0xBF;
// Precompute digit enable patterns
const uint16_t DIGIT_ENABLE[4] = {DIGIT_1_PIN, DIGIT_2_PIN, DIGIT_3_PIN, DIGIT_4_PIN};
uint8_t segment_patterns[4] = {0}; // Precomputed patterns for each digit
float temperature = 0.0f;
uint8_t current_digit = 0;
uint32_t last_display_update = 0;
uint32_t last_sensor_update = 0;
// Simple ADC read function
uint16_t read_adc(void)
{
// Enable ADC clock
RCC->APBENR2 |= RCC_APBENR2_ADCEN;
// Configure PB1 as analog input
GPIOB->MODER |= GPIO_MODER_MODE1; // Analog mode
// Configure ADC
ADC1->CFGR1 = 0; // 12-bit resolution, right aligned
ADC1->CHSELR = ADC_CHSELR_CHSEL18; // Select channel 18 (PB1)
ADC1->CR = ADC_CR_ADEN; // Enable ADC
// Wait for ADC to be ready
while (!(ADC1->ISR & ADC_ISR_ADRDY));
// Start conversion
ADC1->CR |= ADC_CR_ADSTART;
// Wait for conversion to complete
while (!(ADC1->ISR & ADC_ISR_EOC));
// Read result
uint16_t result = ADC1->DR;
// Disable ADC to save power
ADC1->CR |= ADC_CR_ADDIS;
while (ADC1->CR & ADC_CR_ADEN);
return result;
}
// Calculate temperature from ADC value
float calculate_temperature(uint16_t adc_value)
{
// Scale 12-bit ADC value (0-4095) to 10-bit (0-1023)
float scaled_value = (float)adc_value / 4095.0f * 1023.0f;
// Use the same formula as the Arduino example
return 1.0f / (log(1.0f / (1023.0f / scaled_value - 1.0f)) / BETA + 1.0f / 298.15f) - 273.15f;
}
void update_display_buffer()
{
// Handle negative temperatures
if (temperature < 0.0f) {
// Clamp temperature to -24°C
if (temperature < -24.0f) {
temperature = -24.0f;
}
// Display format depends on temperature range
if (temperature > -10.0f) {
// Display as -X.X (e.g., -5.5°C)
uint8_t abs_temp = (uint8_t)(-temperature * 10.0f + 0.5f); // Convert to tenths
segment_patterns[0] = char_minus; // Minus sign
segment_patterns[1] = digit_patterns[abs_temp / 10]; // Whole number part
segment_patterns[1] &= ~(1 << 7); // Add decimal point to first digit
segment_patterns[2] = digit_patterns[abs_temp % 10]; // Tenths
segment_patterns[3] = char_c; // 'C' for Celsius
} else {
// Display as -XX (e.g., -15°C)
uint8_t abs_temp = (uint8_t)(-temperature + 0.5f); // Convert to integer
segment_patterns[0] = char_minus; // Minus sign
segment_patterns[1] = digit_patterns[abs_temp / 10]; // Tens digit
segment_patterns[2] = digit_patterns[abs_temp % 10]; // Units digit
segment_patterns[3] = char_c; // 'C' for Celsius
}
} else {
// Handle positive temperatures
uint16_t value;
uint8_t dp_pos = 4; // No decimal point by default
if (temperature < 10.0f) {
value = (uint16_t)(temperature * 100.0f + 0.5f);
dp_pos = 1;
} else if (temperature < 100.0f) {
value = (uint16_t)(temperature * 10.0f + 0.5f);
dp_pos = 2;
} else {
value = (uint16_t)(temperature + 0.5f);
dp_pos = 4;
}
// Extract digits for the first three digits
segment_patterns[0] = digit_patterns[value / 100 % 10];
segment_patterns[1] = digit_patterns[value / 10 % 10];
segment_patterns[2] = digit_patterns[value % 10];
segment_patterns[3] = char_c;
// Enable decimal point if needed
if (dp_pos < 4) {
segment_patterns[dp_pos - 1] &= ~(1 << 7);
}
}
}
void setup()
{
// Enable GPIO clocks
RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIODEN | RCC_IOPENR_GPIOBEN;
// Configure digit pins as output
GPIOD->MODER &= ~(GPIO_MODER_MODE0 | GPIO_MODER_MODE1 | GPIO_MODER_MODE2 | GPIO_MODER_MODE3);
GPIOD->MODER |= (GPIO_MODER_MODE0_0 | GPIO_MODER_MODE1_0 | GPIO_MODER_MODE2_0 | GPIO_MODER_MODE3_0);
// Configure segment pins as output
GPIOA->MODER &= ~(GPIO_MODER_MODE0 | GPIO_MODER_MODE1 | GPIO_MODER_MODE2 | GPIO_MODER_MODE3 |
GPIO_MODER_MODE4 | GPIO_MODER_MODE5 | GPIO_MODER_MODE6 | GPIO_MODER_MODE7);
GPIOA->MODER |= (GPIO_MODER_MODE0_0 | GPIO_MODER_MODE1_0 | GPIO_MODER_MODE2_0 | GPIO_MODER_MODE3_0 |
GPIO_MODER_MODE4_0 | GPIO_MODER_MODE5_0 | GPIO_MODER_MODE6_0 | GPIO_MODER_MODE7_0);
// Configure PB1 as analog input
GPIOB->MODER |= GPIO_MODER_MODE1;
// Turn off all digits initially
GPIOD->ODR &= ~(DIGIT_1_PIN | DIGIT_2_PIN | DIGIT_3_PIN | DIGIT_4_PIN);
// Read initial temperature
uint16_t adc_value = read_adc();
temperature = calculate_temperature(adc_value);
update_display_buffer();
}
void loop()
{
uint32_t now = HAL_GetTick();
// Display update
if (now - last_display_update >= 2) {
// Turn off current digit
GPIOD->BRR = DIGIT_ENABLE[current_digit];
// Move to next digit
current_digit = (current_digit + 1) & 0x03;
// Set segments for new digit
GPIOA->ODR = (GPIOA->ODR & 0xFF00) | segment_patterns[current_digit];
// Enable new digit
GPIOD->BSRR = DIGIT_ENABLE[current_digit];
last_display_update = now;
}
// Sensor update every 500ms
if (now - last_sensor_update >= 500) {
uint16_t adc_value = read_adc();
temperature = calculate_temperature(adc_value);
update_display_buffer();
last_sensor_update = now;
}
}
int main(void)
{
HAL_Init();
SysTick_Config(SystemCoreClock / 1000);
setup();
while (1) {
loop();
}
}