智能教室控制系统

智能教室控制系统

引言

概述

该系统通过上位机C#控制下位机STM32,实现教室打卡考勤功能,通过打卡对数据库SQLserver进行增删改查,同时能将数据库的数据导出表格,下位机用到RFID的RC522模块,舵机、风扇,ws2812灯环。风扇通过调温度阈值,自动开关。上位机和下位机通过串口进行通信。

运用技术:C#桌面级winform程序、SQL server、stm32f103zet6单片机

技术路线:软件部分:C#:三方API接入 json解析、串口通信、调用数据库

硬件部分:Rfid射频刷卡,舵机,风扇

舵机模块
智能教室控制系统橙色线接脉冲线,红色线接+5V,黑色接GND;

RC522模块
智能教室控制系统
上位机共有5个界面,分别是注册,登录,忘记密码,主界面,导出表单。
智能教室控制系统

登录界面

智能教室控制系统

忘记密码界面

智能教室控制系统

注册界面

智能教室控制系统

导出表单

代码部分

下位机代码

主函数

#include "bsp.h"				/* 底层硬件驱动 */

#include "pwm.h"

/*
STM32 每个系列都会有唯一的一个芯片序列号(96 位 bit):
STM32F10X 的地址是 0x1FFFF7E8 
STM32F20X 的地址是 0x1FFF7A10
STM32F30X 的地址是 0x1FFFF7AC
STM32F40X 的地址是 0x1FFF7A10
STM32L1XX 的地址是 0x1FF80050
*/

/* SN 起始地址 */
#define STM32F10X_SN_ADDR 0x1FFFF7E8


/* 定义例程名和例程发布日期 */
#define EXAMPLE_NAME "WSNEP_V01-051_SPI 实验( RC522) "
#define EXAMPLE_DATE "2021-06-01"
#define DEMO_VER "1.0"


/* 定义 LCD 显示相关公共信息 */
#define DEV_NAME "Wisdomclassroom"
#define LCD_EXAMPLE_NAME "RFID"


const unsigned char str1[] = {0x08,0x00,0x29,0x00,0xCA,0x00,0x4C,0xFF,0x78,0x92,0x4C,0x92,0x4A,0x92,0x08,0x92,0x00,0x92,0x7E,0x92,0x42,0x92,0x42,0xFF,0x42,0x00,0x7E,0x00,0x00,0x00,0x00,0x00};/*"智",0*/
const unsigned char str2[] = {0x44,0x01,0x54,0x06,0x55,0x10,0xFF,0x50,0x55,0x56,0x55,0x51,0x45,0x51,0x01,0x59,0x45,0x55,0x55,0x51,0x55,0x51,0xFF,0x53,0x55,0xF0,0x54,0x04,0x44,0x03,0x00,0x00};/*"慧",1*/
const unsigned char str3[] = {0x04,0x20,0x25,0x50,0x25,0x92,0x25,0x11,0xFF,0x7E,0x25,0xA0,0x2D,0x20,0x14,0x21,0x21,0x02,0x0E,0x04,0xF1,0xC8,0x10,0x30,0x10,0xCC,0x1F,0x02,0x10,0x01,0x00,0x00};/*"教",2*/
const unsigned char str4[] = {0x08,0x02,0x30,0x02,0x24,0x12,0x24,0x92,0x25,0x92,0x26,0x92,0xA4,0x92,0x64,0xFE,0x24,0x92,0x24,0x92,0x25,0x92,0x24,0xD2,0x24,0x12,0x28,0x02,0x30,0x02,0x00,0x00};/*"室",3*/
const unsigned char str5[] = {0x02,0x00,0x42,0x00,0x33,0xFE,0x00,0x04,0x00,0x08,0x22,0x00,0x2A,0xFF,0x2A,0xA8,0x2A,0xA8,0xFE,0xA8,0x2A,0xAA,0x2A,0xA9,0x2A,0xFE,0x22,0x00,0x02,0x00,0x00,0x00};/*"请",4*/
const unsigned char str6[] = {0x00,0x08,0x00,0x70,0x7F,0x80,0x48,0xFC,0x48,0x80,0x48,0x80,0x4F,0xFF,0x48,0x88,0x48,0x84,0x78,0xF8,0x00,0x00,0x0F,0xF0,0x00,0x02,0x00,0x01,0xFF,0xFE,0x00,0x00};/*"刷",5*/
const unsigned char str7[] = {0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0xFF,0xFF,0x22,0x00,0x22,0x00,0x22,0x40,0x22,0x20,0x22,0x10,0x22,0x08,0x02,0x00,0x02,0x00,0x00,0x00};/*"卡",6*/



void IC_test( void )
{
	char cStr [ 30 ];
	char EptStr[30];
	uint8_t ucArray_ID [ 4 ]; /*先后存放 IC 卡的类型和 UID(IC 卡序列号)*/
	uint8_t ucStatusReturn; /*返回状态 */
	static u16 ucLineCount = 170; /* LCD 起始行为 170 */
	
	sprintf ( EptStr, "                "); /* LCD 显示空白,为清空 LCD 显示做准备 */
	/*寻卡*/
	if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )
	{
		/*若失败再次寻卡*/
		ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );
	}
	
	if ( ucStatusReturn == MI_OK )
	{
	//		printf("----------------------------\r\n");
	//		printf("0x%02X%02X\r\n",ucArray_ID [ 0 ],ucArray_ID [ 1 ]);
			/*防冲撞(当有多张卡进入读写器操作范围时,防冲突机制会从其中选择一张进行操作) */
		if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
		{
			sprintf ( cStr, "%02X%02X%02X%02X",ucArray_ID [ 0 ],ucArray_ID [ 1 ],ucArray_ID [ 2 ],ucArray_ID [ 3 ]);
			
			//printf("%s",ucArray_ID);		
			
//			USART1_Send_Data((uint8_t*)ucArray_ID,4);
			
			printf ( "%s",cStr );
			
			if ( ucLineCount == 290 ) /* 最多同时显示 6 行 */
			{
				for(ucLineCount=170;ucLineCount<=290;ucLineCount+=20)
				{
					LCD_ShowString(30,ucLineCount,200,16,16,(u8*)EptStr); //循环清空 6 行 LCD 显示
				}
				ucLineCount=170; /* 设置 LCD 起始行为 170 */
			}			
			LCD_ShowString(30,ucLineCount,200,16,16,(u8*)cStr); //显示读取到的卡号
			ucLineCount += 20; /* 设置下一次 LCD 显示行,自增 20 */
		}
	}
}


/*
*********************************************************************************************************
*	函 数 名: main
*	功能说明: c程序入口
*	形    参:无
*	返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{		
	uint8_t lcd_id[12]; /* 存放 LCD ID 字符串 */
//	uint8_t a0;
		
	/*
	ST 固件库中的启动文件已经执行了 SystemInit() 函数,该函数在 system_stm32f10x.c 文件,主要功能是
	配置 CPU 系统的时钟,内部 Flash 访问时序,配置 FSMC 用于外部 SRAM
	*/
	bsp_Init(); /* 硬件初始化 */
	TIM_Init();
