#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stm32c0xx_hal.h>
/* ================== ХЭНДЛЫ ПЕРИФЕРИИ ================== */
UART_HandleTypeDef huart2;
TIM_HandleTypeDef htim1;
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim3;
/* ================== ПИНЫ ================== */
/* Кнопки — все на GPIOA */
#define BTN_ANG_UP_PIN GPIO_PIN_0 /* A0 */
#define BTN_ANG_DN_PIN GPIO_PIN_1 /* A1 */
#define BTN_SPD_UP_PIN GPIO_PIN_4 /* A2 */
#define BTN_SPD_DN_PIN GPIO_PIN_5 /* D13 */
#define BTN_PORT GPIOA
/* Сервопривод */
#define SERVO_MIN_PULSE 500
#define SERVO_MAX_PULSE 2500
#define SERVO_ANGLE_MAX 180
#define ANGLE_STEP 2
#define SPEED_STEP 2
/* Шаговый двигатель */
#define STEP_PORT GPIOA
#define STEP1_PIN GPIO_PIN_9
#define STEP2_PIN GPIO_PIN_10
#define STEP3_PIN GPIO_PIN_11
#define STEP4_PIN GPIO_PIN_12
#define STEP_ALL (STEP1_PIN | STEP2_PIN | STEP3_PIN | STEP4_PIN)
/* LCD I2C */
#define LCD_ADDR (0x27 << 1)
#define LCD_BL 0x08
#define LCD_EN 0x04
#define LCD_RS 0x01
/* ================== ПЕРЕМЕННЫЕ ================== */
int angle = 90;
int speed = 0;
uint8_t is_updated = 1;
uint8_t step_phase = 0;
uint32_t last_step_time = 0;
/* ================== ПРОТОТИПЫ ================== */
void SystemClock_Config(void);
void MX_GPIO_Init(void);
void MX_USART2_UART_Init(void);
void MX_TIM1_PWM_Init(void);
void MX_I2C1_Init(void);
void TIM14_Init(void);
void delay_us(uint16_t us);
void setAngle(int ang);
void stepper_one_step(void);
void lcd_init(void);
void lcd_send_cmd(uint8_t cmd);
void lcd_send_data(uint8_t data);
void lcd_put_cur(int row, int col);
void lcd_send_string(char *str);
void format_string(char *buf, int len);
/* ================== printf → USART2 ================== */
int _write(int file, uint8_t *ptr, int len) {
HAL_UART_Transmit(&huart2, ptr, len, HAL_MAX_DELAY);
return len;
}
void Error_Handler(void) { while (1); }
/* ================== LCD I2C ДРАЙВЕР ================== */
void lcd_pulse(uint8_t data) {
uint8_t d;
d = data | LCD_EN;
HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, &d, 1, 100);
d = data & ~LCD_EN;
HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, &d, 1, 100);
HAL_Delay(1);
}
void lcd_send_nibble(uint8_t nibble, uint8_t rs) {
lcd_pulse((nibble & 0xF0) | LCD_BL | rs);
}
void lcd_send_byte(uint8_t byte, uint8_t rs) {
lcd_send_nibble(byte & 0xF0, rs);
lcd_send_nibble((byte << 4) & 0xF0, rs);
}
void lcd_send_cmd(uint8_t cmd) { lcd_send_byte(cmd, 0); }
void lcd_send_data(uint8_t data) { lcd_send_byte(data, LCD_RS); }
void lcd_init(void) {
HAL_Delay(50);
lcd_send_nibble(0x30, 0); HAL_Delay(5);
lcd_send_nibble(0x30, 0); HAL_Delay(1);
lcd_send_nibble(0x30, 0); HAL_Delay(1);
lcd_send_nibble(0x20, 0); HAL_Delay(1);
lcd_send_cmd(0x28); /* 4-bit, 2 строки, 5x8 */
lcd_send_cmd(0x0C); /* дисплей вкл, курсор выкл */
lcd_send_cmd(0x06); /* инкремент курсора */
lcd_send_cmd(0x01); /* очистка */
HAL_Delay(2);
}
void lcd_put_cur(int row, int col) {
lcd_send_cmd((row == 0 ? 0x80 : 0xC0) + col);
}
void lcd_send_string(char *str) {
while (*str) lcd_send_data(*str++);
}
/* Дополнение строки пробелами до 16 символов */
void format_string(char *buf, int len) {
int l = strlen(buf);
while (l < len) buf[l++] = ' ';
buf[len] = '\0';
}
/* ================== СЕРВОПРИВОД ================== */
void setAngle(int ang) {
if (ang < 0) ang = 0;
if (ang > SERVO_ANGLE_MAX) ang = SERVO_ANGLE_MAX;
uint32_t ccr = SERVO_MIN_PULSE +
(uint32_t)ang * (SERVO_MAX_PULSE - SERVO_MIN_PULSE) / SERVO_ANGLE_MAX;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, ccr);
}
/* ================== ШАГОВЫЙ ДВИГАТЕЛЬ ================== */
void stepper_one_step(void) {
HAL_GPIO_WritePin(STEP_PORT, STEP_ALL, GPIO_PIN_RESET);
switch (step_phase) {
case 0: HAL_GPIO_WritePin(STEP_PORT, STEP1_PIN, GPIO_PIN_SET); break;
case 1: HAL_GPIO_WritePin(STEP_PORT, STEP3_PIN, GPIO_PIN_SET); break;
case 2: HAL_GPIO_WritePin(STEP_PORT, STEP2_PIN, GPIO_PIN_SET); break;
case 3: HAL_GPIO_WritePin(STEP_PORT, STEP4_PIN, GPIO_PIN_SET); break;
}
step_phase = (step_phase + 1) % 4;
}
/* ================== МИКРОСЕКУНДНАЯ ЗАДЕРЖКА ================== */
void TIM14_Init(void) {
__HAL_RCC_TIM14_CLK_ENABLE();
TIM14->PSC = 47;
TIM14->ARR = 0xFFFF;
TIM14->CR1 |= TIM_CR1_CEN;
}
void delay_us(uint16_t us) {
TIM14->CNT = 0;
while (TIM14->CNT < us);
}
/* ================== ИНИЦИАЛИЗАЦИЯ ================== */
void SystemClock_Config(void) {
RCC_OscInitTypeDef o = {0};
RCC_ClkInitTypeDef c = {0};
o.OscillatorType = RCC_OSCILLATORTYPE_HSI;
o.HSIState = RCC_HSI_ON;
o.HSIDiv = RCC_HSI_DIV1;
o.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
HAL_RCC_OscConfig(&o);
c.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
c.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
c.SYSCLKDivider = RCC_SYSCLK_DIV1;
c.AHBCLKDivider = RCC_HCLK_DIV1;
c.APB1CLKDivider = RCC_APB1_DIV1;
HAL_RCC_ClockConfig(&c, FLASH_LATENCY_1);
}
void MX_GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef g = {0};
/* 4 кнопки — PA0, PA1, PA4, PA5 вход с подтяжкой */
g.Pin = BTN_ANG_UP_PIN | BTN_ANG_DN_PIN | BTN_SPD_UP_PIN | BTN_SPD_DN_PIN;
g.Mode = GPIO_MODE_INPUT;
g.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &g);
/* Шаговый двигатель — PA9-PA12 выход */
g.Pin = STEP_ALL;
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(STEP_PORT, &g);
}
void MX_USART2_UART_Init(void) {
__HAL_RCC_USART2_CLK_ENABLE();
GPIO_InitTypeDef g = {0};
g.Pin = GPIO_PIN_2 | GPIO_PIN_3;
g.Mode = GPIO_MODE_AF_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
g.Alternate = GPIO_AF1_USART2;
HAL_GPIO_Init(GPIOA, &g);
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
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;
HAL_UART_Init(&huart2);
}
void MX_TIM3_PWM_Init(void) {
__HAL_RCC_TIM3_CLK_ENABLE();
GPIO_InitTypeDef g = {0};
g.Pin = GPIO_PIN_7;
g.Mode = GPIO_MODE_AF_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_HIGH;
g.Alternate = GPIO_AF1_TIM3;
HAL_GPIO_Init(GPIOA, &g);
htim3.Instance = TIM3;
htim3.Init.Prescaler = 47;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 19999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&htim3);
TIM_OC_InitTypeDef oc = {0};
oc.OCMode = TIM_OCMODE_PWM1;
oc.Pulse = 1500;
oc.OCPolarity = TIM_OCPOLARITY_HIGH;
oc.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &oc, TIM_CHANNEL_2);
}
void MX_I2C1_Init(void) {
__HAL_RCC_I2C1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef g = {0};
g.Pin = GPIO_PIN_8 | GPIO_PIN_9; /* PB8=SCL, PB9=SDA */
g.Mode = GPIO_MODE_AF_OD;
g.Pull = GPIO_PULLUP;
g.Speed = GPIO_SPEED_FREQ_HIGH;
g.Alternate = GPIO_AF6_I2C1;
HAL_GPIO_Init(GPIOB, &g);
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x2000090E; /* 100кГц при 48МГц */
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
/* ================== MAIN ================== */
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM3_PWM_Init(); /* вместо MX_TIM1_PWM_Init */
MX_I2C1_Init();
TIM14_Init();
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
setAngle(angle);
lcd_init();
lcd_put_cur(0, 0); lcd_send_string("Angle: N/A ");
lcd_put_cur(1, 0); lcd_send_string("Speed: N/A ");
printf("System started\r\n");
while (1) {
/* ===== ПРИЛОЖЕНИЕ А: опрос кнопок ===== */
/* ANG+ (PA0) */
if (HAL_GPIO_ReadPin(BTN_PORT, BTN_ANG_UP_PIN) == GPIO_PIN_RESET) {
if (angle <= SERVO_ANGLE_MAX - ANGLE_STEP) angle += ANGLE_STEP;
is_updated = 1;
while (HAL_GPIO_ReadPin(BTN_PORT, BTN_ANG_UP_PIN) == GPIO_PIN_RESET);
HAL_Delay(50);
}
/* ANG- (PA1) */
if (HAL_GPIO_ReadPin(BTN_PORT, BTN_ANG_DN_PIN) == GPIO_PIN_RESET) {
if (angle >= ANGLE_STEP) angle -= ANGLE_STEP;
is_updated = 1;
while (HAL_GPIO_ReadPin(BTN_PORT, BTN_ANG_DN_PIN) == GPIO_PIN_RESET);
HAL_Delay(50);
}
/* SPD+ (PA4) */
if (HAL_GPIO_ReadPin(BTN_PORT, BTN_SPD_UP_PIN) == GPIO_PIN_RESET) {
if (speed <= 100 - SPEED_STEP) speed += SPEED_STEP;
is_updated = 1;
while (HAL_GPIO_ReadPin(BTN_PORT, BTN_SPD_UP_PIN) == GPIO_PIN_RESET);
HAL_Delay(50);
}
/* SPD- (PA5) */
if (HAL_GPIO_ReadPin(BTN_PORT, BTN_SPD_DN_PIN) == GPIO_PIN_RESET) {
if (speed >= SPEED_STEP) speed -= SPEED_STEP;
is_updated = 1;
while (HAL_GPIO_ReadPin(BTN_PORT, BTN_SPD_DN_PIN) == GPIO_PIN_RESET);
HAL_Delay(50);
}
/* ===== ПРИЛОЖЕНИЕ Б: исполнение ===== */
if (is_updated) {
/* Сервопривод — установка угла */
setAngle(angle);
/* LCD — обновление дисплея */
char line0[17], line1[17];
snprintf(line0, sizeof(line0), "Angle: %d", angle);
snprintf(line1, sizeof(line1), "Speed: %d", speed);
format_string(line0, 16);
format_string(line1, 16);
lcd_put_cur(0, 0);
lcd_send_string(line0);
lcd_put_cur(1, 0);
lcd_send_string(line1);
/* UART диагностика */
printf("Angle: %d, Speed: %d\r\n", angle, speed);
is_updated = 0;
}
/* Шаговый двигатель — неблокирующее управление */
if (speed > 0) {
uint32_t step_delay = 200 - speed; /* чем больше speed, тем быстрее */
if (step_delay < 10) step_delay = 10;
uint32_t now = HAL_GetTick();
if (now - last_step_time >= step_delay) {
last_step_time = now;
stepper_one_step();
}
}
}
}