STM32F103C8T6作为MODBUS从机驱动WS2812B

STM32F103C8T6作为MODBUS从机驱动WS2812B

终于实现了:

1、STM32F103C8T6驱动WS2812B;

2、通过Modbus协议给STM32F103C8T6发送指令,设置WS2812B显示方式;

显示方式有:

方向:正向        反向

模式:常亮    流水(正反显)    跑马(正反显)    两种颜色过渡跑马    单段亮度阶梯流水   呼吸     

颜色:有(黑、白、红、绿、蓝、黄、紫、青)8种预设,颜色9为自定义,可以自由定义      

3、2025-2-20更新,增加了将收到参数存入FLASH,重启后判断是刷机后重启还是正常重启,正常重启从FLASH读取上次的参数运行;

4、2025-2-20更新,增加了IWDG,防止死机,死机后自动重启,并用上次的参数直接运行;

5、通过GPIO灵活设置STM32F103C8T6从机地址。

6、2025-2-20更新,增加RS485通信模块,

        测试通过了上位机-UART-RS485-RS485-UART-F103  的数据发送与接收。

先记录一下,回头慢慢完善帖子。

工程文件如下(stm32cubeMX、MDK),这是旧的,其实都是stm32cubeMX生成的,就懒得上传新的了,关键的自己写的main.h、main.c完整的贴到最后,共同好一起研究:

【免费】STM32F103C8T6作为MODBUS从机驱动WS2812B资源-优快云文库

买过两种WS2812B的灯带,发现有所不同。

一种是“G-R-B”的数据顺序,一种是“R-G-B”的数据顺序。

根据情况,调整这一段代码,对应的“red”和“green”即可。

2025-2-20更新,解决了C6T6内部RAM小的问题:

/*二维数组存放最终PWM输出数组,每一行24个数据代表一个LED,最后一行24个0代表RESET码*/
uint16_t Pixel_Buf[141][24];

用uint16_t  存储每个灯的颜色参数,同时PWM的DMA发送选“HALFWORD”如下,这样在140灯的情况下,RAM占用刚好小于10240(10K),实测裸板C6T6跑起来了;

2025-2-20上午更新,为了进一步减少RAM占用,增加灯的数量,用  uint8_t  和  “BYTE”  试了,但是没有成功,以后再研究。

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* tim_pwmHandle)
{

  if(tim_pwmHandle->Instance==TIM2)
  {
  /* USER CODE BEGIN TIM2_MspInit 0 */
  /* USER CODE END TIM2_MspInit 0 */
    /* TIM2 clock enable */
    __HAL_RCC_TIM2_CLK_ENABLE();

    /* TIM2 DMA Init */
    /* TIM2_CH3 Init */
    hdma_tim2_ch3.Instance = DMA1_Channel1;
    hdma_tim2_ch3.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tim2_ch3.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim2_ch3.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim2_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_tim2_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_tim2_ch3.Init.Mode = DMA_NORMAL;
    hdma_tim2_ch3.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_tim2_ch3) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(tim_pwmHandle,hdma[TIM_DMA_ID_CC3],hdma_tim2_ch3);

  /* USER CODE BEGIN TIM2_MspInit 1 */
  /* USER CODE END TIM2_MspInit 1 */
  }
}

Keil 编译后的存储占用情况

Program Size: Code=14676 RO-data=312 RW-data=140 ZI-data=10020    

RAM=RW-data+ZI-data=140+10020=10160<10240(10k)

2025-2-20下午更新,今天真是个好日子,中午刚写了用  uint8_t  和 “BYTE” 下午就无意中找到一篇文章,测试通过了 。参考以下文章:

STM32 G431RB 中实现对WS2812灯带的控制(PWM + DMA) - magicduan - 博客园

内存用“byte”这样就省了RAM,给外设发送的时候用“HalfWord”,就没问题了。

同时为了对齐,定义buff的时候如下:

/*二维数组存放最终PWM输出数组,每一行24个数据代表一个LED,最后一行24个0代表RESET码*/
#define DMA_TYPE  uint8_t
__attribute__((aligned(4))) volatile  DMA_TYPE Pixel_Buf[281][24];

281灯编译程序后存储占用如下:

Program Size: Code=14672 RO-data=312 RW-data=140 ZI-data=9996  

RAM=RW-data+ZI-data=140+9996=10136<10240(10k)

O(∩_∩)O哈哈~O(∩_∩)O哈哈~O(∩_∩)O哈哈~O(∩_∩)O哈哈~O(∩_∩)O哈哈~

谢谢:

205-2-21更新:又深入的研究了一下代码,发现RAM占用还有潜力可挖,如下图main.h这段代码:

如果主机从机通讯的数据量很小,我的就是点儿配置数据,加上ModBus的协议字段,也就不超过30byte,这个几个数组可以整的更小,腾出更多的RAM给灯的数据用,还可以增加灯的数量,这里指提出思路,就不再测试了。

2025-2-20更新,解决了收到数据后进不到DMA空闲中断的问题:

参考的这个帖子:HAL库 串口空闲中断+DMA接收不定长数据 详解及踩坑_hal库串口空闲中断-优快云博客

 HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp, sizeof(xUSART1.BuffTemp));  					//开启DMA空闲中断

千万不要将上面的  “重新开启DMA空闲中断”  放在中断处理回调函数里

/*************************************************************************************************************
功能:usart接收中断处理函数
*************************************************************************************************************/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    if (huart == &huart1) {                                                                   // 判断串口
        __HAL_UNLOCK(huart);                                                                  // 解锁串口状态
        xUSART1.receiveNUM  = Size;                                                           // 把接收字节数,存入结构体xUSART1.ReceiveNum
        memset(Modbus.receive, 0, sizeof(Modbus.receive));					                          // 清除Modbus.receive前一帧接收到的数据
        memset(xUSART1.receiveData, 0, sizeof(xUSART1.receiveData));                          // 清除xUSART1.ReceiveData前一帧接收到的数据
        memcpy(xUSART1.receiveData, xUSART1.BuffTemp, Size);	                                // 把新数据,从临时缓存中,复制到xUSART1.ReceiveData[]
        memcpy(Modbus.receive, xUSART1.BuffTemp, Size);																				// 把新数据,从临时缓存中,复制到Modbus.receive

        // 再次开启DMA空闲中断;不要放在这里,会出现开启不了的可能,应放到中断处理函数里
        //HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp,sizeof(xUSART1.BuffTemp)); 
    }
}

而应放到                stm32f1xx_it.c                中断处理函数里:

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
    HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp,sizeof(xUSART1.BuffTemp));   	        
   // 再次开启DMA空闲中断; 一定要放在这里,否则可能会无法再次打开
  /* USER CODE END USART1_IRQn 1 */
}

经过不断修改又加了一些显示方式,就不修改上传的工程了,把最新的main.h、main.c直接贴出来:

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) 2024 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 */
	#include "stdbool.h"
/*这里是上文计算所得CCR的宏定义*/

#define CODE_1       (58)       																										//1码定时器计数次数

#define CODE_0       (28)       																										//0码定时器计数次数

/*建立一个定义单个LED三原色值大小的结构体*/