//	SMBus_Init();//人体红外初始化
//	bsp_InitUART5(9600);/*注意,串口 4 初始化放在 LCD 之前*/
	TFTLCD_Init(); /* TFTLCD 初始化*/
	bsp_InitWS2812B(); /* 初始化 WS2812B */
	
	
	POINT_COLOR=BLUE;
	sprintf((char*)lcd_id,"LCD ID:%04X",lcddev.id);//将 LCD ID 打印到 lcd_id 数组。
	LCD_ShowString(30,40,200,24,24,(uint8_t*)DEV_NAME);
	LCD_ShowString(30,70,200,16,16,(uint8_t*)LCD_EXAMPLE_NAME);
	LCD_ShowString(30,90,200,16,16,(uint8_t*)DEMO_VER);
	LCD_ShowString(30,110,200,16,16,lcd_id); //显示 LCD ID
	LCD_ShowString(30,130,200,12,12,(uint8_t*)EXAMPLE_DATE);
	
	//汉字部分
	Show_Graph(30,150,(u8*)str1,16,0);
	Show_Graph(45,150,(u8*)str2,16,0);
	Show_Graph(60,150,(u8*)str3,16,0);
	Show_Graph(75,150,(u8*)str4,16,0);
	Show_Graph(105,150,(u8*)str5,16,0);
	Show_Graph(120,150,(u8*)str6,16,0);
	Show_Graph(135,150,(u8*)str7,16,0);
	
	bsp_InitRc522(); /* 初始化 RC522 的 SPI 端口 */
	PcdReset(); /* 复位 RC522 */
	
	M500PcdConfigISOType('A'); /* 设置工作方式 */
	
	bsp_StartAutoTimer(0, 300); /* 定时器 0 周期 300 毫秒 */
	bsp_StartAutoTimer(1, 500); /* 定时器 1 周期 500 毫秒 */
	while(1)
	{		
		bsp_Idle(); /* CPU 空闲时执行的函数,在 bsp.c */
		if (bsp_CheckTimer(0)) /* 定时到 */
		{
			bsp_LedToggle(2);		
		}
		if (bsp_CheckTimer(1)) /* 定时到 */
		{
			IC_test();
		}
	}
}

串口通信模块

#include "bsp.h"
#include "bsp_fan.h"
#include "bsp_uart5.h"

static const uint8_t temp[16] = {0XFD,0X00,0X0D,0X01,0X01,0XBB,0XD4,0XB8,0XE7,0XB4,0XF8,0XB4,0XF8,0XCE,0XD2,0XDC};
static const uint8_t temp1[16] = {0XFD,0X00,0X0D,0X01,0X01,0XC0,0XA4,0XB8,0XE7,0XB4,0XF8,0XB4,0XF8,0XCE,0XD2,0XD7};


#if EN_USART1_RX

uint8_t USART_RX_BUF[USART_REC_LEN];		//接收缓冲,最大USART_REC_LEN个字节,末字节为换行符
uint8_t ReceiveState=0;									//接收状态标记
uint16_t RxCounter=0;

u16 USART_RX_STA=0;       //接收状态标记

#endif 

/*
*********************************************************************************************************
*	函 数 名:bsp_InitUart
*	功能说明:初始化 CPU 的 USART1 串口硬件设备
*	形    参:无
*	返 回 值:无
*********************************************************************************************************
*/


/*
********************************************************************************************************
*/
//加入以下代码,支持 printf 函数,而不需要选择 use MicroLIB 
#if 1
#pragma import(__use_no_semihosting) 
/* 标准库需要的支持函数 */ 
struct __FILE 
{ 
int handle; 
}; 
FILE __stdout; 
/* 定义_sys_exit()以避免使用半主机模式 */ 

void _sys_exit(int x) 
{ 
x = x; 
} 
/*
*********************************************************************************************************
* 函 数 名: fputc
* 功能说明: 重定义 putc 函数,这样可以使用 printf 函数从串口 1 打印输出
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
int fputc(int ch, FILE *f)
{ 
while((USART1->SR&0X40)==0){};//;//循环发送,直到发送完毕 
 USART1->DR = (u8) ch; 
return ch;
}
#endif



void bsp_InitUart1(uint32_t baud)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
#if EN_USART1_RX	//如果使能了接收
	
	NVIC_InitTypeDef NVIC_InitStructure;
	
#endif
	
	/* 串口 1 TX = PA9 RX = PA10 */
	/* 第 1 步: 配置 GPIO */
	/* 打开 GPIO 时钟 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	/* 打开 UART 时钟 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	/* 配置 USART Tx 为复用功能 */ //USART1_TX GPIOA.9
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;		//PA.9
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;	//复用推挽输出	
	GPIO_Init(GPIOA,&GPIO_InitStructure);		//初始化 GPIOA.9
	
	/* 配置 USART Rx 为复用功能 */ //USART1_RX GPIOA.10 初始化
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;		//PA.10
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;	//浮空输入	
	GPIO_Init(GPIOA,&GPIO_InitStructure);		//初始化 GPIOA.10
	
	/* 第 2 步: 配置串口硬件参数 */
	USART_InitStructure.USART_BaudRate=baud;		//波特率
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;		//字长为8位数据格式
	USART_InitStructure.USART_StopBits=USART_StopBits_1;		//一个停止位
	USART_InitStructure.USART_Parity=USART_Parity_No;		//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;	//无硬件数据流控制
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;		//收发模式
	
	USART_Init(USART1,&USART_InitStructure);	//初始化串口
	
#if EN_USART1_RX		//如果使能了接收

	/* 第 3 步: Usart1 NVIC 配置 */
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;		//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;		//IRQ 通道使能
	NVIC_Init(&NVIC_InitStructure);		//根据指定的参数初始化 VIC 寄存器
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);		/* 开启串口接受中断 */
	USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);		 /* 开启串口空闲中断 */
	
#endif	
	/* 第 4 步: 使能串口 1 */
	USART_Cmd(USART1,ENABLE);  //使能串口	
}

