// STM32 Nucleo-L031K6 HAL Blink + printf() example
// SSD1306
// Simulation: https://wokwi.com/projects/367244067477216257
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stm32l0xx_hal.h>
// ST Nucleo Green user LED (PB3)
#define LED_PORT GPIOB
#define LED_PIN GPIO_PIN_3
#define LED_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE
#define VCP_TX_Pin GPIO_PIN_2
#define VCP_RX_Pin GPIO_PIN_15
UART_HandleTypeDef huart2;
// Глобальная переменная для хранения конфигурации I2C
I2C_HandleTypeDef hi2c1;
void SystemClock_Config(void);
static void MX_USART2_UART_Init(void);
void osSystickHandler(void)
{
// 1 Hz blinking:
if ((HAL_GetTick() % 500) == 0)
{
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}
}
void I2C1_Init(void){
// Инициализация структуры конфигурации I2C
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00100509; // Для 400 кГц (APB1 = 32 МГц)
//hi2c1.Init.Timing = 0x00B0B47B; // Для 100 кГц при APB1 = 32 МГц
hi2c1.Init.OwnAddress1 = 0; // Адрес устройства (если используется режим Slave)
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // Режим адресации (7-битный)
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // Отключение двойного адреса
hi2c1.Init.OwnAddress2 = 0; // Второй адрес устройства (если используется)
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK; // Маска для второго адреса
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // Отключение общего вызова
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // Отключение режима отсутствия растяжения
// Инициализация I2C
if (HAL_I2C_Init(&hi2c1) != HAL_OK){
// Обработка ошибки инициализации
Error_Handler();
}
// Настройка фильтрации I2C (если требуется)
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK){
Error_Handler();
}
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK){
Error_Handler();
}
}
// Функция для отправки команды на SSD1306
void SSD1306_SendCommand(uint8_t command){
uint8_t data[2] = {0x00, command}; // Первый байт 0x00 - это команда
HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)0x3C << 1, data, sizeof(data), HAL_MAX_DELAY);
}
// Функция для отправки данных на SSD1306
void SSD1306_SendData(uint8_t *data, uint16_t size){
uint8_t buffer[129];
buffer[0] = 0x40; // Первый байт 0x40 - это данные
memcpy(&buffer[1], data, size);
HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)0x3C << 1, buffer, size + 1, HAL_MAX_DELAY);
}
// Функция для инициализации SSD1306
void SSD1306_Init(void){
// Инициализация дисплея
SSD1306_SendCommand(0xAE); // Display Off
SSD1306_SendCommand(0x20); // Set Memory Addressing Mode
SSD1306_SendCommand(0x10); // Horizontal Addressing Mode
SSD1306_SendCommand(0xB0); // Set Page Start Address for Page Addressing Mode
SSD1306_SendCommand(0xC8); // Set COM Output Scan Direction
SSD1306_SendCommand(0x00); // Set Lower Column Start Address
SSD1306_SendCommand(0x10); // Set Higher Column Start Address
SSD1306_SendCommand(0x40); // Set Display Start Line
SSD1306_SendCommand(0x81); // Set Contrast Control Register
SSD1306_SendCommand(0x7F); // Contrast Value
SSD1306_SendCommand(0xA1); // Set Segment Re-map
SSD1306_SendCommand(0xA6); // Set Normal Display
SSD1306_SendCommand(0xA8); // Set Multiplex Ratio
SSD1306_SendCommand(0x1F); // 1/32 Duty (0x0F Minimum)
SSD1306_SendCommand(0xA4); // Output RAM to Display
SSD1306_SendCommand(0xD3); // Set Display Offset
SSD1306_SendCommand(0x00); // No Offset
SSD1306_SendCommand(0xD5); // Set Display Clock Divide Ratio/Oscillator Frequency
SSD1306_SendCommand(0xF0); // Set Clock as 15 Frame Frequency
SSD1306_SendCommand(0xD9); // Set Pre-charge Period
SSD1306_SendCommand(0x22); // Set Pre-charge as 15 Clocks & Discharge as 1 Clock
SSD1306_SendCommand(0xDA); // Set COM Pins Hardware Configuration
SSD1306_SendCommand(0x02); // Alternative COM Pin Configuration
SSD1306_SendCommand(0xDB); // Set VCOMH Deselect Level
SSD1306_SendCommand(0x40); // 0.77xVcc
SSD1306_SendCommand(0x8D); // Set DC-DC Enable
SSD1306_SendCommand(0x14); // DC-DC Converter ON
SSD1306_SendCommand(0xAF); // Display ON
}
// Функция для очистки экрана
void SSD1306_ClearScreen(void)
{
uint8_t data[128];
memset(data, 0x00, sizeof(data)); // Заполняем экран нулями (черный цвет)
for (uint8_t i = 0; i < 4; i++)
{
SSD1306_SendCommand(0xB0 + i); // Устанавливаем страницу
SSD1306_SendCommand(0x00); // Устанавливаем младший байт адреса
SSD1306_SendCommand(0x10); // Устанавливаем старший байт адреса
SSD1306_SendData(data, sizeof(data));
}
}
// Функция для мигания точки на экране
void SSD1306_BlinkDot(void)
{
uint8_t data[128];
memset(data, 0x00, sizeof(data)); // Очищаем буфер
// Мигаем точкой на экране
for (uint8_t i = 0; i < 4; i++){
SSD1306_SendCommand(0xB0 + i); // Устанавливаем страницу
SSD1306_SendCommand(0x00); // Устанавливаем младший байт адреса
SSD1306_SendCommand(0x10); // Устанавливаем старший байт адреса
// Устанавливаем точку в середине экрана
data[64] = 0xFF; // Точка в середине экрана
SSD1306_SendData(data, sizeof(data));
HAL_Delay(500); // Пауза 500 мс
// Убираем точку
data[64] = 0x00;
SSD1306_SendData(data, sizeof(data));
HAL_Delay(500); // Пауза 500 мс
}
}
void GPIO_Init(){
GPIO_InitTypeDef GPIO_Config;
GPIO_Config.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Config.Pull = GPIO_NOPULL;
GPIO_Config.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_Config.Pin = LED_PIN;
LED_PORT_CLK_ENABLE();
HAL_GPIO_Init(LED_PORT, &GPIO_Config);
__HAL_RCC_GPIOB_CLK_ENABLE(); // Включение тактирования GPIOB
GPIO_InitTypeDef GPIO_InitStruct = {0};
// Настройка PB6 (SCL) и PB7 (SDA) как альтернативные функции I2C1
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // Выход с открытым стоком
GPIO_InitStruct.Pull = GPIO_NOPULL; // Без подтяжки (если требуется, можно включить)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; // Альтернативная функция I2C1
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
int main(void){
HAL_Init(); // Инициализация HAL
SystemClock_Config(); // Настройка системного тактирования
GPIO_Init(); // Инициализация GPIO
I2C1_Init(); // Инициализация I2C1
SSD1306_Init(); // Инициализация SSD1306
MX_USART2_UART_Init();
printf("Hello, %s!\n", "Wokwi");
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
while (1){
// Мигаем точкой на экране
SSD1306_BlinkDot();
}
return 0;
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
@brief USART2 Initialization Function
@param None
@retval None
*/
static void MX_USART2_UART_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA15 ------> USART2_RX
*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = VCP_TX_Pin | VCP_RX_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
__HAL_RCC_USART2_CLK_ENABLE();
}
void Error_Handler(void)
{
/* User can add his own implementation to report the HAL error return state */
}
// The following makes printf() write to USART2:
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
int _write(int file, uint8_t *ptr, int len)
{
switch (file)
{
case STDOUT_FILENO:
HAL_UART_Transmit(&huart2, ptr, len, HAL_MAX_DELAY);
break;
case STDERR_FILENO:
HAL_UART_Transmit(&huart2, ptr, len, HAL_MAX_DELAY);
break;
default:
return -1;
}
return len;
}
Loading
st-nucleo-l031k6
st-nucleo-l031k6