一、 TCS3200
TCS3200是TAOS公司推出的可编程彩色光到频率的转换器,它把可配置的硅光电二极管与电流频率转换器集成在一个单一的CMOS电路上,同时集成了三种颜色(RGB)的滤光器
,是业界第一个具有数字兼容接口的RGB彩色传感器。
引脚:
二、cubemx配置
1. 配置PA0-PA3为S0-S3输出
2. 增加一个串口作为python接口 buad 115200
3. tim2时钟源为外部,并且ARR最大防止溢出
4. tim1最为1s的中断 时钟源进行计数
三、keil编写代码
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 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 "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
float RGB_Scale[3]; //????3?RGB????
float fei[3] = {0};
int count=0; //?????
int cnt[3]; //????RGB????????
int flag = 0;
send_t send;
/* 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 */
/* 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 */
/* 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_TIM2_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim1);
HAL_TIM_Base_Start(&htim2);
S0_L;
S1_H;
HAL_Delay(5000);
RGB_Scale[0] = 255.0/ cnt[0]; //红色光比例因孿
RGB_Scale[1] = 255.0/ cnt[1] ; //绿色光比例因孿
RGB_Scale[2] = 255.0/ cnt[2] ; //蓝色光比例因孿
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(100);
for(int i=0; i<3; i++){
fei[i] = cnt[i] * RGB_Scale[i];
}
send.head = 0x5a;
send.tail = 0xa5;
send.R = fei[0];
send.G = fei[1];
send.B = fei[2];
HAL_UART_Transmit_DMA(&huart1,(uint8_t *)&send,sizeof(send_t));
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);
/** 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.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
RCC_OscInitStruct.PLL.PLLN = 85;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
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_4) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if(htim->Instance==htim1.Instance){
uint16_t count=__HAL_TIM_GET_COUNTER(&htim2);
switch(flag){
case 0:
S2_H;
S3_L;
//NOTHINT to do clear the counter
__HAL_TIM_SetCounter(&htim2,0);
S2_L;
S3_L;//红色
flag = 1 ;
break ;
case 1:
S2_H;
S3_H;//绿色
cnt[0] = count;//获取红色的数倿
__HAL_TIM_SetCounter(&htim2,0);
flag = 2 ;
break ;
case 2:
S2_L;
S3_H;//蓝色
cnt[1] = count;//获取绿色的数倿
__HAL_TIM_SetCounter(&htim2,0);
flag = 3 ;
break ;
case 3:
S2_L;
S3_L;
cnt[2] = count;//获取蓝色的数倿
__HAL_TIM_SetCounter(&htim2,0);
flag = 0 ;
break ;
}
}
}
/* 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 */
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) 2025 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 "stm32g4xx_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 -----------------------------------------------------------*/
#define S0_Pin GPIO_PIN_0
#define S0_GPIO_Port GPIOA
#define S1_Pin GPIO_PIN_1
#define S1_GPIO_Port GPIOA
#define S2_Pin GPIO_PIN_2
#define S2_GPIO_Port GPIOA
#define S3_Pin GPIO_PIN_3
#define S3_GPIO_Port GPIOA
/* USER CODE BEGIN Private defines */
#define S0_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
#define S0_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
#define S1_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
#define S1_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
#define S2_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
#define S2_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
#define S3_L HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET);
#define S3_H HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
typedef struct{
uint8_t head;
float R;
float G;
float B;
uint8_t tail;
} __attribute__((packed)) send_t;
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
这里有个小坑,
typedef struct{
uint8_t head;
float R;
float G;
float B;
uint8_t tail;
} __attribute__((packed)) send_t;
无__attribute__((packed))的话代码会优化,多发了几个0
四、python
import serial
import struct
import tkinter as tk
from tkinter import ttk
from threading import Thread
class RGBDisplayApp:
def __init__(self, root):
self.root = root
self.root.title("RGB 颜色接收显示器")
# 串口接收器实例
self.serial_receiver = SerialRGBReceiver()
# 创建UI组件
self.setup_ui()
# 连接串口
if not self.serial_receiver.connect():
self.status_label.config(text="串口连接失败", foreground="red")
def setup_ui(self):
# 主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)
# 串口控制区域
serial_frame = ttk.LabelFrame(main_frame, text="串口设置", padding="10")
serial_frame.pack(fill=tk.X, pady=5)
ttk.Label(serial_frame, text="端口:").grid(row=0, column=0, padx=5, pady=5)
self.port_entry = ttk.Entry(serial_frame)
self.port_entry.insert(0, self.serial_receiver.port)
self.port_entry.grid(row=0, column=1, padx=5, pady=5)
ttk.Label(serial_frame, text="波特率:").grid(row=0, column=2, padx=5, pady=5)
self.baudrate_entry = ttk.Entry(serial_frame)
self.baudrate_entry.insert(0, self.serial_receiver.baudrate)
self.baudrate_entry.grid(row=0, column=3, padx=5, pady=5)
self.connect_btn = ttk.Button(serial_frame, text="连接", command=self.toggle_connection)
self.connect_btn.grid(row=0, column=4, padx=5, pady=5)
# 颜色显示区域
color_frame = ttk.LabelFrame(main_frame, text="颜色显示", padding="10")
color_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# 颜色预览画布
self.color_canvas = tk.Canvas(color_frame, width=200, height=200, bg='white')
self.color_canvas.pack(pady=10)
# RGB值显示
value_frame = ttk.Frame(color_frame)
value_frame.pack(pady=5)
ttk.Label(value_frame, text="R:").grid(row=0, column=0, padx=5)
self.r_label = ttk.Label(value_frame, text="0.00", width=10)
self.r_label.grid(row=0, column=1, padx=5)
ttk.Label(value_frame, text="G:").grid(row=0, column=2, padx=5)
self.g_label = ttk.Label(value_frame, text="0.00", width=10)
self.g_label.grid(row=0, column=3, padx=5)
ttk.Label(value_frame, text="B:").grid(row=0, column=4, padx=5)
self.b_label = ttk.Label(value_frame, text="0.00", width=10)
self.b_label.grid(row=0, column=5, padx=5)
# 状态栏
self.status_label = ttk.Label(main_frame, text="准备就绪", relief=tk.SUNKEN)
self.status_label.pack(fill=tk.X, pady=5)
def toggle_connection(self):
if self.serial_receiver.ser and self.serial_receiver.ser.is_open:
self.serial_receiver.stop_receiving()
self.connect_btn.config(text="连接")
self.status_label.config(text="已断开连接", foreground="black")
else:
self.serial_receiver.port = self.port_entry.get()
self.serial_receiver.baudrate = int(self.baudrate_entry.get())
if self.serial_receiver.connect():
self.serial_receiver.start_receiving()
self.connect_btn.config(text="断开")
self.status_label.config(text="已连接", foreground="green")
else:
self.status_label.config(text="连接失败", foreground="red")
def update_rgb(self, r, g, b):
# 确保在UI线程中更新
self.root.after(0, lambda: self._update_rgb(r, g, b))
def _update_rgb(self, r, g, b):
# 更新RGB值显示
self.r_label.config(text=f"{r:.2f}")
self.g_label.config(text=f"{g:.2f}")
self.b_label.config(text=f"{b:.2f}")
# 确定RGB值的范围并归一化到0-1
max_val = max(r, g, b)
if max_val > 1:
# 0-255范围,转换为0-1
r_norm = r / 255.0
g_norm = g / 255.0
b_norm = b / 255.0
else:
# 已经是0-1范围
r_norm = r
g_norm = g
b_norm = b
# 确保归一化后的值在0-1范围内
r_norm = max(0, min(1, r_norm))
g_norm = max(0, min(1, g_norm))
b_norm = max(0, min(1, b_norm))
# 转换为16进制颜色代码
hex_color = "#{:02X}{:02X}{:02X}".format(
int(r_norm * 255),
int(g_norm * 255),
int(b_norm * 255)
)
# 更新颜色预览
self.color_canvas.config(bg=hex_color)
# 更新状态
self.status_label.config(text=f"接收中 - 当前颜色: {hex_color}", foreground="blue")
class SerialRGBReceiver:
def __init__(self, port='COM11', baudrate=115200):
self.port = port
self.baudrate = baudrate
self.ser = None
self.running = False
self.root = None
def set_root(self, root):
self.root = root
def connect(self):
try:
self.ser = serial.Serial(self.port, self.baudrate, timeout=1)
print(f"Connected to {self.port} at {self.baudrate} baud")
return True
except Exception as e:
print(f"Error connecting to serial port: {e}")
return False
def disconnect(self):
if self.ser and self.ser.is_open:
self.ser.close()
print("Disconnected from serial port")
def start_receiving(self):
if self.ser and self.ser.is_open:
self.running = True
self.receive_thread = Thread(target=self.read_data, daemon=True)
self.receive_thread.start()
def stop_receiving(self):
self.running = False
if self.ser and self.ser.is_open:
self.ser.close()
def read_data(self):
try:
while self.running:
data = self.ser.read(14)
if len(data) == 14:
head, r, g, b, tail = struct.unpack('<BfffB', data)
if head == 0x5A and tail == 0xA5:
print(f"Received RGB: Head={head}, R={r}, G={g}, B={b}, Tail={tail}")
if self.root:
self.root.update_rgb(r, g, b)
else:
print("Invalid frame head or tail")
else:
print("Received incomplete frame")
except struct.error as e:
print(f"Error unpacking data: {e}")
except Exception as e:
print(f"Error reading data: {e}")
if __name__ == "__main__":
root = tk.Tk()
app = RGBDisplayApp(root)
app.serial_receiver.set_root(app)
def on_closing():
app.serial_receiver.stop_receiving()
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
连结即可