/*
*********************************************************************************************************
*	函 数 名:USART1_IRQHandler
*	功能说明:USART1 中断,主要是接收中断和空闲中断
*	形    参:无
*	返 回 值:无
*********************************************************************************************************
*/
void USART1_IRQHandler(void)		//串口 1 中断服务程序
{

	u8 Res;	
	bsp_InitUART5(9600);/*注意,串口 4 初始化放在 LCD 之前*/	
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
	Res=USART_ReceiveData(USART1);	//读取接收到的数据
	bsp_InitFan(); /* 初始化风扇 */
	if(Res=='1' || Res==0x01)		//接收到1或0x01,正转舵机3s,停止
	{
		TIM_SetCompare1(TIM1,10);
		delay_ms(3000);
		TIM_SetCompare1(TIM1,15);
	}
	
	if(Res=='2' || Res==0x02)		//接收到2或0x02,反转转舵机3s,停止
	{
		TIM_SetCompare1(TIM1,20);
		delay_ms(3000);
		TIM_SetCompare1(TIM1,15);
	}
	
	if(Res=='3'||Res==0x03) 		//开灯环
	{
		WS2812_effect(1);
	}
	
	if(Res=='4'||Res==0x04)			//关灯环
	{
		WS2812_effect(0);
	}
	
	if(Res=='5'||Res==0x05)
	{
		bsp_FanOn(1);
		bsp_FanOn(2);
	}
	
	if(Res=='6'||Res==0x06)
	{
		bsp_FanOff(1);
		bsp_FanOff(2);
	}	
	
	if(Res=='7'||Res==0x07)
	{
		bsp_BeepToggle();
		delay_ms(200);
		bsp_BeepToggle();

	}
	
	if(Res=='8'||Res==0x08)
	{
		bsp_BeepOff;
	}
			
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);		
		
		USART_RX_STA=0;		
	}	

	
	/*
*********************************************************************************************************
* 函 数 名: USART1_Send_Data
* 功能说明: USART1 发送 len 个字。
* 形 参: buf:发送区首地址
* len:发送的字节数 0~255
* 返 回 值: 无
*********************************************************************************************************
*/
void USART1_Send_Data(uint8_t *buf,uint8_t len)
{
	uint8_t t;
	for(t=0;t<len;t++) //循环发送数据
	{
		while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
		USART_SendData(USART1,buf[t]);
	}
	while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
		




uart.h


#ifndef _BSP_UART_H_
#define _BSP_UART_H_

#include "sys.h"

#define USART_REC_LEN				1024		//定义最大接收字节数1024
#define EN_USART1_RX				1				//使能(1)/禁止(0)串口1接收

extern uint8_t USART_RX_BUF[USART_REC_LEN];		//接收缓冲,最大USART_REC_LEN个字节,末字节为换行符
extern uint8_t ReceiveState;									//接收状态标记
extern uint16_t RxCounter;

void bsp_InitUart1(uint32_t baud);

#if EN_USART1_RX		//如果使能了接收

void Uart0_STA_Clr(void);

void USART1_Send_Data(uint8_t *buf,uint8_t len);

#endif



#endif

/***************************** 德致伦电子 DeZLinc (END OF FILE) *********************************/

PWM模块部分

#include "pwm.h"

/*
*********************************************************************************************************
* 函 数 名: bsp_GetRCCofGPIO
* 功能说明: 根据 GPIO 得到 RCC 寄存器
* 形 参:无
* 返 回 值: GPIO 外设时钟名
*********************************************************************************************************
*/


static void TIM_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
 
  // 输出比较通道 GPIO 初始化
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 
	// BKIN引脚默认先输出低电平
	GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
 
static void Advance_TIM_Config(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	  // 开启定时器时钟,即内部时钟CK_INT=72M
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);
 
/*--------------------时基结构体初始化-------------------------*/
	   //TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 
	// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
	TIM_TimeBaseStructure.TIM_Period= (200-1);	
	// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
	TIM_TimeBaseStructure.TIM_Prescaler= (7200-1);	
	// 时钟分频因子 ,用于配置死区时间,没用到,随意
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;		
	// 计数器计数模式,设置为向上计数
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;		
	// 重复计数器的值,没用到,可以随意设置
	TIM_TimeBaseStructure.TIM_RepetitionCounter=0;	
	// 初始化定时器
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
 
	/*--------------------输出比较结构体初始化-------------------*/		
	
	// 配置为PWM模式2
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	// 输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	// 互补输出使能
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; 
	// 设置占空比大小
	TIM_OCInitStructure.TIM_Pulse = 0;
	// 输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	
	// 输出通道空闲电平极性配置
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
	
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
	
	// 使能计数器
	TIM_Cmd(TIM1, ENABLE);	
	// 主输出使能,当使用的是通用定时器时,这句不需要
	TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
 
void TIM_Init(void)
{
	TIM_GPIO_Config();
	Advance_TIM_Config();
}

ws2812.c

#include "bsp.h"

static void send_Data_rgb(uint8_t r,uint8_t g, uint8_t b);

uint16_t LED_BYTE_Buffer[LED_BYTE_Buffer_Size]; //发送数组缓存

/*
*********************************************************************************************************
* 函 数 名: bsp_InitWS2812B
* 功能说明: 配置 WS2812 灯相关的 GPIO 和 TIM PWM 功能
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/

extern const uint8_t test_color[9][3] = {
	{0, 0, 0 },//黑色(不亮)
	{32, 32, 32},//白色
	{64, 0, 0 },//红色
	{0, 64, 0 },//绿色
	{0, 0, 64},//蓝色
	{64, 64, 0 },//黄色
	{64, 32, 0 },//橙色
	{64, 0, 64},//紫红色
	{0, 64, 64},//青色
};

void bsp_InitWS2812B(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	
	RCC_APB2PeriphClockCmd(WS2812_GPIO_RCC, ENABLE); //使能 GPIO 时钟
	
	GPIO_InitStructure.GPIO_Pin = WS2812_GPIO_Pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(WS2812_GPIO_Port, &GPIO_InitStructure);
	
	RCC_APB1PeriphClockCmd(WS2812_TIM_RCC, ENABLE); //使能定时器 x 时钟
	
	/* Time base configuration */
	TIM_TimeBaseStructure.TIM_Period = 89; // 800kHz = 72M/(89+1)/(0+1)=800KHz
	TIM_TimeBaseStructure.TIM_Prescaler = 0;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
	TIM_TimeBaseInit(WS2812_TIM, &TIM_TimeBaseStructure); //根据 TIM_TimeBaseInitStruct 中指定的参数初始化TIMx 的时间基数单位
	
	/* PWM1 Mode configuration: Channelx */
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM 脉冲宽度调制模式 1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM 输出比较极性高
	
	WS2812_TIM_OCxInit(WS2812_TIM, &TIM_OCInitStructure); //配置输出设置函数
	/* 配置 DMA */
	
	/* 使能 DMA 时钟 */
	RCC_AHBPeriphClockCmd(WS2812_DMA_RCC, ENABLE);
	/* 配置 WS2812 所用 TIM 的 DMA 通道信息 */
	DMA_DeInit(WS2812_DMA_CH);
	
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&WS2812_DMA_ADDR; //DMA 外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LED_BYTE_Buffer; //DMA 内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设
	DMA_InitStructure.DMA_BufferSize = LED_BYTE_Buffer_Size; //DMA 通道的 DMA 缓存的大小
	
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为 16 位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为 16 位
	
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//工作在正常模式 // stop DMA feed after buffer size is reached
	DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA 通道 x 拥有高优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA 通道 x 没有设置为内存到内存传输
	
	DMA_Init(WS2812_DMA_CH, &DMA_InitStructure); //根据 DMA_InitStruct 中指定的参数初始化 DMA 的通道
	/* 只能使用通道 x TIMx_UP */
	TIM_DMACmd(WS2812_TIM, TIM_DMA_Update, ENABLE);
}
/*
*********************************************************************************************************
* 函 数 名: send_Data_rgb
* 功能说明: 发送一次数据
* 形 参: red 红色数值, green 绿色数值, blue 蓝色数值
* 返 回 值: 无
*********************************************************************************************************
*/
static void send_Data_rgb(uint8_t red,uint8_t green, uint8_t blue)
{
	
	uint8_t i=0;
	//注意需要发送的数据顺序高位->低位的顺序 是 绿->红->蓝
	uint32_t rgb_value = ( ( (uint32_t)green ) << 16 ) | ( ( (uint32_t)red ) << 8 ) | ( (uint32_t)blue );
	
	for(i=0;i<24;i++)
	{
		//判断最高位是否为 1,而赋值
		LED_BYTE_Buffer[i] = ( ( rgb_value << i ) & 0x800000 ) > 0 ? TIMING_ONE : TIMING_ZERO;
	}
	
	DMA_SetCurrDataCounter(WS2812_DMA_CH, LED_BYTE_Buffer_Size); // 装载需要发送的数据长度到 DMA 通道
	
	DMA_Cmd(WS2812_DMA_CH, ENABLE); // 使能对应的 DMA 通道
	
	TIM_Cmd(WS2812_TIM, ENABLE); // 使能对应的 TIMx
	
	while(!DMA_GetFlagStatus(WS2812_DMA_TC)); // 等待传输完毕
	
	TIM_Cmd(WS2812_TIM, DISABLE); // 失能对应的 TIMx
	DMA_Cmd(WS2812_DMA_CH, DISABLE); // 失能对应的 DMA 通道
	DMA_ClearFlag(WS2812_DMA_TC); // 清除对应的 DMA 传输完成标志位
	
}

/*
*********************************************************************************************************
* 函 数 名: ws2812Send
* 功能说明: 发送一次灯带数据
* 形 参: uint8_t (*color)[3] 是二维数组(按照[0]对应红色数值,[1]对应绿色数值,[2]对应蓝色数值)
* uint16_t len 需要同时操作的灯的数量(注意,不要超过 NBR_LEDS)
* 返 回 值: 无
*********************************************************************************************************
*/
void ws2812Send(uint8_t (*color)[3], uint16_t len)
{
	
	uint16_t i = 0;
	if(len>NBR_LEDS)
	{
		printf("操作的灯数量超出定义\r\n");
		return;
	}
	for(i = 0;i < NBR_LEDS; i++)
	{
		
		if(i<len)
		{
			send_Data_rgb(color[i][0],color[i][1],color[i][2]);  //调换rgb 灯环的颜色顺序导致不同
		}
		else
		{
			send_Data_rgb(0,0,0);//灭剩余的灯 ,注意,黑色的 rgb 为 0,0,0
		}
	}
}



