STM32F103RCT6+STM32CubeMX+keil5+薄膜键盘控制SG90舵机+ADC环境光传感器控制WS2812RGB灯
·
SG90:
| https://item.taobao.com/item.htm?id=678830927467&mi_id=0000bfs3fkPCVUszg031x_05EnwHrp6_hy12yq0S6RyIjgk&skuId=5193877811199&spm=a21xtw.29978518.0.0&xxc=shop |
WS2812RGB灯:
| https://item.taobao.com/item.htm?id=623668535961&mi_id=0000ASQFV4Rm-kGmtUN_w3qVzYFZc2EIM6cJ2y-qQSnBf38&skuId=4812440351520&spm=a21xtw.29978518.0.0&xxc=shop |
1、


2、











3、配SPI



4、加一下lcd驱动,RGB灯驱动

5、ws2812.h
#ifndef __WS2812_H
#define __WS2812_H
#include "main.h"
#include "spi.h"
#include "dma.h"
// ========= 配置参数 =========
#define PIXEL_NUM 1
#define WS2812_SPI hspi1
#define WS2812_DMA hdma_spi1_tx
#define WS_HIGH 0xF8 // 1111 1000 → 高电平时间更长(1码)
#define WS_LOW 0xE0 // 1110 0000 → 高电平时间更短(0码)
// ========= 函数声明 =========
void ws281x_init(void);
void ws281x_show(void);
void ws281x_setPixelRGB(uint16_t n, uint8_t r, uint8_t g, uint8_t b);
void ws281x_closeAll(void);
uint32_t ws281x_color(uint8_t r, uint8_t g, uint8_t b);
// ========= 外部变量声明 ?? 关键! =========
extern DMA_HandleTypeDef hdma_spi1_tx;
extern SPI_HandleTypeDef hspi1;
#endif
ws2812.c
#include "ws2812.h"
// 像素缓冲区:每个LED需要24字节(3颜色×8bit)
uint8_t pixelBuffer[PIXEL_NUM][24];
// ========= 初始化 =========
void ws281x_init(void)
{
// HAL库已自动初始化SPI1和DMA,只需确保DMA空闲
if(HAL_DMA_GetState(&hdma_spi1_tx) != HAL_DMA_STATE_READY)
{
HAL_DMA_Abort(&hdma_spi1_tx);
}
ws281x_closeAll();
HAL_Delay(100);
}
// ========= 关闭所有灯 =========
void ws281x_closeAll(void)
{
for(uint16_t i = 0; i < PIXEL_NUM; i++)
{
for(uint8_t j = 0; j < 24; j++)
{
pixelBuffer[i][j] = WS_LOW;
}
}
ws281x_show();
}
// ========= 颜色转换(GRB顺序) =========
uint32_t ws281x_color(uint8_t red, uint8_t green, uint8_t blue)
{
// WS2812B需要GRB顺序,不是RGB!
return ((uint32_t)green << 16) | ((uint32_t)red << 8) | (uint32_t)blue;
}
// ========= 设置单个像素颜色 =========
void ws281x_setPixelRGB(uint16_t n, uint8_t red, uint8_t green, uint8_t blue)
{
if(n >= PIXEL_NUM) return;
uint32_t grb_color = ws281x_color(red, green, blue); // GRB顺序
// 编码24位数据到pixelBuffer
for(uint8_t bit = 0; bit < 24; bit++)
{
// 从最高位(MSB)开始判断
if(grb_color & (0x800000 >> bit))
pixelBuffer[n][bit] = WS_HIGH; // 1码
else
pixelBuffer[n][bit] = WS_LOW; // 0码
}
}
// ========= 发送数据到WS2812B(核心!) =========
void ws281x_show(void)
{
// 1. 确保DMA空闲
if(HAL_DMA_GetState(&hdma_spi1_tx) != HAL_DMA_STATE_READY)
{
HAL_DMA_Abort(&hdma_spi1_tx);
}
// 2. 启动SPI+DMA传输
HAL_SPI_Transmit_DMA(&hspi1,
(uint8_t*)pixelBuffer,
PIXEL_NUM * 24);
// 3. ?? 关键:等待传输完成
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY)
{
__NOP();
}
// 4. 确保50μs复位时间
HAL_Delay(3); // 1ms > 50μs
}
void ws281x_setPixelColor(uint16_t n, uint32_t GRBcolor)
{
// 提取GRB颜色分量
uint8_t green = (GRBcolor >> 16) & 0xFF;
uint8_t red = (GRBcolor >> 8) & 0xFF;
uint8_t blue = GRBcolor & 0xFF;
// 调用新函数
ws281x_setPixelRGB(n, red, green, blue);
}
// ========= 可选:流水灯特效(教学演示用) =========
void ws281x_colorWipe(uint32_t c, uint8_t wait)
{
for(uint16_t i = 0; i < PIXEL_NUM; i++)
{
ws281x_setPixelColor(i, c);
ws281x_show();
HAL_Delay(wait); // ← 替换 delay_ms
}
}
6、main.h
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
// 4��4
#define KEY_COL1_PIN GPIO_PIN_0
#define KEY_COL2_PIN GPIO_PIN_1
#define KEY_COL3_PIN GPIO_PIN_7
#define KEY_COL4_PIN GPIO_PIN_8
#define KEY_ROW1_PIN GPIO_PIN_3
#define KEY_ROW2_PIN GPIO_PIN_4
#define KEY_ROW3_PIN GPIO_PIN_5
#define KEY_ROW4_PIN GPIO_PIN_6
#define KEY_PORT GPIOB
#define KEY_COUNT 16
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
7、main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* ========== 功能开关 ========== */
//#define USE_LCD // 注释这行就禁用LCD,腾出空间
#define USE_WS2812RGB
#include <stdio.h>
#include <string.h>
#ifdef USE_LCD
#include "GUI.h"
#include "Lcd_Driver.h"
#endif
#ifdef USE_WS2812RGB
#include "ws2812.h"
#endif
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t humidity;
// ADC variables
uint16_t adc_value = 0;
float voltage = 0.0f;
float light_percent = 0.0f;
// Servo control variables
uint16_t servo_position = 1500; // Initial position (90 degrees)
#define SERVO_LEFT 500 // 0 degrees
#define SERVO_CENTER 1500 // 90 degrees
#define SERVO_RIGHT 2500 // 180 degrees
uint8_t servo_hint_id = 0;
#ifdef USE_WS2812RGB
// 保留颜色变量
uint8_t ws2812_r = 0, ws2812_g = 0, ws2812_b = 0;
uint8_t rgb_mode = 0;
#endif
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* ========== 4x4 Matrix Keypad ========== */
/* Keypad key map (4x4) */
const char keypad44_map[4][4] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
/* Initialize 4x4 matrix keypad */
void Keypad44_Init(void)
{
HAL_GPIO_WritePin(KEY_PORT,
KEY_COL1_PIN|KEY_COL2_PIN|KEY_COL3_PIN|KEY_COL4_PIN,
GPIO_PIN_RESET);
}
/* Scan matrix keypad, return key index (0-15), return 0xFF if no key */
uint8_t Keypad44_Scan(void)
{
uint8_t row, col;
uint16_t col_pins[4] = {KEY_COL1_PIN, KEY_COL2_PIN, KEY_COL3_PIN, KEY_COL4_PIN};
uint16_t row_pins[4] = {KEY_ROW1_PIN, KEY_ROW2_PIN, KEY_ROW3_PIN, KEY_ROW4_PIN};
/* Scan each column */
for(col = 0; col < 4; col++)
{
/* Set all columns high, then set current column low */
HAL_GPIO_WritePin(KEY_PORT,
KEY_COL1_PIN|KEY_COL2_PIN|KEY_COL3_PIN|KEY_COL4_PIN,
(uint16_t)(0x0F << 0));
HAL_GPIO_WritePin(KEY_PORT, col_pins[col], GPIO_PIN_RESET);
HAL_Delay(1); /* Short delay for stability */
/* Read all rows */
for(row = 0; row < 4; row++)
{
if(HAL_GPIO_ReadPin(KEY_PORT, row_pins[row]) == GPIO_PIN_RESET)
{
/* Key detected, debounce */
HAL_Delay(20);
if(HAL_GPIO_ReadPin(KEY_PORT, row_pins[row]) == GPIO_PIN_RESET)
{
/* Wait for key release */
while(HAL_GPIO_ReadPin(KEY_PORT, row_pins[row]) == GPIO_PIN_RESET);
return col * 4 + row; /* Return key index (0-15) - FIXED */
}
}
}
}
return 0xFF; /* No key pressed */
}
/* Get keypad character */
char Keypad44_GetChar(void)
{
uint8_t key = Keypad44_Scan();
if(key != 0xFF)
{
return keypad44_map[key / 4][key % 4];
}
return 0;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_ADC1_Init();
MX_TIM3_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
printf("===SG90 Test ===\r\n");
printf("Starting...\r\n");
// Start PWM for servo control
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
HAL_Delay(100); // Wait for servo initialization
#ifdef USE_WS2812RGB
printf("WS2812B SPI Init...\r\n");
ws281x_init(); // ← 调用新驱动
// 测试:初始微亮
ws281x_setPixelRGB(0, 20, 20, 20);
printf("WS2812B Ready\r\n");
#endif
/* Initialize 4x4 matrix keypad */
Keypad44_Init();
/* LCD initialization */
#ifdef USE_LCD
Lcd_Init();
Lcd_Clear(GRAY0);
#endif
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
static char last_keypad_char = 0;
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// Start ADC conversion
HAL_ADC_Start(&hadc1);
// Wait for conversion to complete
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
// Read ADC value
adc_value = HAL_ADC_GetValue(&hadc1);
// Stop ADC
HAL_ADC_Stop(&hadc1);
// Calculate voltage and light percentage
voltage = (float)adc_value * 3.3f / 4095.0f;
light_percent = ((3.3f - voltage) / 3.3f) * 100.0f;
#ifdef USE_LCD
// Display Light Sensor title
Gui_DrawFont_GBK16(0, 70, RED, GRAY0, (u8*)"Light Sensor");
// Display Light percentage
char light_str[20];
sprintf(light_str, "Light: %.1f%%", light_percent);
Gui_DrawFont_GBK16(0, 90, GREEN, GRAY0, (u8*)light_str);
#endif
// Print to serial
//printf("ADC: %d, Volt: %.2fV, Light: %.1f%%\r\n", adc_value, voltage, light_percent);
/* Scan 4x4 matrix keypad */
char keypad44_char = Keypad44_GetChar();
if(keypad44_char != 0)
{
printf("Keypad44: %c\r\n", keypad44_char);
if(keypad44_char != last_keypad_char)
{
last_keypad_char = keypad44_char;
#ifdef USE_LCD
Lcd_Clear(WHITE);
Gui_DrawFont_GBK16(0, 10, BLUE, WHITE, (u8*)"Keyboard Display");
u8 str[20];
sprintf((char*)str, "Keypad44: %c", keypad44_char);
Gui_DrawFont_GBK24(0, 30, RED, WHITE, str);
#endif
#ifdef USE_WS2812RGB
switch(keypad44_char)
{
case 'A':
rgb_mode = 1;
ws281x_setPixelRGB(0, 255, 255, 255); // 白色
ws281x_show(); // ⚠️ 必须调用这个!
HAL_Delay(50);
printf("RGB: WHITE\r\n");
break;
case 'B':
rgb_mode = 2;
ws281x_setPixelRGB(0, 0, 255, 255); // 青色
ws281x_show(); // ⚠️ 必须调用这个!
HAL_Delay(50);
printf("RGB: CYAN\r\n");
break;
case 'C':
rgb_mode = 3;
ws281x_setPixelRGB(0, 255, 0, 0); // 红色
ws281x_show(); // ⚠️ 必须调用这个!
HAL_Delay(50);
printf("RGB: RED\r\n");
break;
case '2':
rgb_mode = 0; // 恢复光敏自动
printf("RGB: AUTO (Light Sensor)\r\n");
break;
case '3':
rgb_mode = 4;
ws281x_setPixelRGB(0, 0, 0, 0); // 关闭
ws281x_show(); // ⚠️ 必须调用这个!
HAL_Delay(100);
printf("RGB: OFF\r\n");
break;
}
#endif
// Servo control based on keypad input
switch(keypad44_char)
{
case 'A':
// Key 1: Turn left 90 degrees (to 0 degrees)
servo_position = SERVO_LEFT;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, servo_position);
printf("Servo: Turn LEFT to 0 degrees\r\n");
servo_hint_id = 1;
break;
case 'B':
// Key 2: Turn right 90 degrees (to 180 degrees)
servo_position = SERVO_RIGHT;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, servo_position);
printf("Servo: Turn RIGHT to 180 degrees\r\n");
servo_hint_id = 2;
break;
case 'C':
// Key 3: Turn left then back to center
printf("Servo: Turn LEFT to 0 degrees\r\n");
servo_position = SERVO_LEFT;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, servo_position);
HAL_Delay(1000); // Wait 1 second
printf("Servo: Return to CENTER (90 degrees)\r\n");
servo_hint_id = 3;
servo_position = SERVO_CENTER;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, servo_position);
break;
default:
servo_hint_id = 0;
break;
}
}
}
#ifdef USE_LCD
switch(servo_hint_id)
{
case 1:
Gui_DrawFont_GBK16(0, 50, RED, WHITE, (u8*)"LEFT");
break;
case 2:
Gui_DrawFont_GBK16(0, 50, RED, WHITE, (u8*)"RIGHT");
break;
case 3:
Gui_DrawFont_GBK16(0, 50, RED, WHITE, (u8*)"Center");
break;
default:
Gui_DrawFont_GBK16(0, 50, GRAY0, WHITE, (u8*)"Press A/B/C...");
break;
}
#endif
#ifdef USE_WS2812RGB
// WS2812B 光敏自动模式
static float last_light_for_led = -1.0f;
float light_diff = light_percent - last_light_for_led;
if(light_diff < 0) light_diff = -light_diff;
if(rgb_mode == 0 && light_diff > 2.0f) {
last_light_for_led = light_percent;
// 直接调用新驱动函数
if(light_percent < 30.0f) {
ws281x_setPixelRGB(0, 150, 150, 150); // 白色
}
else if(light_percent < 70.0f) {
ws281x_setPixelRGB(0, 0, 255, 255); // 青色
}
else {
ws281x_setPixelRGB(0, 255, 0, 0); // 红色
}
ws281x_show(); // ⚠️ 必须调用这个!
}
#endif
HAL_Delay(20);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
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_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV8;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch;
HAL_UART_Receive(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);
return ch;
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
8、WS2812RGB灯,DIN连接PA7,VCC连接5V,GND连GND
光敏传感器VCC连3.3V,GND连GND,A0连PC0
键盘这样连接

SG90棕色线接地,红色3.3V,橙色接PC6

9、实验结果
SG90控制逻辑:
按键A:舵机转到0度(左)
按键B:舵机转到180度(右)
按键C:舵机先转到0度,等待1秒,然后回到90度(中间位置)
RGB小灯控制逻辑:
按键A:白灯
按键B:蓝灯
按键C:红灯
按键3:关灯
按键2:切换到光传感器控制,环境光亮度低于30%白灯,大于30%小于70%蓝灯,高于70%变红灯
更多推荐



所有评论(0)