typedef struct {
    uint8_t number;  																																		//颜色序号
    uint8_t red;       																																	//红
    uint8_t green;     																																	//绿
    uint8_t blue;      																																	//蓝
} color_map_TypeDef;

/* 声明modbus的结构体 */
typedef struct {
    uint8_t address;                  /* 从机基地址 */
    uint8_t send[100];                /* 发送缓冲区 C8T6   100*/
    uint8_t receive[100];             /* 接收缓冲区  C8T6   100*/
} modbus_TypeDef;

extern modbus_TypeDef Modbus;         /* 声明modbus的结构体 */

typedef struct {                    /* 声明一个变量,用于管理变量 */
    uint16_t receiveNUM;            /* 接收字节数;在中段回调函数里被自动赋值;8>0即为接收到的第一帧  */
    uint8_t  receiveData[512];      /* 接受到的数据	C8T6   512 */
    uint8_t  BuffTemp[512];         /* 接收缓存;C8T6   512接收到一帧的时候就会清零并(一帧)复制到ReceivedData[ ] */
} xUSARTx_TypeDef;

/* 声明串口结构体 */
extern xUSARTx_TypeDef xUSART1; /* 声明串口1的结构体 */

/* 声明WB2812相关函数 */

void Reset_Load(void);																//该函数用于将数组最后24个数据变为0,代表RESET_code

void RGB_SendArray(void);															//发送最终数组

void RGB_RESET(uint16_t Pixel_Len);													//数据全部清零

void RGB_SetColor(uint16_t LedId, color_map_TypeDef Color);							//给一个LED装载24个颜色数据码(0码和1码)

void RGB_SetnColor(uint16_t LedSrart, uint16_t LedNumber, color_map_TypeDef Color); 	//给n个LED装载24个颜色数据码(0码和1码)

void RGB_COLOR(uint16_t Pixel_Len, color_map_TypeDef Color);								//指定长度LED显示指定颜色

void rgb_strip_f(uint16_t Pixel_Len, uint16_t Step,  color_map_TypeDef Color);							//指定长度LED显示指定颜色正向流水灯

void rgb_strip_f_rsv(uint16_t Pixel_Len, uint16_t Step, color_map_TypeDef Color);							//指定长度LED显示指定颜色"反显"正向流水灯

void rgb_strip_b(uint16_t Pixel_Len, uint16_t Step,  color_map_TypeDef Color);							//指定长度LED显示指定颜色反向流水灯

void rgb_strip_b_rsv(uint16_t Pixel_Len, uint16_t Step, color_map_TypeDef Color);							//指定长度LED显示指定颜色"反显"反向流水灯

void rgb_run_f(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color);		//指定长度LED显示指定颜色正向跑马灯

void rgb_run_f_rsv(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color);		//指定长度LED显示指定颜色"反显"正向跑马灯

void rgb_run_b(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color);		//指定长度LED显示指定颜色反向跑马灯

void rgb_run_b_rsv(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color);		//指定长度LED显示指定颜色"反显"反向跑马灯

void Set_Color_Trans(color_map_TypeDef color_1, color_map_TypeDef color_2, uint16_t len_color1, uint16_t len_in,
                     uint16_t len_color2); //分段颜色转换设置,第一段-过渡-第三段

void rgb_run_f_trans_rsv(color_map_TypeDef color_1, color_map_TypeDef color_2, uint16_t Pixel_Len, uint16_t Step,  uint16_t len_color1,
                         uint16_t len_color2);//正向分段颜色转换“反显”跑马,第一段-过渡-第三段

void rgb_run_b_trans_rsv(color_map_TypeDef color_1, color_map_TypeDef color_2, uint16_t Pixel_Len, uint16_t Step,  uint16_t len_color1,
                         uint16_t len_color2);//反向分段颜色转换“反显”跑马,第一段-过渡-第三段

void rgb_strip_trans(color_map_TypeDef color,uint16_t Pixel_Len,uint16_t Step);//按步长明暗渐变流水

void rgb_strip_trans_rsv(color_map_TypeDef color, uint16_t Pixel_Len, uint16_t Step);//按步长明暗渐变反向流水

void rgb_run_breathe(color_map_TypeDef color, uint16_t Pixel_Len, uint16_t Step);			//亮-灭-亮 呼吸灯

void rgb_run_breathe_rsv(color_map_TypeDef color, uint16_t Pixel_Len, uint16_t Step);			//灭-亮-灭 呼吸灯

bool flashWriteData(int paramSizeKb, uint16_t data[], size_t len);

void flashReadData(int paramSizeKb, uint16_t *data, size_t len);

/* 声明modbus的相关函数 */
void Modbus_Init(void);																//初始化modbus

void Modbus_processing(void);													//modbus命令处理主程序

void Function_03(void);																//modbus03命令处理程序

void Function_06(void);																//modbus06命令处理程序

void Function_16(void);																//modbus16命令处理程序

uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t buffer_length);						//modbusCRC-16处理程序

/* 声明求最大最小值相关函数 */
uint8_t max(uint8_t a, uint8_t b);

uint8_t min(uint8_t a, uint8_t b);

/* 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 */

/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 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 "iwdg.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//https://blog.youkuaiyun.com/2402_84849283/article/details/142520108?spm=1001.2014.3001.5506
#include "stdio.h"
#include <string.h>
#include <stdint.h>
#include "stdbool.h"
/* 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 */

/* 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 */
#define PAGE_START_ADDRESS 0x8000000
#define PARA_SIZE 26
/* 定义一些变量 */
uint16_t LED_NUM = 160;				//定义LED数量
uint16_t LED_STEP_n = 5;				//定义跑马灯长度
//
uint16_t LED_DIRECT = 0;			//定义运行方向,0正向,1反向
uint16_t LED_MOD = 0;					//定义模式,0全亮,1,流水,2跑马
//
uint8_t LED_COLOR1 = 0;				//定义灯颜色,0-7为预设(黑、白、红、绿、蓝、黄、青、紫,8可以自行设定颜色
uint8_t LED_COLOR2 = 0;				//定义灯颜色,0-7为预设(黑、白、红、绿、蓝、黄、青、紫,8可以自行设定颜色
//
uint8_t LED_LIGHT = 0;				//定义预设颜色的亮度
uint16_t delay_ms = 8;				//定义跑马灯正常的时间间隔
//
uint8_t red1  = 0;						  //定义自设定灯颜色 红1
uint8_t red2  = 0;						  //定义自设定灯颜色 红2
//
uint8_t green1  = 0;					  //定义自设定灯颜色 绿1
uint8_t green2  = 0;					  //定义自设定灯颜色 绿2
//
uint8_t blue1  = 0;					  //定义自设定灯颜色 蓝1
uint8_t blue2  = 0;					  //定义自设定灯颜色 蓝2
//
uint16_t LEN_1 = 5;			//定义过渡色第一段
uint16_t LEN_2 = 8;				//定义过渡色第三段


/*呼吸灯暂未调通*/
uint16_t BRTH_STEP = 5;			//定义呼吸灯步长
uint8_t BREATHE_MAX = 0;					//定义呼吸灯的颜色最大值
uint8_t BREATHE_MIN = 0;					//定义呼吸灯的颜色最小值