/*
*********************************************************************************************************
* 函 数 名: WS2812_effect
* 功能说明: WS2812 效果
* 形 参: uint8_t effect 效果参数
* 返 回 值: 无
*********************************************************************************************************
*/
void WS2812_effect(uint8_t effect)
{
	uint8_t i=0;
	uint8_t buf[NBR_LEDS][3] = {0};
	for(i=0;i<NBR_LEDS;i++) //填充数值
	{
		buf[i][0] = test_color[effect][0];
		buf[i][1] = test_color[effect][1];
		buf[i][2] = test_color[effect][2];
	}
	ws2812Send(buf,NBR_LEDS); //显示
}



ws2812.h

#ifndef __BSP_WS2812_H
#define __BSP_WS2812_H
#include "sys.h"
/
移植修改区
按照实际端口修改
较前版本更加容易移植(注意,端口仅仅适用于非 JTAG/SWD 引脚,如果是 JTAG 引脚,需要打开 AFIO 时钟,并失能JTAG)
//注意,配合程序仅适用于 TIM2~TIM5 通用定时器

#define WS2812_GPIO_RCC RCC_APB2Periph_GPIOB //端口时钟
#define WS2812_GPIO_Pin GPIO_Pin_6 //引脚号
#define WS2812_GPIO_Port GPIOB //端口号
#define WS2812_TIM_RCC RCC_APB1Periph_TIM4 //定时器时钟

#define WS2812_TIM TIM4 //定时器
#define WS2812_TIM_OCxInit TIM_OC1Init //定时器比较函数
#define WS2812_DMA_RCC RCC_AHBPeriph_DMA1 //DMA 时钟

#define WS2812_DMA_ADDR TIM4->CCR1 //连接的外设基地址
#define WS2812_DMA_CH DMA1_Channel7 //DMAx 通道
#define WS2812_DMA_TC DMA1_FLAG_TC7 //DMAx 传输完成标志

/
#define TIMING_ONE 61 //T1H 1 码 68%占空比 68%*89=61
#define TIMING_ZERO 28 //T0L 0 码 32%占空比 32%*89=28
#define LED_BYTE_Buffer_Size 24 //RGB 共 24
#define NBR_LEDS 12 //WS2812 RGB 灯个数

/* 供外部调用的函数声明 */
void bsp_InitWS2812B(void);

void ws2812Send(uint8_t (*color)[3], uint16_t len);
void WS2812_effect(uint8_t effect);
#endif


RC522.c

#include "bsp.h"
/*
*********************************************************************************************************
* 函 数 名: bsp_InitRc522
* 功能说明: 初始化 RC522 的 IO 口和 SPI 功能配置
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitRc522(void)
{
	SPI_InitTypeDef SPI_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	
	RCC_APB2PeriphClockCmd( RCC_ALL_RC522, ENABLE ); //RC522 时钟使能
	GPIO_InitStructure.GPIO_Pin = RC522_CS_GPIO_PIN; // RC522_CS 推挽
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(RC522_CS_GPIO_PORT, &GPIO_InitStructure);
	
	
	GPIO_InitStructure.GPIO_Pin = RC522_RST_GPIO_PIN; // RC522_RST 推挽
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(RC522_RST_GPIO_PORT, &GPIO_InitStructure);
	
	
	RC522_CS = 1; //RC522 不选中
	RC522_RST = 1; //RC522 不复位
	bsp_InitSPIx(RC522_SPIx); //初始化 CLK,MISO,MOSI 三个端口和 SPI 时钟
	
	
	/* SPI 模式配置 */
	// FLASH 芯片 支持 SPI 模式 0,据此设置 CPOL CPHA
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	
	//SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(RC522_SPIx , &SPI_InitStructure);
	/* 使能 SPI */
	SPI_Cmd(RC522_SPIx , ENABLE);
}
/*
*********************************************************************************************************
* 函 数 名: SPI_RC522_SendByte
* 功能说明: 从 SPI 总线写入一个字节
* 形 参: byte:要写入的字节
* 返 回 值: 返回接收到的数据
*********************************************************************************************************
*/
uint8_t SPI_RC522_SendByte(uint8_t byte)
{
	return SPIx_ReadWriteByte(RC522_SPIx,byte);
}
/*
*********************************************************************************************************
* 函 数 名: SPI_RC522_ReadByte
* 功能说明: 从 SPI 总线读取一个字节
* 形 参: 无
* 返 回 值: 读取到的字节
*********************************************************************************************************
*/
uint8_t SPI_RC522_ReadByte(void)
{
	return (SPI_RC522_SendByte(RC522_Dummy_Byte));
}
/*
*********************************************************************************************************
* 函 数 名: ReadRawRC
* 功能说明: 读 RC522 寄存器
* 形 参: Address:寄存器地址
* 返 回 值: 读出的值
*********************************************************************************************************
*/
uint8_t ReadRawRC(uint8_t Address)
{
	uint8_t ucAddr;
	uint8_t ucResult=0;
	ucAddr = ( ( ( Address <<1 ) & 0x7E ) | 0x80 );
	/*选择 RC522: CS 低 */
	/* 通讯开始: CS 低 */
	RC522_CS = 0;
	SPI_RC522_SendByte( ucAddr );
	ucResult = SPI_RC522_ReadByte();
	/* 停止信号 RC522: CS 高 */
	/* 通讯开始: CS 低 */
	RC522_CS = 1;
	return ucResult;
}
/*
*********************************************************************************************************
* 函 数 名: ReadRawRC
* 功能说明: 写 RC522 寄存器
* 形 参: uint8_t Address:寄存器地址
* uint8_t value:写入的值
* 返 回 值: 无
*********************************************************************************************************
*/
void WriteRawRC(uint8_t Address, uint8_t value)
{
	uint8_t ucAddr;
	ucAddr = ( ( Address << 1 ) & 0x7E );
	/*选择 RC522: CS 低 */
	/* 通讯开始: CS 低 */
	RC522_CS = 0;
	SPI_RC522_SendByte( ucAddr );
	SPI_RC522_SendByte( value );
	/* 停止信号 RC522: CS 高 */
	/* 通讯开始: CS 低 */
	RC522_CS = 1;
}
/*
*********************************************************************************************************
* 函 数 名: SetBitMask
* 功能说明: 对 RC522 寄存器置位
* 形 参: uint8_t ucReg,寄存器地址
* uint8_t ucMask,置位值
* 返 回 值: 无
*********************************************************************************************************
*/
void SetBitMask ( uint8_t ucReg, uint8_t ucMask )
{
	uint8_t ucTemp = 0;
	ucTemp = ReadRawRC ( ucReg );
	WriteRawRC ( ucReg, ucTemp | ucMask ); // set bit mask
}
/*
*********************************************************************************************************
* 函 数 名: ClearBitMask
* 功能说明: 对 RC522 寄存器清位
* 形 参: uint8_t ucReg,寄存器地址
* uint8_t ucMask,清位值
* 返 回 值: 无
*********************************************************************************************************
*/
void ClearBitMask ( uint8_t ucReg, uint8_t ucMask )
{
	uint8_t ucTemp = 0;
	ucTemp = ReadRawRC ( ucReg );
	WriteRawRC ( ucReg, ucTemp & ( ~ ucMask) ); // clear bit mask
}
/*
*********************************************************************************************************
* 函 数 名: PcdAntennaOn
* 功能说明: 开启天线
* 形 参: 无
* 返 回 值: 无
* 注 意:每次启动或关闭天险发射之间应至少有 1ms 的间隔
*********************************************************************************************************
*/
void PcdAntennaOn ( void )
{
	uint8_t uc;
	uc = ReadRawRC ( TxControlReg );
	if ( ! ( uc & 0x03 ) )
	{
		SetBitMask( TxControlReg, 0x03 );
	}
}
/*
*********************************************************************************************************
* 函 数 名: PcdAntennaOff
* 功能说明: 关闭天线
* 形 参: 无
* 返 回 值: 无
* 注 意:每次启动或关闭天险发射之间应至少有 1ms 的间隔
*********************************************************************************************************
*/
void PcdAntennaOff ( void )
{
	ClearBitMask ( TxControlReg, 0x03 );
}
/*
*********************************************************************************************************
* 函 数 名: PcdReset
* 功能说明: 复位 RC522
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void PcdReset ( void )
{
	/* 复位 RC522: RST 高 */
	RC522_RST = 1;
	delay_us(10); //_NOP();
	/* 取消复位 RC522: RST 低 */
	RC522_RST = 0;
	delay_us(10); //_NOP();
	/* 复位 RC522: RST 高 */
	RC522_RST = 1;
	delay_us(10); //_NOP();
	WriteRawRC ( CommandReg, 0x0f );
	while ( ReadRawRC ( CommandReg ) & 0x10 );
	delay_us(10); //_NOP();
	WriteRawRC ( ModeReg, 0x3D ); //定义发送和接收常用模式 和 Mifare 卡通讯, CRC 初始值 0x6363
	WriteRawRC ( TReloadRegL, 30 ); //16 位定时器低位
	WriteRawRC ( TReloadRegH, 0 ); //16 位定时器高位
	WriteRawRC ( TModeReg, 0x8D ); //定义内部定时器的设置
	WriteRawRC ( TPrescalerReg, 0x3E ); //设置定时器分频系数
	WriteRawRC ( TxAutoReg, 0x40 ); //调制发送信号为 100%ASK
}
/*
*********************************************************************************************************
* 函 数 名: PcdComMF522
* 功能说明: 通过 RC522 和 ISO14443 卡通讯
* 形 参: ucCommand, RC522 命令字
* pInData,通过 RC522 发送到卡片的数据
* ucInLenByte,发送数据的字节长度
* pOutData,接收到的卡片返回数据
* pOutLenBit,返回数据的位长度
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit )
{
	char cStatus = MI_ERR;
	uint8_t ucIrqEn = 0x00;
	uint8_t ucWaitFor = 0x00;
	uint8_t ucLastBits;
	uint8_t ucN;
	uint32_t ul;
	switch ( ucCommand )
	{
		case PCD_AUTHENT: //Mifare 认证
		{
			ucIrqEn = 0x12; //允许错误中断请求 ErrIEn 允许空闲中断 IdleIEn
			ucWaitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
		}
		break;
		
		case PCD_TRANSCEIVE: //接收发送 发送接收
		{
			ucIrqEn = 0x77; //允许 TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
			ucWaitFor = 0x30; //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
		}
		break;
		
		default:break;
	}
	
	WriteRawRC ( ComIEnReg, ucIrqEn | 0x80 ); //IRqInv 置位管脚 IRQ 与 Status1Reg 的 IRq 位的值相反
	ClearBitMask ( ComIrqReg, 0x80 ); //Set1 该位清零时, CommIRqReg 的屏蔽位清零
	WriteRawRC ( CommandReg, PCD_IDLE ); //写空闲命令
	SetBitMask ( FIFOLevelReg, 0x80 ); //置位 FlushBuffer 清除内部 FIFO 的读和写指针以及 ErrReg 的BufferOvfl 标志位被清除
	for ( ul = 0; ul < ucInLenByte; ul ++ )
	{
		WriteRawRC ( FIFODataReg, pInData [ ul ] ); //写数据进 FIFOdata
	}
	
	WriteRawRC ( CommandReg, ucCommand ); //写命令
	
	if ( ucCommand == PCD_TRANSCEIVE )
	{
		SetBitMask(BitFramingReg,0x80); //StartSend 置位启动数据发送 该位与收发命令使用时才有效
	}
	ul = 1000;//根据时钟频率调整,操作 M1 卡最大等待时间 25ms
	do //认证 与寻卡等待时间
	{
		ucN = ReadRawRC ( ComIrqReg ); //查询事件中断
		ul --;
	} 
	while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); //退出条件 i=0,定时器中断,与写空闲命令
	ClearBitMask ( BitFramingReg, 0x80 ); //清理允许 StartSend 位
	if ( ul != 0 )
	{
		if ( ! ( ReadRawRC ( ErrorReg ) & 0x1B ) ) // 读 错 误 标 志 寄 存 器 BufferOfI CollErr ParityErrProtocolErr
		{
			cStatus = MI_OK;
			
			if ( ucN & ucIrqEn & 0x01 ) //是否发生定时器中断
			{
				cStatus = MI_NOTAGERR;
			}
			
			if ( ucCommand == PCD_TRANSCEIVE )
			{
				ucN = ReadRawRC ( FIFOLevelReg ); //读 FIFO 中保存的字节数
				ucLastBits = ReadRawRC ( ControlReg ) & 0x07; //最后接收到得字节的有效位数
				if ( ucLastBits )
				{
					* pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits; //N 个字节数减去 1(最后一个字节) +最后一位的位数 读取到的数据总位数
				}
				
				else
				{
					* pOutLenBit = ucN * 8; //最后接收到的字节整个字节有效
				}
				
				if ( ucN == 0 )
				{
					ucN = 1;
				}
				
				if ( ucN > MAXRLEN )
				{
					ucN = MAXRLEN;
				}
				
				for ( ul = 0; ul < ucN; ul ++ )
				{
					pOutData [ ul ] = ReadRawRC ( FIFODataReg );
				}
			}
		}
		
		else
		{
			cStatus = MI_ERR;
		}
	}
	
	SetBitMask ( ControlReg, 0x80 ); // stop timer now
	WriteRawRC ( CommandReg, PCD_IDLE );
	return cStatus;
}
/*
*********************************************************************************************************
* 函 数 名: M500PcdConfigISOType
* 功能说明: 设置 RC522 的工作方式
* 形 参: ucType,工作方式
* 返 回 值: 无
*********************************************************************************************************
*/
void M500PcdConfigISOType ( uint8_t ucType )
{
	if ( ucType == 'A') //ISO14443_A
	{
		ClearBitMask ( Status2Reg, 0x08 );
		
		WriteRawRC ( ModeReg, 0x3D ); //3F
		WriteRawRC ( RxSelReg, 0x86 ); //84
		WriteRawRC( RFCfgReg, 0x7F ); //4F
		WriteRawRC( TReloadRegL, 30 );//tmoLength);// TReloadVal = 'h6a =tmoLength(dec)
		WriteRawRC ( TReloadRegH, 0 );
		WriteRawRC ( TModeReg, 0x8D );
		WriteRawRC ( TPrescalerReg, 0x3E );
		
		delay_us ( 2 );
		PcdAntennaOn ();//开天线
	}
}
/*
*********************************************************************************************************
* 函 数 名: PcdRequest
* 功能说明: 寻卡
* 形 参: ucReq_code,寻卡方式
* = 0x52,寻感应区内所有符合 14443A 标准的卡
* = 0x26,寻未进入休眠状态的卡
* pTagType,卡片类型代码
* = 0x4400, Mifare_UltraLight
* = 0x0400, Mifare_One(S50)
* = 0x0200, Mifare_One(S70)
* = 0x0800, Mifare_Pro(X))
* = 0x4403, Mifare_DESFire
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType )
{
	char cStatus;
	uint8_t ucComMF522Buf [ MAXRLEN ];
	uint32_t ulLen;
	ClearBitMask ( Status2Reg, 0x08 ); //清理指示 MIFARECyptol 单元接通以及所有卡的数据通信被加密的情况
	WriteRawRC ( BitFramingReg, 0x07 ); // 发送的最后一个字节的 七位
	SetBitMask ( TxControlReg, 0x03 ); //TX1,TX2 管脚的输出信号传递经发送调制的 13.56 的能量载波信号
	ucComMF522Buf [ 0 ] = ucReq_code; //存入 卡片命令字
	cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, & ulLen ); //寻卡
	if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) ) //寻卡成功返回卡类型
	{
		*pTagType = ucComMF522Buf [ 0 ];
		*( pTagType + 1 ) = ucComMF522Buf [ 1 ];
	}
	
	else
	{
		cStatus = MI_ERR;
	}
	return cStatus;
}
/*
*********************************************************************************************************
* 函 数 名: PcdAnticoll
* 功能说明: 防冲撞
* 形 参: pSnr,卡片序列号, 4 字节
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdAnticoll ( uint8_t * pSnr )
{
	char cStatus;
	uint8_t uc, ucSnr_check = 0;
	uint8_t ucComMF522Buf [ MAXRLEN ];
	uint32_t ulLen;
	
	ClearBitMask ( Status2Reg, 0x08 ); //清 MFCryptol On 位 只有成功执行 MFAuthent 命令后,该位才能置位
	WriteRawRC ( BitFramingReg, 0x00); //清理寄存器 停止收发
	ClearBitMask ( CollReg, 0x80 ); //清 ValuesAfterColl 所有接收的位在冲突后被清除
	
	ucComMF522Buf [ 0 ] = 0x93; //卡片防冲突命令
	ucComMF522Buf [ 1 ] = 0x20;
	
	cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, & ulLen);//与卡片通信
	if ( cStatus == MI_OK) //通信成功
	{
		for ( uc = 0; uc < 4; uc ++ )
		{
			*( pSnr + uc ) = ucComMF522Buf [ uc ]; //读出 UID
			ucSnr_check ^= ucComMF522Buf [ uc ];
		}
		
		if ( ucSnr_check != ucComMF522Buf [ uc ] )
		{
			cStatus = MI_ERR;
		}
	}
	
	SetBitMask ( CollReg, 0x80 );
	
	return cStatus;
}
/*
*********************************************************************************************************
* 函 数 名: CalulateCRC
* 功能说明: 用 RC522 计算 CRC16
* 形 参:
* pIndata,计算 CRC16 的数组
* ucLen,计算 CRC16 的数组字节长度
* pOutData,存放计算结果存放的首地址
* 返 回 值: 无
*********************************************************************************************************
*/
void CalulateCRC ( uint8_t * pIndata, uint8_t ucLen, uint8_t * pOutData )
{
	uint8_t uc, ucN;
	
	ClearBitMask(DivIrqReg,0x04);
	WriteRawRC(CommandReg,PCD_IDLE);
	SetBitMask(FIFOLevelReg,0x80);
	
	for ( uc = 0; uc < ucLen; uc ++)
	{
		WriteRawRC ( FIFODataReg, * ( pIndata + uc ) );
	}
	
	WriteRawRC ( CommandReg, PCD_CALCCRC );
	uc = 0xFF;
	
	do
	{
		ucN = ReadRawRC ( DivIrqReg );
		uc --;
	} 
	while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );
	
	pOutData [ 0 ] = ReadRawRC ( CRCResultRegL );
	pOutData [ 1 ] = ReadRawRC ( CRCResultRegM );
}
/*
*********************************************************************************************************
* 函 数 名: PcdSelect
* 功能说明: 选定卡片
* 形 参: pSnr,卡片序列号, 4 字节
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdSelect ( uint8_t * pSnr )
{
	char ucN;
	uint8_t uc;
	uint8_t ucComMF522Buf [ MAXRLEN ];
	uint32_t ulLen;
	
	ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;
	ucComMF522Buf [ 1 ] = 0x70;
	ucComMF522Buf [ 6 ] = 0;
	
	for ( uc = 0; uc < 4; uc ++ )
	{
		ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );
		ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );
	}
	
	CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );
	ClearBitMask ( Status2Reg, 0x08 );
	ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );
	
	if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )
	{
		ucN = MI_OK;
	}
	else
	{
		ucN = MI_ERR;
	}
	return ucN;
}
/*
*********************************************************************************************************
* 函 数 名: PcdSelect
* 功能说明: 验证卡片密码
* 形 参:
* ucAuth_mode,密码验证模式
* = 0x60,验证 A 密钥
* = 0x61,验证 B 密钥
* ucAddr,块地址
* pKey,密码
* pSnr,卡片序列号, 4 字节
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr )
{
	char cStatus;
	uint8_t uc, ucComMF522Buf [ MAXRLEN ];
	uint32_t ulLen;
	
	ucComMF522Buf [ 0 ] = ucAuth_mode;
	ucComMF522Buf [ 1 ] = ucAddr;
	
	for ( uc = 0; uc < 6; uc ++ )
	{
		ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );
	}
	
	for ( uc = 0; uc < 6; uc ++ )
	{
		ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );
	}
	
	cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );
	
	if ( ( cStatus != MI_OK ) || ( ! ( ReadRawRC ( Status2Reg ) & 0x08 ) ) )
	{
		cStatus = MI_ERR;
	}
	return cStatus;
}
/*
*********************************************************************************************************
* 函 数 名: PcdWrite
* 功能说明: 写数据到 M1 卡一块
* 形 参:
* ucAddr,块地址
* pData,写入的数据, 16 字节
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdWrite ( uint8_t ucAddr, uint8_t * pData )
{
	char cStatus;
	uint8_t uc, ucComMF522Buf [ MAXRLEN ];
	uint32_t ulLen;
	
	ucComMF522Buf [ 0 ] = PICC_WRITE;
	ucComMF522Buf [ 1 ] = ucAddr;
	CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
	cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
	
	if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
	{
		cStatus = MI_ERR;
	}
	
	if ( cStatus == MI_OK )
	{
		//memcpy(ucComMF522Buf, pData, 16);
		for ( uc = 0; uc < 16; uc ++ )
		{
			ucComMF522Buf [ uc ] = * ( pData + uc );
		}
		
		CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );
		cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );
		
		if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
		{
			cStatus = MI_ERR;
		}
	}
	return cStatus;
}
/*
*********************************************************************************************************
* 函 数 名: PcdRead
* 功能说明: 读取 M1 卡一块数据
* 形 参:
* ucAddr,块地址
* pData,读出的数据, 16 字节
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdRead ( uint8_t ucAddr, uint8_t * pData )
{
	char cStatus;
	uint8_t uc, ucComMF522Buf [ MAXRLEN ];
	uint32_t ulLen;
	
	ucComMF522Buf [ 0 ] = PICC_READ;
	ucComMF522Buf [ 1 ] = ucAddr;
	
	CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );	
	cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
	if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) )
	{
		for ( uc = 0; uc < 16; uc ++ )
		*( pData + uc ) = ucComMF522Buf [ uc ];
	}
	
	else
	{
		cStatus = MI_ERR;
	}
	
	return cStatus;
}
/*
*********************************************************************************************************
* 函 数 名: PcdHalt
* 功能说明: 命令卡片进入休眠状态
* 形 参: 无
* 返 回 值: 状态值= MI_OK,成功
*********************************************************************************************************
*/
char PcdHalt( void )
{
	uint8_t ucComMF522Buf [ MAXRLEN ];
	uint32_t ulLen;
	
	ucComMF522Buf [ 0 ] = PICC_HALT;
	ucComMF522Buf [ 1 ] = 0;
	
	CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
	PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
	
	return MI_OK;
}
/***************************** 德致伦电子 DeZLinc (END OF FILE) *********************************/