/*二维数组存放最终PWM输出数组,每一行24个数据代表一个LED,最后一行24个0代表RESET码*/
#define DMA_TYPE  uint8_t
__attribute__((aligned(4))) volatile  DMA_TYPE Pixel_Buf[281][24];
//uint16_t Pixel_Buf[161][24];
/*
如果使用uint8_t
在tim.c  中   hdma_tim2_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
							hdma_tim2_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
占用内存为   Buf 的大小   161*24
如果使用uint16_t
在tim.c  中   hdma_tim2_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
							hdma_tim2_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
占用内存为   Buf 的大小   161*24*2
如果使用uint32_t
在tim.c  中   hdma_tim2_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
							hdma_tim2_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
占用内存为   Buf 的大小   161*24*4

外设必须为  HALFWORD 或 WORD,内存可以  WORD  HALFWORD  BYTE,BYTE的话必须如上对齐,其他不必要。
*/
/* 定义颜色结构体数组 */
color_map_TypeDef color_list[9] = {				//预制8种颜色,第9种可以通过指令自行设定
    {0, 0, 0, 0},													//预制黑色
    {1, 255, 255, 255},										//预制白色
    {2, 255, 0, 0},												//预制红色
    {3, 0, 255, 0},												//预制绿色
    {4, 0, 0, 255},												//预制蓝色
    {5, 255, 255, 0},											//预制黄色
    {6, 0, 255, 255},											//预制青色
    {7, 255, 0, 255},											//预制紫色
    {8, 255, 255, 255},										//用于自由设定颜色
};

/* modbus寄存器数据 */
uint16_t Reg[] = {0x1403,		/*0 fff 灯带长度	 |	f 跑马灯步长 */
                  0x0002,		/*1 ff 方向(0,正向;1,反向	 | ff	模式(0,全亮;1,流水;2,跑马;3,反显流水;4,反显跑马;反显过度色*/
                  0x0203,		/*2	ff 标准颜色1	     |	ff 标准颜色2*/
                  0x3508,		/*3 ff 标准颜色亮度	 |	ff 正常时延 */
                  0x0000,		/*4 ff 自定义红1	     |	ff 自定义红2*/
                  0x0001,		/*5 ff 自定义绿1	     |	ff 自定义绿2*/
                  0x0900,		/*6 ff 自定义蓝1	     |	ff 自定义蓝2*/
                  0X0000,		/*7 ff 第一段长度     |  ff 第二段长度*/
                  0XF0F0		/*7 ffff 读取flash校验               */
                 };

uint16_t readData[9] = {0xA003, 0x0002, 0x0203, 0xc808, 0x0011, 0x1100, 0x3535, 0x2020, 0x00BB};

xUSARTx_TypeDef  xUSART1;			//定义USART结构体
modbus_TypeDef Modbus;    		//定义Modbus结构体
color_map_TypeDef color_1 = {0, 0, 0, 0};
color_map_TypeDef color_2 = {0, 0, 0, 0};

#pragma import(__use_no_semihosting)
struct __FILE {
    int handle;
};                                                         // 标准库需要的支持函数

FILE __stdout;                                             // FILE 在stdio.h文件
void _sys_exit(int x)
{
    x = x;                                                 // 定义_sys_exit()以避免使用半主机模式
}

int fputc(int ch, FILE *f)                                 // 重写fputc函数,使printf的输出由UART1实现,  这里使用USART1
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x02);
    return ch;
}