RC522.h

#ifndef __BSP_RC522_H
#define __BSP_RC522_H
#include "sys.h"
/****************************************************************************/
#define RCC_ALL_RC522 ( RC522_CS_GPIO_CLK | RC522_RST_GPIO_CLK )
#define RC522_CS_GPIO_PIN GPIO_Pin_6 								//RC522_CS 引脚号
#define RC522_CS_PIN_ID 6 											//RC522_CS 引脚序号
#define RC522_CS_GPIO_PORT GPIOF 									//RC522_CS 端口号
#define RC522_CS_GPIO_CLK RCC_APB2Periph_GPIOF						//RC522_CS 时钟
#define RC522_CS_FUN_OUT PFout 										//RC522_CS 输出端口配置函数
//#define RC522_CS_FUN_IN PFin 										//RC522_CS 输入端口配置函数
#define RC522_RST_GPIO_PIN GPIO_Pin_7 								//RC522_RST 引脚号
#define RC522_RST_PIN_ID 7 											//RC522_RST 引脚序号
#define RC522_RST_GPIO_PORT GPIOB 									//RC522_RST 端口号
#define RC522_RST_GPIO_CLK RCC_APB2Periph_GPIOB 					//RC522_RST 时钟
#define RC522_RST_FUN_OUT PBout 									//RC522_RST 输出端口配置函数
//#define RC522_CS_FUN_IN PBin 										//RC522_RST 输入端口配置函数
#define RC522_SPIx SPI1 											//RC522 使用 SPI1 端口
/****************************************************************************/
//IO 操作函数
#define RC522_CS RC522_CS_FUN_OUT(RC522_CS_PIN_ID) 					//RC522_CS RC522 的片选信号
#define RC522_RST RC522_RST_FUN_OUT(RC522_RST_PIN_ID) 				//RC522_RST RC522 的复位信号