/* 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_USART1_UART_Init();
  MX_IWDG_Init();
  /* USER CODE BEGIN 2 */
    //uint16_t color_len = sizeof(color_list) / sizeof(color_list[0]);
    Modbus_Init(); //初始化Modbus地址代码
    /*************************************************************************************************************
    	下面是三种发送方式
        *发送方式1:阻塞式发送
        *发送方式2:中断式发送
        *发送方式3:DMA发送
    *************************************************************************************************************/
    static char  Example [] = "Hello World!\r\n";										//定义一个数组
    HAL_UART_Transmit(&huart1, (uint8_t *)Example, strlen(Example), 0xFFFF);     									//方式1
    HAL_UART_Transmit_IT(&huart1, (uint8_t *)Example, strlen(Example));														//方式2

    while ((&huart1)->gState != HAL_UART_STATE_READY);																						//等待中断式发送完成

    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Example, strlen(Example));													//方式3
    HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp, sizeof(xUSART1.BuffTemp));  					//开启DMA空闲中断
    flashReadData(PARA_SIZE, readData, 9);

    if (readData[8] == 61680) {
        for (int i = 0; i < 9; i++) {
            Reg[i] = readData[i];
        }
    } else {
    }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    while (1) {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);												//系统监视灯

        /*USART 接收 Modbus 处理*/
        if (xUSART1.receiveNUM) {                                	                 // 判断字节数
            //printf("\r<<<<< USART1 接收到一帧数据 \r");                           // 提示
            //printf("ASCII : %s\r", (char *)xUSART1.receiveData);                 // 显示数据,以ASCII方式显示,即以字符串的方式显示
            // printf("字节数:%d \r", xUSART1.receiveNUM);                         // 显示字节数
            //printf("\r\r");
            Modbus_processing();																									// Modbus命令处理主程序
            xUSART1.receiveNUM = 0;                      							            // 清0接收标记
        }

        /***********000***********************************************************************************************/
        LED_NUM = (Reg[0] >> 8) & 0x0FFF;
        if (LED_NUM>320){
            LED_NUM=320;
        }																											//获取 LED 长度
        LED_STEP_n = Reg[0] & 0x000F;																														//获取 LED 跑马灯步长
        /***********111***********************************************************************************************/
        LED_DIRECT = (Reg[1] >> 8) & 0x00FF;																									//获取 LED 方向
        LED_MOD = Reg[1] & 0x00FF;																														//获取 LED 模式

        if (LED_DIRECT > 1) {
            LED_DIRECT = 0;																								//方向超出范围,置为正向
        }

        if (LED_MOD > 7) {
            LED_MOD = 0;   																								//模式超出范围,置为全亮,模式0
        }

        /*********222*************************************************************************************************/
        LED_COLOR1 = (uint8_t)(Reg[2] >> 8);																										//获取 LED 标准颜色1序号
        LED_COLOR2 = (uint8_t)(Reg[2]);																												//获取 LED 标准颜色2序号
        /********333**************************************************************************************************/
        LED_LIGHT = (uint8_t)(Reg[3] >> 8);																												//获取 LED 亮度
        delay_ms = Reg[3]  & 0x00FF;																										//获取 LED 时延
        /*******444***************************************************************************************************/
        red1 = (uint8_t)(Reg[4] >> 8);																														//获取 LED 时延
        red2  = (uint8_t)(Reg[4]);																												//获取 LED 自选 红色亮度
        /********555**************************************************************************************************/
        green1 = (uint8_t)(Reg[5] >> 8);																														//获取 LED 时延
        green2  = (uint8_t)(Reg[5]);																												//获取 LED 自选 红色亮度
        /*********666*************************************************************************************************/
        blue1 = (uint8_t)(Reg[6] >> 8);																														//获取 LED 时延
        blue2  = (uint8_t)(Reg[6]);																												//获取 LED 自选 红色亮度
        /********777**************************************************************************************************/
        LEN_1 = (Reg[7] >> 8) & 0x00FF;																													//获取 流水灯步长
        LEN_2 = Reg[7] & 0x00FF;																													//获取 呼吸灯步长
        /*************************************************************************************************************/

        if (LED_COLOR1 == 8) {
            color_1.red = red1;											//设定 LED 自选1 红色亮度
            color_1.green = green1;									//设定 LED 自选1 绿色亮度
            color_1.blue = blue1;										//设定 LED 自选1 蓝色亮度
        } else if (LED_COLOR1 < 8) {
            color_1.red = color_list[LED_COLOR1].red &LED_LIGHT;											//设定 LED 红色亮度
            color_1.green = color_list[LED_COLOR1].green &LED_LIGHT;									//设定 LED 绿色亮度
            color_1.blue = color_list[LED_COLOR1].blue &LED_LIGHT;										//设定 LED 蓝色亮度
        } else {
            color_1.red = 0;											//超出颜色数量范围全置零
            color_1.green = 0;										//超出颜色数量范围全置零
            color_1.blue = 0;										//超出颜色数量范围全置零
        }

        if (LED_COLOR2 == 8) {
            color_2.red = red2;											//设定 LED 自选2 红色亮度
            color_2.green = green2;									//设定 LED 自选2 绿色亮度
            color_2.blue = blue2;										//设定 LED 自选2 蓝色亮度
        } else if (LED_COLOR2 < 8) {
            color_2.red = color_list[LED_COLOR2].red &LED_LIGHT;											//设定 LED 红色亮度
            color_2.green = color_list[LED_COLOR2].green &LED_LIGHT;									//设定 LED 绿色亮度
            color_2.blue = color_list[LED_COLOR2].blue &LED_LIGHT;										//设定 LED 蓝色亮度
        } else {
            color_2.red = 0;											//超出颜色数量范围全置零
            color_2.green = 0;										//超出颜色数量范围全置零
            color_2.blue = 0;										//超出颜色数量范围全置零
        }

        //判断方向功能码
        if (LED_DIRECT == 0) {													//判断方向功能码正向

            //判断模式功能码
            if (LED_MOD == 0) {	 												//判断模式功能码 全亮
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据  经测试,这些关闭打开没有也可以运行,以后再深入研究,当时是无法在 驱动WS2812事printf,所以加了这些打开关闭
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                RGB_COLOR(LED_NUM, color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 1)  {  								//判断模式功能码 流水灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_strip_f(LED_NUM, LED_STEP_n, color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 2) { 									//判断模式功能码 跑马灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_f(LED_NUM, LED_STEP_n,  color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 3)  {  								//判断模式功能码 流水灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_strip_f_rsv(LED_NUM, LED_STEP_n, color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 4) { 									//判断模式功能码 跑马灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_f_rsv(LED_NUM, LED_STEP_n,  color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 5) { 									//判断模式功能码 呼吸灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_f_trans_rsv(color_1,  color_2, LED_NUM, LED_STEP_n,  LEN_1,  LEN_2);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 6) { 									//判断模式功能码 呼吸灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_strip_trans(color_1, LED_NUM, LED_STEP_n);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 7) { 									//判断模式功能码 呼吸灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_breathe(color_1, LED_NUM, LED_STEP_n);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            }else {
                break;
            }
        } else if (LED_DIRECT == 1)  { 									//判断方向功能码反向

            //判断模式功能码
            if (LED_MOD == 0) {  												//判断模式功能码 全亮
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                RGB_COLOR(LED_NUM, color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 1) {  								//判断模式功能码 流水灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_strip_b(LED_NUM, LED_STEP_n, color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 2) { 									//判断模式功能码 跑马灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_b(LED_NUM, LED_STEP_n, color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 3)  {  								//判断模式功能码 流水灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_strip_b_rsv(LED_NUM, LED_STEP_n, color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 4) { 									//判断模式功能码 跑马灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_b_rsv(LED_NUM, LED_STEP_n,  color_1);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 5) { 									//判断模式功能码 呼吸灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_b_trans_rsv(color_1,  color_2, LED_NUM, LED_STEP_n,  LEN_1,  LEN_2);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 6) { 									//判断模式功能码 呼吸灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_strip_trans_rsv(color_1, LED_NUM, LED_STEP_n);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            } else if (LED_MOD == 7) { 									//判断模式功能码 呼吸灯
                HAL_NVIC_DisableIRQ(DMA1_Channel4_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_DisableIRQ(DMA1_Channel5_IRQn);														 //关闭串口DMA通道,以便DMA发送WS2812数据
                HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);														 	 //开启WS2812用DMA通道,以便DMA发送WS2812数据
                rgb_run_breathe_rsv(color_1, LED_NUM, LED_STEP_n);
                HAL_NVIC_DisableIRQ(DMA1_Channel1_IRQn);														 //关闭WS2812DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
                HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);														 	 //开启串口用DMA通道,以便DMA发送串口数据
            }else {
                break;
            }
        } else {
            break;
        }

        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);												//系统监视灯
        HAL_Delay(100);
				HAL_IWDG_Refresh(&hiwdg);
    }

  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.LSIState = RCC_LSI_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();
  }
}

/* USER CODE BEGIN 4 */

/*************************************************************************************************************
功能:最后一行装载24个0,输出24个周期占空比为0的PWM波,作为最后reset延时,
		 这里总时长为24*1.2=30us > 24us(要求大于24us)
*************************************************************************************************************/

void Reset_Load(void)
{
    uint8_t i;

    for (i = 0; i < 24; i++) {
        Pixel_Buf[LED_NUM][i] = 0;
    }
}

/*************************************************************************************************************
功能:发送数组
参数:(&htim1)定时器1,
     (TIM_CHANNEL_2)通道2,
		 ((uint32_t *)Pixel_Buf)待发送数组,
		 (Pixel_NUM+1)*24)发送个数,数组行列相乘
*************************************************************************************************************/

void RGB_SendArray(void)
{
    HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_3, (uint32_t *)Pixel_Buf, (LED_NUM + 1) * 24);
}

/*************************************************************************************************************
功能:数据全部清零
参数:Pixel_Len为显示LED个数
*************************************************************************************************************/

void RGB_RESET(uint16_t Pixel_Len)
{
    uint16_t i;

    for (i = 0; i < Pixel_Len; i++) { //给对应个数LED写入黑色
        RGB_SetColor(i, color_list[0]);
    }
}

/*************************************************************************************************************
功能:设定单个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId 为LED序号,
     Color 为定义的颜色结构体
*************************************************************************************************************/