/
//MF522 命令字
/
#define PCD_IDLE 0x00 												//取消当前命令
#define PCD_AUTHENT 0x0E 											//验证密钥
#define PCD_RECEIVE 0x08 											//接收数据
#define PCD_TRANSMIT 0x04 											//发送数据
#define PCD_TRANSCEIVE 0x0C 										//发送并接收数据
#define PCD_RESETPHASE 0x0F 										//复位
#define PCD_CALCCRC 0x03 											//CRC 计算
/
//Mifare_One 卡片命令字
/
#define PICC_REQIDL 0x26 											//寻天线区内未进入休眠状态
#define PICC_REQALL 0x52 											//寻天线区内全部卡
#define PICC_ANTICOLL1 0x93 										//防冲撞
#define PICC_ANTICOLL2 0x95 										//防冲撞
#define PICC_AUTHENT1A 0x60 										//验证 A 密钥
#define PICC_AUTHENT1B 0x61 										//验证 B 密钥
#define PICC_READ 0x30 												//读块
#define PICC_WRITE 0xA0 											//写块
#define PICC_DECREMENT 0xC0 										//扣款
#define PICC_INCREMENT 0xC1 										//充值
#define PICC_RESTORE 0xC2 											//调块数据到缓冲区
#define PICC_TRANSFER 0xB0 											//保存缓冲区中数据
#define PICC_HALT 0x50 												//休眠
/
//MF522 FIFO 长度定义
/
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
#define MAXRLEN 18
/
//MF522 寄存器定义
/


// PAGE 0
#define RFU00 0x00
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivlEnReg 0x03
#define ComIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define RFU0F 0x0F


// PAGE 1
#define RFU10 0x10
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxAutoReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#define RFU1A 0x1A
#define RFU1B 0x1B
#define MifareReg 0x1C
#define RFU1D 0x1D
#define RFU1E 0x1E
#define SerialSpeedReg 0x1F


// PAGE 2
#define RFU20 0x20
#define CRCResultRegM 0x21
#define CRCResultRegL 0x22
#define RFU23 0x23
#define ModWidthReg 0x24
#define RFU25 0x25
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsCfgReg 0x28
#define ModGsCfgReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadRegH 0x2C
#define TReloadRegL 0x2D
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F


// PAGE 3
#define RFU30 0x30
#define TestSel1Reg 0x31
#define TestSel2Reg 0x32
#define TestPinEnReg 0x33
#define TestPinValueReg 0x34
#define TestBusReg 0x35
#define AutoTestReg 0x36
#define VersionReg 0x37
#define AnalogTestReg 0x38
#define TestDAC1Reg 0x39
#define TestDAC2Reg 0x3A
#define TestADCReg 0x3B
#define RFU3C 0x3C
#define RFU3D 0x3D
#define RFU3E 0x3E
#define RFU3F 0x3F


/
//和 MF522 通讯时返回的错误代码
/
#define MI_OK 0x26
#define MI_NOTAGERR 0xcc
#define MI_ERR 0xbb
#define RC522_Dummy_Byte 0xFF

/* 供外部调用的函数声明 */
void bsp_InitRc522(void);
void PcdReset ( void ); 												//复位
void M500PcdConfigISOType ( u8 type ); 									//工作方式
char PcdRequest ( u8 req_code, u8 * pTagType ); 						//寻卡
char PcdAnticoll ( u8 * pSnr); 											//读卡号
char PcdAuthState ( u8 ucAuth_mode, u8 ucAddr, u8 * pKey, u8 * pSnr );  //验证卡片密码
char PcdRead ( u8 ucAddr, u8 * pData ); 								//读卡
char PcdSelect ( u8 * pSnr );										    //选卡
char PcdHalt( void );

#endif

上位机部分
智能教室控制系统
主界面