void RGB_SetColor(uint16_t LedId, color_map_TypeDef Color)
{
    uint8_t i;

    if (LedId > LED_NUM) {
        return;    //avoid overflow 防止写入ID大于LED总数
    }

    for (i = 0; i < 8; i++) {
        Pixel_Buf[LedId][i]   = ((Color.red & (1 << (7 - i))) ? (CODE_1) : CODE_0);    //数组某一行0~7转化存放G
    }

    for (i = 8; i < 16; i++) {
        Pixel_Buf[LedId][i]   = ((Color.green & (1 << (15 - i))) ? (CODE_1) : CODE_0);    //数组某一行8~15转化存放R
    }

    for (i = 16; i < 24; i++) {
        Pixel_Buf[LedId][i]   = ((Color.blue & (1 << (23 - i))) ? (CODE_1) : CODE_0);    //数组某一行16~23转化存放B
    }
}

/*************************************************************************************************************
功能:设定n个RGB LED的颜色,把结构体中RGB的24BIT转换为0码和1码
参数:LedId 为LED序号,
     LedNumber 为需要显示的LED数量,
     Color 定义的颜色结构体
*************************************************************************************************************/

void RGB_SetnColor(uint16_t LedStart, uint16_t LedNumber, color_map_TypeDef Color)
{
    uint16_t i, j;

    if (LedStart > LED_NUM) {
        return;    //avoid overflow 防止写入ID大于LED总数
    }

    if ((LedStart + LedNumber) > LED_NUM) {
        LedNumber = LED_NUM - LedStart; //avoid overflow 防止写入ID大于LED总数
    }

    for (j = LedStart; j < LedStart + LedNumber; j++) {
        for (i = 0; i < 8; i++) {
            Pixel_Buf[j][i]   = ((Color.red & (1 << (7 - i))) ? (CODE_1) : CODE_0);    //数组某一行0~7转化存放G
        }

        for (i = 8; i < 16; i++) {
            Pixel_Buf[j][i]   = ((Color.green & (1 << (15 - i))) ? (CODE_1) : CODE_0);    //数组某一行8~15转化存放R
        }

        for (i = 16; i < 24; i++) {
            Pixel_Buf[j][i]   = ((Color.blue & (1 << (23 - i))) ? (CODE_1) : CODE_0);    //数组某一行16~23转化存放B
        }
    }
}

/*************************************************************************************************************
功能:显示指定颜色
参数:Pixel_Len为显示LED总个数
     Color 为颜色RGB结构体
*************************************************************************************************************/

void RGB_COLOR(uint16_t Pixel_Len, color_map_TypeDef Color)
{
    uint8_t i;

    for (i = 0; i < Pixel_Len / 10 ; i++) {
        RGB_SetnColor(0, Pixel_Len, Color);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms * 10);
    }
}


/*************************************************************************************************************
功能:显示指定颜色正向跑马灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
		 Pixel_Num 为多少个灯亮
*************************************************************************************************************/
void rgb_run_f(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color)
{
    uint16_t  i;

    //前Pixel_Num个,从1个开始亮
    for (i = 0; i < Pixel_Num; i++) {
        RGB_RESET(Pixel_Len);
        RGB_SetnColor(0, i,  Color);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }

    //从第Pixel_Num个,整体亮并流动,最后逐个消逝
    for (i = 1; i < Pixel_Len; i++) {
        RGB_RESET(Pixel_Len);
        RGB_SetnColor(i, Pixel_Num,  Color);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:显示指定颜色"反显"正向跑马灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
		 Pixel_Num 为多少个灯亮
*************************************************************************************************************/
void rgb_run_f_rsv(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color)
{
    uint16_t  i;

    //前Pixel_Num个,从1个开始亮
    for (i = 0; i < Pixel_Num; i++) {
        RGB_SetnColor(0, Pixel_Len,  Color);
        RGB_SetnColor(0, i,  color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }

    //从第Pixel_Num个,整体亮并流动,最后逐个消逝
    for (i = 1; i < Pixel_Len; i++) {
        RGB_SetnColor(0, Pixel_Len,  Color);
        RGB_SetnColor(i, Pixel_Num,  color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:显示指定颜色正向流水灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
*************************************************************************************************************/
void rgb_strip_f(uint16_t Pixel_Len, uint16_t Step,  color_map_TypeDef Color)
{
    uint16_t i;

    for (i = 0; i < Pixel_Len; i += Step) {
        RGB_RESET(Pixel_Len);
        RGB_SetnColor(0, i,  Color);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:显示指定颜色"反显"正向流水灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
*************************************************************************************************************/
void rgb_strip_f_rsv(uint16_t Pixel_Len, uint16_t Step,  color_map_TypeDef Color)
{
    uint16_t i;

    for (i = 0; i < Pixel_Len; i += Step) {
        RGB_SetnColor(0, Pixel_Len,  Color);
        RGB_SetnColor(0, i,  color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:显示指定颜色反向跑马灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
		 Pixel_Num 为多少个灯亮
*************************************************************************************************************/
void rgb_run_b(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color)
{
    uint16_t  i, j, Pixel_Num1;

    //前Pixel_Num个,从1个开始亮
    for (i = 0; i <  Pixel_Num; i++) {
        RGB_RESET(Pixel_Len);
        RGB_SetnColor(Pixel_Len - i, Pixel_Num,  Color);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }

    //从第Pixel_Num个,整体亮并流动,最后逐个消逝
    for (i = Pixel_Num; i < Pixel_Len + Pixel_Num; i++) {
        RGB_RESET(Pixel_Len);
        Pixel_Num1 = Pixel_Num;
        j = i;

        if (Pixel_Len < i) {
            Pixel_Num1 = Pixel_Num - (i - Pixel_Len);
            j = Pixel_Len;
        }

        RGB_SetnColor(Pixel_Len - j, Pixel_Num1,  Color);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:显示指定颜色"反显"反向跑马灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
		 Pixel_Num 为多少个灯亮
*************************************************************************************************************/
void rgb_run_b_rsv(uint16_t Pixel_Len, uint16_t Pixel_Num, color_map_TypeDef Color)
{
    uint16_t  i, j, Pixel_Num1;

    //前Pixel_Num个,从1个开始亮
    for (i = 0; i <  Pixel_Num; i++) {
        RGB_SetnColor(0, Pixel_Len,  Color);
        RGB_SetnColor(Pixel_Len - i, Pixel_Num,  color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }

    //从第Pixel_Num个,整体亮并流动,最后逐个消逝
    for (i = Pixel_Num; i < Pixel_Len + Pixel_Num; i++) {
        RGB_SetnColor(0, Pixel_Len,  Color);
        Pixel_Num1 = Pixel_Num;
        j = i;

        if (Pixel_Len < i) {
            Pixel_Num1 = Pixel_Num - (i - Pixel_Len);
            j = Pixel_Len;
        }

        RGB_SetnColor(Pixel_Len - j, Pixel_Num1,  color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:显示指定颜色反向流水灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
*************************************************************************************************************/
void rgb_strip_b(uint16_t Pixel_Len, uint16_t Step, color_map_TypeDef Color)
{
    uint16_t i;

    for (i = 0; i < Pixel_Len; i += Step) {
        RGB_RESET(Pixel_Len);
        RGB_SetnColor(Pixel_Len - i, i, Color);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:显示指定颜色"反显"反向流水灯
参数:Pixel_Len为显示LED个数
     Color 为颜色RGB结构体
*************************************************************************************************************/
void rgb_strip_b_rsv(uint16_t Pixel_Len, uint16_t Step,  color_map_TypeDef Color)
{
    uint16_t i;

    for (i = 0; i < Pixel_Len; i += Step) {
        RGB_SetnColor(0, Pixel_Len,  Color);
        RGB_SetnColor(Pixel_Len - i, i, color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:设定 分段 颜色 及 过渡段  正向流水
参数:color_1  第一段颜色
		 color_2  第三段颜色
     len_color1  第一段长度
     len_color2  第三段长度
     len_in      中间段长度
*************************************************************************************************************/
void Set_Color_Trans(color_map_TypeDef color_1, color_map_TypeDef color_2, uint16_t len_color1, uint16_t len_in, uint16_t len_color2)
{
    color_map_TypeDef color;
    uint8_t red_;
    uint8_t green_;
    uint8_t blue_;
    uint8_t red_step;
    uint8_t green_step;
    uint8_t blue_step;
    color.red = color_1.red;
    color.green = color_1.green;
    color.blue = color_1.blue;

    //计算红色步长
    if (color_1.red > color_2.red) {
        red_ = color_1.red - color_2.red;
    } else {
        red_ = color_2.red - color_1.red;
    }

    red_step = red_ / len_in + 1;

    //计算绿色步长
    if (color_1.green > color_2.green) {
        green_ = color_1.green - color_2.green;
    } else {
        green_ = color_2.green - color_1.green;
    }

    green_step = green_ / len_in + 1;

    //计算蓝色步长
    if (color_1.blue > color_2.blue) {
        blue_ = color_1.blue - color_2.blue;
    } else {
        blue_ = color_2.blue - color_1.blue;
    }

    blue_step = blue_ / len_in + 1;
    //
    RGB_RESET(len_color1 + len_in + len_color2);
    //设定颜色 第一段
    RGB_SetnColor(0, len_color1, color_1);
    //设定颜色 第三段
    RGB_SetnColor(len_color1 + len_in, len_color2, color_2);

    //设定  过渡 颜色
    for (int i = 0; i < len_in + 1; i++) {
        //红色过渡
        if (color_1.red > color_2.red) {
            if (color.red <= color_2.red  || color.red < red_step) {
                color.red = color_2.red;
            } else {
                color.red = color.red - red_step;
            }
        } else {
            if (color.red >= color_2.red || (255 - color.red) < red_step) {
                color.red = color_2.red;
            } else {
                color.red = color.red + red_step;
            }
        }

        //绿色过渡
        if (color_1.green > color_2.green) {
            if (color.green <= color_2.green  || color.green < green_step) {
                color.green = color_2.green;
            } else {
                color.green = color.green - green_step;
            }
        } else {
            if (color.green >= color_2.green || (255 - color.green) < green_step) {
                color.green = color_2.green;
            } else {
                color.green = color.green + green_step;
            }
        }

        //蓝色过渡
        if (color_1.blue > color_2.blue) {
            if (color.blue <= color_2.blue  || color.blue < blue_step) {
                color.blue = color_2.blue;
            } else {
                color.blue = color.blue - blue_step;
            }
        } else {
            if (color.blue >= color_2.blue || (255 - color.blue) < blue_step) {
                color.blue = color_2.blue;
            } else {
                color.blue = color.blue + blue_step;
            }
        }

        RGB_SetColor(len_color1 + i, color);
    }
}

/*************************************************************************************************************
功能:设定 分段 颜色 及 过渡段 颜色 反向流水
参数:color_1  第一段颜色
		 color_2  第三段颜色
     len_color1  第一段长度
     len_color2  第三段长度
     len_in      中间段长度
*************************************************************************************************************/
void rgb_run_f_trans_rsv(color_map_TypeDef color_1, color_map_TypeDef color_2, uint16_t Pixel_Len, uint16_t Step,  uint16_t len_color1,
                         uint16_t len_color2)
{
    for (int i = 0; i < Pixel_Len; i++) {
        Set_Color_Trans(color_1, color_2, len_color1, Pixel_Len - len_color1 - len_color2, len_color2);
        RGB_SetnColor(i, Step, color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:设定 分段 颜色 及 过渡段 颜色 反向流水
参数:color_1  第一段颜色
		 color_2  第三段颜色
     len_color1  第一段长度
     len_color2  第三段长度
     len_in      中间段长度
*************************************************************************************************************/
void rgb_run_b_trans_rsv(color_map_TypeDef color_1, color_map_TypeDef color_2, uint16_t Pixel_Len, uint16_t Step,  uint16_t len_color1,
                         uint16_t len_color2)
{
    for (int i = 0; i < Pixel_Len; i++) {
        Set_Color_Trans(color_1, color_2, len_color1,  Pixel_Len - len_color1 - len_color2, len_color2);
        RGB_SetnColor(Pixel_Len - i, Step, color_list[0]);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}


/*************************************************************************************************************
功能:设定一定长度同一颜色渐变流水灯
参数:color  颜色
     Pixel_Len  长度
     Step  步长
*************************************************************************************************************/
void rgb_strip_trans(color_map_TypeDef color, uint16_t Pixel_Len, uint16_t Step)
{
    color_map_TypeDef color_t;
    color_map_TypeDef color_t1;
    color_map_TypeDef color_t2;
    color_t.red = color.red;
    color_t.green = color.green;
    color_t.blue = color.blue;

    for (int k = 0; k < Pixel_Len; k++) {
        //写流动后空出的灯
        if (k == 0) {
        } else {
            int l = k - 1;

            for (int m = 0 ; m < (k / Step); m++) {
                for (int n = 0; n < Step; n++) {
                    if (color_t.red == 0) {
                    } else {
                        color_t1.red = color_t.red - (color_t.red / Step) * n;
                    }

                    if (color_t.green == 0) {
                    } else {
                        color_t1.green = color_t.green - (color_t.green / Step) * n;
                    }

                    if (color_t.blue == 0) {
                    } else {
                        color_t1.blue = color_t.blue - (color_t.blue / Step) * n;
                    }

                    if (l < 0) {
                        break;
                    } else {
                        RGB_SetColor(l,  color_t1);
                    }

                    l = l - 1;
                }
            }
        }

        //写前面流动的灯

        for (int j = 0; j < (Pixel_Len / Step); j++) {
            for (int i = 0; i < Step; i++) {
                if (color_t.red == 0) {
                } else {
                    color_t2.red = i*(color_t.red / Step);
                }

                if (color_t.green == 0) {
                } else {
                    color_t2.green = i*(color_t.green / Step);
                }

                if (color_t.blue == 0) {
                } else {
                    color_t2.blue = i*(color_t.blue / Step);
                }

                if (k + j *Step + i > Pixel_Len) {
                } else {
                    RGB_SetColor(k + j *Step + i,  color_t2);
                }
            }
        }

        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:设定一定长度同一颜色渐变反向流水灯
参数:color  颜色
     Pixel_Len  长度
     Step  步长
*************************************************************************************************************/
void rgb_strip_trans_rsv(color_map_TypeDef color, uint16_t Pixel_Len, uint16_t Step)
{
    color_map_TypeDef color_t;
    color_map_TypeDef color_t1;
    color_map_TypeDef color_t2;
    color_t.red = color.red;
    color_t.green = color.green;
    color_t.blue = color.blue;

    for (int k = 0; k < Pixel_Len; k++) {
        //写流动后空出的灯
        if (k == 0) {
        } else {
            int l = Pixel_Len - k - 1;

            for (int m = 0 ; m < ((Pixel_Len - k) / Step + 1); m++) {
                for (int n = 0; n < Step; n++) {
                    if (color_t.red == 0) {
                    } else {
                        color_t1.red = (color_t.red / Step) * (Step - n);
                    }

                    if (color_t.green == 0) {
                    } else {
                        color_t1.green = (color_t.green / Step) * (Step - n);
                    }

                    if (color_t.blue == 0) {
                    } else {
                        color_t1.blue = (color_t.blue / Step) * (Step - n);
                    }

                    if (l < 0) {
                        break;
                    } else {
                        RGB_SetColor(l,  color_t1);
                    }

                    l = l - 1;
                }
            }
        }

        //写前面流动的灯

        for (int j = 0; j < (Pixel_Len / Step); j++) {
            for (int i = 0; i < Step; i++) {
                if (color_t.red == 0) {
                } else {
                    color_t2.red = color_t.red - i*(color_t.red / Step);
                }

                if (color_t.green == 0) {
                } else {
                    color_t2.green = color_t.green - i*(color_t.green / Step);
                }

                if (color_t.blue == 0) {
                } else {
                    color_t2.blue = color_t.blue - i*(color_t.blue / Step);
                }

                if (Pixel_Len - k - 1 + j *Step + i > Pixel_Len) {
                } else {
                    RGB_SetColor(Pixel_Len - k - 1 + j *Step + i,  color_t2);
                }
            }
        }

        Reset_Load();
        RGB_SendArray();
        HAL_Delay(delay_ms);
    }
}

/*************************************************************************************************************
功能:设定按颜色呼吸灯
参数:color颜色
     Pixel_Len		长度
     Step  呼吸步长
*************************************************************************************************************/
void rgb_run_breathe(color_map_TypeDef color, uint16_t Pixel_Len, uint16_t Step)

{
    color_map_TypeDef color_t;
    color_t.red = color.red;
    color_t.green = color.green;
    color_t.blue = color.blue;

    for (int i = 0; i < Step; i++) {
        color_t.red = color.red - i *color.red / Step;
        color_t.green = color.green - i *color.green / Step;
        color_t.blue = color.blue - i *color.blue / Step;
        RGB_SetnColor(0, Pixel_Len, color_t);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(1000 / Step);
    }
		    for (int i = 0; i < Step+1; i++) {
        color_t.red = i *color.red / Step;
        color_t.green = i *color.green / Step;
        color_t.blue = i *color.blue / Step;
        RGB_SetnColor(0, Pixel_Len, color_t);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(1000 / Step);
    }
}

/*************************************************************************************************************
功能:设定按颜色呼吸灯
参数:color颜色
     Pixel_Len		长度
     Step  呼吸步长
*************************************************************************************************************/
void rgb_run_breathe_rsv(color_map_TypeDef color, uint16_t Pixel_Len, uint16_t Step)

{
    color_map_TypeDef color_t;
    color_t.red = color.red;
    color_t.green = color.green;
    color_t.blue = color.blue;

    for (int i = 0; i < Step; i++) {
        color_t.red = i *color.red / Step;
        color_t.green = i *color.green / Step;
        color_t.blue = i *color.blue / Step;
        RGB_SetnColor(0, Pixel_Len, color_t);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(1000 / Step);
    }
		    for (int i = 0; i < Step; i++) {
        color_t.red = color.red - i *color.red / Step;
        color_t.green = color.green - i *color.green / Step;
        color_t.blue = color.blue - i *color.blue / Step;
        RGB_SetnColor(0, Pixel_Len, color_t);
        Reset_Load();
        RGB_SendArray();
        HAL_Delay(1000 / Step);
    }
}

/*************************************************************************************************************
功能:modbus初始化,根据GPIO  PB3-PB10  设定地址
*************************************************************************************************************/
void Modbus_Init(void)
{
    Modbus.address = 0x00;          /* 基地址位00 */
    uint16_t a1 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3);
    uint16_t a2 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4);
    uint16_t a3 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5);
    uint16_t a4 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6);
    uint16_t a5 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7);
    uint16_t a6 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8);
    uint16_t a7 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9);
    uint16_t a8 = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10);
    Modbus.address = Modbus.address | (a1 << 0);
    Modbus.address = Modbus.address | (a2 << 1);
    Modbus.address = Modbus.address | (a3 << 2);
    Modbus.address = Modbus.address | (a4 << 3);
    Modbus.address = Modbus.address | (a5 << 4);
    Modbus.address = Modbus.address | (a6 << 5);
    Modbus.address = Modbus.address | (a7 << 6);
    Modbus.address = Modbus.address | (a8 << 7);
    printf("Modbus.address:%d \r\n", Modbus.address);                         	 // 显示Modbus地址
}

/*************************************************************************************************************
功能:mdbus命令处理主程序
*************************************************************************************************************/
void Modbus_processing(void)
{
    uint16_t crc, rccrc;                                                            //定义两个获取接收和计算校验码的变量
    crc = Modbus_CRC16(&Modbus.receive[0], xUSART1.receiveNUM - 2);                         //获取到接收的数据进行计算
    rccrc = (Modbus.receive[xUSART1.receiveNUM - 2] << 8) + Modbus.receive[xUSART1.receiveNUM - 1]; //获取接收数组的校验位

    if (crc == rccrc) {                                                                    //校验码相同才可以进入
        if (Modbus.receive[0] == Modbus.address) {                                         //判断基地址
            switch (Modbus.receive[1]) {                                                    //判断功能码
            case 3:
                Function_03();
                break;

            case 6:
                Function_06();
                break;

            case 16:
                Function_16();
                break;
            }
        } else if (Modbus.receive[0] == 0) { //基地址不一样返回空
        }
    }
}

/*************************************************************************************************************
功能03:主机读取从机多寄存器
*************************************************************************************************************/
void Function_03(void)
{
    uint16_t Regadd, Reglen, crc;
    uint8_t i, j;
    i = 0;
    Regadd = (Modbus.receive[2] << 8) + Modbus.receive[3]; //要读取寄存器的首地址
    Reglen = (Modbus.receive[4] << 8) + Modbus.receive[5]; //要读取寄存器个数
    //开始打包数据包
    Modbus.send[i++] = Modbus.address;                //ID号
    Modbus.send[i++] = 0x03;                          //功能码
    Modbus.send[i++] = ((Reglen * 2) % 256);          //返回字节个数

    for (j = 0; j < Reglen; j++) {
        Modbus.send[i++] = Reg[Regadd + j] >> 8;      //寄存器数据的高位;因为寄存器是16位,所以要分成2个8位发送
        Modbus.send[i++] = Reg[Regadd + j] % 256;     //寄存器数据的地位
    }

    crc = Modbus_CRC16(Modbus.send, i);               //计算校验码;同理校验码是16位
    Modbus.send[i++] = crc >> 8;                      //返回校验码高位
    Modbus.send[i++] = crc % 256;                     //返回校验码地位
    //打包好数据开始发送
    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Modbus.send, i);     // 发送方式DMA
}

/*************************************************************************************************************
功能06:主机写入从机单寄存器
*************************************************************************************************************/
void Function_06(void)
{
    uint16_t Regadd, crc, val;                        //地址;验证码;要修改的值;
    uint16_t i;
    i = 0;
    Regadd = (Modbus.receive[2] << 8) + Modbus.receive[3];  //获取要修改的地址
    val = (Modbus.receive[4] << 8) + Modbus.receive[5];     //获取修改的数值
    Reg[Regadd] = val;                                //修改数值
    //开始打包回应包
    Modbus.send[i++] = Modbus.address;                //从机地址
    Modbus.send[i++] = 0x06;                          //功能码
    Modbus.send[i++] = Regadd >> 8;                   //返回写入地址
    Modbus.send[i++] = Regadd % 256;
    Modbus.send[i++] = val >> 8;                      //返回写入数值
    Modbus.send[i++] = val % 256;
    crc = Modbus_CRC16(Modbus.send, i);               //校验码
    Modbus.send[i++] = crc >> 8;                      //返回校验码高位
    Modbus.send[i++] = crc % 256;                     //返回校验码地位
    flashWriteData(PARA_SIZE, Reg, 9);
    //数据打包完开始发回
    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Modbus.send, i);     // 发送方式DMA
}

/*************************************************************************************************************
功能16:主机写入从机多寄存器
*************************************************************************************************************/
void Function_16(void)
{
    uint16_t Regadd, crc, Regnum;                       //地址;验证码;要修改的值;
    uint16_t i;
    i = 0;
    Regadd = (Modbus.receive[2] << 8) + Modbus.receive[3];  //获取要修改的地址
    Regnum = (Modbus.receive[4] << 8) + Modbus.receive[5];  //获取要修改的数量
    uint16_t k = 7; // 跳过 1地址+1功能码+2寄存器起始地址+2寄存器数量+1数据数量

    for (uint16_t j = 0; j < Regnum; j++) {
        Reg[Regadd + j] = (uint16_t)((Modbus.receive[k] << 8) | Modbus.receive[k + 1]);
        k += 2;
    }

    //修改数值
    //开始打包回应包
    Modbus.send[i++] = Modbus.address;                //从机地址
    Modbus.send[i++] = 0x10;                          //功能码
    Modbus.send[i++] = Regadd >> 8;                   //返回写入地址
    Modbus.send[i++] = Regadd % 256;

    for (uint16_t j = 0; j < Regnum; j++) {													//返回写入数值
        Modbus.send[i++] = Reg[Regadd + j] >> 8;
        Modbus.send[i++] = Reg[Regadd + j] % 256;
    }

    flashWriteData(PARA_SIZE, Reg, 9);
    crc = Modbus_CRC16(Modbus.send, i);								               //校验码
    Modbus.send[i++] = crc >> 8;							                       //返回校验码高位
    Modbus.send[i++] = crc % 256;	  						                     //返回校验码地位
    //数据打包完开始发回
    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Modbus.send, i);       // 发送方式DMA
}

/*************************************************************************************************************
功能:modbus_CRC16处理程序
参数1:数组指针
参数2;长度
*************************************************************************************************************/

uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t buffer_length)
{
    uint16_t crc = 0xFFFF;

    for (uint16_t i = 0; i < buffer_length; i++) {
        crc ^= (uint16_t)buffer[i];

        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc = crc >> 1;
            }
        }
    }

    crc = (crc >> 8) | (crc << 8);
    return crc;
}

/*************************************************************************************************************
//paramSizeKb 程序大小,要写入FLASH的位置起始点
//data 准备写入数据
//len 大小
 *************************************************************************************************************/

bool flashWriteData(int paramSizeKb, uint16_t data[], size_t len)
{
    HAL_FLASH_Unlock();//解锁flash
    //准备擦除整页,写入前需要擦除
    FLASH_EraseInitTypeDef f;
    f.TypeErase = FLASH_TYPEERASE_PAGES;//页擦除
    f.PageAddress = PAGE_START_ADDRESS + (1024 * paramSizeKb);//擦除页地址
    f.NbPages = 1;//擦除1页
    uint32_t PageError = 0;
    HAL_FLASHEx_Erase(&f, &PageError);//擦除PageError == 0xFFFFFFFF表示成功

    if (PageError != 0xFFFFFFFF) {
        return false;
    }

    //开始写数据
    uint32_t startAddressIndex = PAGE_START_ADDRESS + (1024 * paramSizeKb);

    //startAddressIndex += 2,写入位置,每次+2是因为存储的类型是uint16_t,占用16bit,2字节
    for (int i = 0 ; i < len; i ++, startAddressIndex += 2) {
        //写入数据
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, startAddressIndex, data[i]);   //uint16_t  用  HALFWORD,uint32_t  用  WORD
    }

    //重新上锁
    HAL_FLASH_Lock();
    return true;
}

/*************************************************************************************************************
//读取数据
//paramSizeKb 程序大小,要读取FLASH的位置起始点
//data 读入数据
//len 读入数据长度
 *************************************************************************************************************/
void flashReadData(int paramSizeKb, uint16_t *data, size_t len)
{
    uint32_t startAddressIndex = PAGE_START_ADDRESS + (1024 * paramSizeKb);

    for (int i = 0; i < len; i++, startAddressIndex += 2) {
        data[i] = *(__IO uint32_t *)(startAddressIndex);
    }
}

/*************************************************************************************************************
功能:求两数的最大值
*************************************************************************************************************/
uint8_t max(uint8_t a, uint8_t b)

{
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

/*************************************************************************************************************
功能:求两数的最小值
*************************************************************************************************************/
uint8_t min(uint8_t a, uint8_t b)

{
    if (a < b) {
        return a;
    } else {
        return b;
    }
}

/*************************************************************************************************************
功能:usart接收中断处理函数
*************************************************************************************************************/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    if (huart == &huart1) {                                                                   // 判断串口
        __HAL_UNLOCK(huart);                                                                  // 解锁串口状态
        xUSART1.receiveNUM  = Size;                                                           // 把接收字节数,存入结构体xUSART1.ReceiveNum
        memset(Modbus.receive, 0, sizeof(Modbus.receive));					                          // 清除Modbus.receive前一帧接收到的数据
        memset(xUSART1.receiveData, 0, sizeof(xUSART1.receiveData));                          // 清除xUSART1.ReceiveData前一帧接收到的数据
        memcpy(xUSART1.receiveData, xUSART1.BuffTemp, Size);	                                // 把新数据,从临时缓存中,复制到xUSART1.ReceiveData[]
        memcpy(Modbus.receive, xUSART1.BuffTemp, Size);																				// 把新数据,从临时缓存中,复制到Modbus.receive
        //HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp,
        //                             sizeof(xUSART1.BuffTemp));   	// 再次开启DMA空闲中断;不要放在这里,会出现开启不了的可能,应放到中断处理函数里
    }
}

/* 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 */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值