using System;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Wisdomclassroom
{
    public partial class Main : Form
    {

        SerialClass sc;
        SQL sql;

        String wendu;



        public Main()
        {
            this.StartPosition = FormStartPosition.CenterScreen;
            InitializeComponent();
        }


        private void Main_Load(object sender, EventArgs e)
        {

            /*串口部分 */
            sc = new SerialClass("COM10", 9600);
            sc.openPort();
            sc.ReadData = Read1;
            
            /*连接数据库*/
            sql = new SQL("11.111.11.11", "xxxx", "xxx", "xxxxx");
            sql.Connect();

            /* 天气 API外接   json解析*/
            string str = API.PostWebRequest("http://apis.juhe.cn/simpleWeather/query", "city=%E5%A4%A9%E6%B4%A5&key=xxxxx");
            JObject json1 = JObject.Parse(str);//解析json Newtonsoft.Json.Linq.JObject class.
            string jieguo = json1["reason"].ToString();
            wendu = json1["result"]["realtime"]["temperature"].ToString();
            string info = json1["result"]["realtime"]["info"].ToString();
            string humidity = json1["result"]["realtime"]["humidity"].ToString();
           temp .Text = wendu;
           weather.Text = info;
           hum.Text  = humidity;


            int a = Convert.ToInt32(wendu);

            if (numericUpDown1.Value < a)
            {
                bo = true;
                byte[] data = { 0x05 };
                sc.SendData(data);
            }
            else 
            {
                bo = false;
                byte[] data = { 0x06 };
                sc.SendData(data);
          }

            /*启动time控件 */
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            /*显示系统时间、日期 */
            calendar.Text = DateTime.Now.ToString("yyy年MM月dd日");
            time.Text = DateTime.Now.ToString("HH时mm分ss秒");
        }

        public void Read(string readstr, byte[] temp)
        {   
                //byte[] data = { 0x05 };
                //sc.SendData(data);
                /* 下位机体温部分 */
            Invoke(new MethodInvoker(delegate ()
            {            
                //label1.Text = readstr+"°C";
               // MessageBox.Show(byteToHexStr(temp));
               //do something... 
            }));
        }

        public void Read1(string readstr, byte[] card)
        {
            /*读取数据库的数据 ,刷卡显示信息*/
            string xingming = sql.GetString(string.Format("select xingming from [login] where ID='{0}'", card));

            string arr = byteToHexStr(card);
            string sr = sql.GetString("select ID from [login] where ID='" + arr + "'");
            if (sr != "")
            {
                string id = sql.GetString("select ID from [login] where ID='" + arr + "'");
                string xm = sql.GetString("select xingming from [login] where ID='" + arr + "'");
                string xb = sql.GetString("select xingbie from [login] where ID='" + arr + "'");
                string xj = sql.GetString("select xueji from [login] where ID='" + arr + "'");

                MessageBox.Show("姓名:" + xm + '\n' + "性别:" + xb + '\n' + "学籍校:" + xj + '\n' + "ID:" + id+'\n'+"已打卡");
            }
            else
            {
                MessageBox.Show("卡号不存在");
            }
        }

        public string byteToHexStr(byte[] bytes)
        {
            //字节数组转16进制字符串 
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                }
            }
            return returnStr;
        }
        private void Main_Activated(object sender, EventArgs e)
        {
            /*RFID刷卡、打开串口*/
            sc = new SerialClass("COM10", 9600);
            sc.openPort();
            sc.ReadData = Read1;
        }

        private void openled_Click(object sender, EventArgs e)
        {
            /* 开下位机灯  */
            // byte[] data = { 0xfd, 0x03, 0x07, 0x04, 0x01, 0x60, 0xdf };
            byte[] data = { 0x03 };
            sc.SendData(data);
            MessageBox.Show("教室灯光已开");
        }

        private void panel3_Click(object sender, EventArgs e)
        {
            /* 关下位机灯  */
          //  byte[] data = { 0xfd, 0x03, 0x07, 0x04, 0x02, 0x60, 0xdf };
             byte[] data = { 0x04};
             sc.SendData(data);
            MessageBox.Show("教室灯光已关");
        }

        private void panel2_MouseUp(object sender, MouseEventArgs e)
        {
            //效果:按下颜色变暗
        }

        private void panel4_Click(object sender, EventArgs e)
        {

            /*温度阈值判断,温度大于阈值开下位机风扇,温度小于阈值关风扇*/

            int a = Convert.ToInt32(wendu);
            if (numericUpDown1.Value < a)
            {
                byte[] data = { 0x05 };
                sc.SendData(data);
                MessageBox.Show("风扇已开");
            }
            else
            {
                byte[] data = { 0x06 };
                sc.SendData(data);
                MessageBox.Show("温度低于阈值");             
            }
        }



        private void Media_off_Click(object sender, EventArgs e)
        {
            //关下位机风扇
            byte[] data = { 0x06 };
            sc.SendData(data);
            MessageBox.Show("风扇已关");
        }


        private void panel5_Click(object sender, EventArgs e)
        {
            //开下位机蜂鸣器
            byte[] data = { 0x07 };
            sc.SendData(data);
            MessageBox.Show("音箱已开");
        }

        private void panel6_Click(object sender, EventArgs e)
        {
            //开下位机舵机
            byte[] data = { 0x01};
            sc.SendData(data);
            MessageBox.Show("教室窗户已开");
        }

        private void win_off_Click(object sender, EventArgs e)
        {
            //关下位机舵机
            byte[] data = { 0x02 };
            sc.SendData(data);
            MessageBox.Show("教室窗户已关");
        }

        private void beep_off_Click(object sender, EventArgs e)
        {
            //关下位机蜂鸣器
            byte[] data = {0x08};
            sc.SendData(data);
        }


        private void panel1_Click(object sender, EventArgs e)
        {
            /* 跳转表单 退出线程 */
            Form f1 = new DataSheet();
            this.Hide();
            f1.ShowDialog();
            Application.ExitThread();
        }


        /**/
        private void panel5_Click_1(object sender, EventArgs e)
        {
            /*跳转登录 退出线程*/
            Form f = new denglu();
            this.Hide();
            f.ShowDialog();
            Application.ExitThread();
        }

        private void Main_Deactivate(object sender, EventArgs e)
        {
            sc.closePort();
        }

        private void Main_Activated_1(object sender, EventArgs e)
        {
            sc.openPort();
        }


        bool bo;
        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {

            /*温度阈值判断,温度大于阈值开下位机风扇,温度小于阈值关风扇*/
            
            int a = Convert.ToInt32(wendu);
            if (numericUpDown1.Value < a)
            {
                byte[] data = { 0x05 };
                sc.SendData(data);
                if (bo==false)
                { 
                   MessageBox.Show("温度高于阈值,风扇已开");
                    bo = true;
                }
        
            }
            else
            {
                byte[] data = { 0x06 };
                sc.SendData(data);
                if (bo == true)
                { 
                   MessageBox.Show("温度低于阈值,风扇已关闭");
                    bo = false;
                    byte[] data1 = { 0x07 };
                    sc.SendData(data1);
                }
                
            }
        }
    }
}

表单部分
智能教室控制系统

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.IO;

namespace Wisdomclassroom
{
    
    public partial class DataSheet : Form
    {
        SQL sql;
        public DataSheet()
        {
            this.StartPosition = FormStartPosition.CenterScreen;
            InitializeComponent();
        }

        private void DataSheet_Load(object sender, EventArgs e)
        {

            sql = new SQL("11.111.11.11", "xxxxxx", "xxx", "xxxxx");
            sql.Connect();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string connstr = @"Data Source=11.111.11.11;Initial Catalog=xxxxxx;Uid=xxx;Pwd=xxxxx";
            saveFileDialog1.Filter = "文本文件|*.txt";
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string path = saveFileDialog1.FileName;
                using (SqlConnection conn = new SqlConnection(connstr))
                {
                    
                    string sql1 = "select xingming,ID,xueji from [login]";
                    using (SqlCommand cmd = new SqlCommand(sql1, conn))
                    {

                         conn.Open();
                        using (SqlDataReader dr = cmd.ExecuteReader())
                        {
                            List<string> student = new List<string>();
                            while (dr.Read())
                            {
                                string num = dr["ID"].ToString();
                                string name = dr["xingming"].ToString();
                                string school = dr["xueji"].ToString();
                                student.Add( "姓名:"+name+ "  "+"学号:"+num+"   "+"学籍校:"+ school) ;
                            }
                            File.WriteAllLines(path, student.ToArray());
                            MessageBox.Show("导出成功");
                        }
                    }
                }
            }
        }

        private denglu anotherForm;
        private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            anotherForm = new denglu();
            this.Hide();
            anotherForm.ShowDialog();
            Application.ExitThread();
        }

        private zhuce anotherForm1;
        private void 个人信息录入ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            anotherForm1 = new zhuce();
            this.Hide();
            anotherForm1.ShowDialog();
            Application.ExitThread();
        }

        private wangjimima anotherForm2;
        private void 修改密码ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            anotherForm2 = new wangjimima();
            this.Hide();
            anotherForm2.ShowDialog();
            Application.ExitThread();
        }

        private void 清空数据库ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            String str = sql.GetString("delete from [login]");
            MessageBox.Show("数据库已清空");
        }
    }
}

总结:

由于本项目应用到了硬件,主要通过串口将上位机C#和下位机stm32进行通信,上位机通过发送协议数据给下位机,数据传输完成,下位机判断不同的数据,响应执行串口中断函数,实现不同的功能。而串口通信在传输协议里并不是最优的选择,所以我想在以后项目用到Wi-Fi或Zigbee协议等,来提升自己的水平。

技术问题:

硬件层面上:

项目初期,在实现单独人体红外测温模块,下位机能够正常响应,能够接收到正常温度的发送。

项目中期阶段,其他外设的加入,导致硬件引脚的冲突,例如下位机板载的灯环用到了PB6引脚及RFID模块的PB7引脚,在板载外设不能更改的情况下,先是将红外测温模块底层代码的数据线和时钟线引脚进行修改,但还是读不到正确的温度。

项目后期阶段,在保证核心功能RFID的实现下,权衡将红外模块去除,加入执行器风扇,通过和阈值温度的判断,实现自动开关,其也是较为创新的地方。

软件层面上:

C#核心问题:

在上位机C#在项目快结束时一直存在的问题,其下位机RFID,通过串口将电子标签的ID发送给上位机,上位机里的串口类,当接收到一个字节时,也会触发DataReceived事件,同时调用Invoke函数,将卡号显示的textbox里,其只有第一次刷的卡号才能显示,再刷传输来的数据就会溢出或不正确。
经过老师指导,在每次的刷卡之后接收到数据,可以用clear将上次传过来的数据清除,保证每次的数据帧的正确,但这个串口类调用不了clear,所以转换思路,通过设置缓存区的方式,将传过来的数据的帧头和结束符做一个判断,将其放入线程里,将其实现数据的传输的正确性。
问题解决思路的不同,也导致项目后期工作量不同、业务逻辑不同。后来在下位机中,将电子标签一靠近RFID,就会一直读改为读一次。最后也是通过简单的方法解决复杂的问题。

可加本人Q1429536866,欢迎大家指导讨论。

上一篇:SpringBoot学习笔记:动态数据源切换


下一篇:【Zookeeper】源码分析之服务器(五)之ObserverZooKeeperServer