U盘数据采集系统软件篇一(串口和矩阵键盘模块)

硬件设计完成,下面就要进行软件开发了,本篇主要介绍串口模块、按键模块的初始化及功能测试。
程序开发前我习惯查看原理图把所有外设对应的管脚全部列出来,方便后续开发时查看。

串口模块

串口模块管脚

串口号管脚
USART1 TXDPA9
USART1 RXDPA10
USART3 TXDPD8
USART3 RXDPD9

查看芯片规格书可知 PA9和PA10的默认功能为串口1,PD8和PD9需要重定义为串口3使用。
管脚定义:

 #define UART1_TXD_GPIO_PORT              GPIOA
 #define UART1_TXD_GPIO_CLK               RCC_APB2Periph_GPIOA
 #define UART1_TXD_GPIO_PIN               GPIO_Pin_9
 #define UART1_TXD_GPIO_MODE              GPIO_Mode_AF_PP

 #define UART1_RXD_GPIO_PORT              GPIOA
 #define UART1_RXD_GPIO_CLK               RCC_APB2Periph_GPIOA
 #define UART1_RXD_GPIO_PIN               GPIO_Pin_10
 #define UART1_RXD_GPIO_MODE              GPIO_Mode_IN_FLOATING

#define UART3_TXD_GPIO_PORT              GPIOD
#define UART3_TXD_GPIO_CLK               RCC_APB2Periph_GPIOD
#define UART3_TXD_GPIO_PIN               GPIO_Pin_8
#define UART3_TXD_GPIO_MODE              GPIO_Mode_AF_PP

#define UART3_RXD_GPIO_PORT              GPIOD
#define UART3_RXD_GPIO_CLK               RCC_APB2Periph_GPIOD
#define UART3_RXD_GPIO_PIN               GPIO_Pin_9
#define UART3_RXD_GPIO_MODE              GPIO_Mode_IPU

本文使用中断方式接收和发送,串口和串口中断初始化代码如下:

GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

//UART1 管脚配置:时钟、模式	
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

GPIO_InitStructure.GPIO_Pin = UART1_RXD_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = UART1_RXD_GPIO_MODE; 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;	
GPIO_Init(UART1_RXD_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = UART1_TXD_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = UART1_TXD_GPIO_MODE; 
GPIO_Init(UART1_TXD_GPIO_PORT, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);	
//配置nvic向量表
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

/* Enable the USARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);	

/* Enable USART1 Receive and Transmit interrupts */
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);		//一开始就是要关闭

/* Enable USART */
USART_Cmd(USART1, ENABLE);	

gb_needDealUart1Data = 0;
uart1DelayTimer = 0;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);
//UART3 管脚配置:时钟、模式	
GPIO_InitStructure.GPIO_Pin = UART3_RXD_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = UART3_RXD_GPIO_MODE; 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;	
GPIO_Init(UART3_RXD_GPIO_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = UART3_TXD_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = UART3_TXD_GPIO_MODE; 
GPIO_Init(UART3_TXD_GPIO_PORT, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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(USART3, &USART_InitStructure);	


//配置nvic向量表
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

/* Enable the USARTy Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);	

/* Enable USART3 Receive and Transmit interrupts */
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART3, USART_IT_TXE, DISABLE);		//一开始就是要关闭

/* Enable USART */
USART_Cmd(USART3, ENABLE);	

gb_needDealUart3Data = 0;

初始化的时候别忘记串口3的remap

GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);

串口收发中断处理,以串口3为例:

void USART3_IRQHandler(void)
{
	u8 d; 
	if (USART_GetFlagStatus(USART3, USART_FLAG_RXNE))
	{
		/* Read one byte from the receive data register */
		d = USART_ReceiveData(USART3);
		uart3infifo_DataIn(d);	
		uart3DelayTimer = UART3_DATA_DELAY;
		gb_needDealUart3Data = 0;
	}
	else if (USART_GetFlagStatus(USART3, USART_FLAG_TXE))
	{		
		if (uart3outfifo_count > 0)
		{
			/* Write one b	yte to the transmit data register */
			USART_SendData(USART3, uart3outfifo_DataOut());	//顺便清除flag_TXE
		}				
		else
		{
		  	/* Disable the USARTy Transmit interrupt */
		  	USART_ITConfig(USART3, USART_IT_TXE, DISABLE);		//实际上,发送为空的标志还在,只是关闭中断
		}    
	}
}

串口中断中收到数据先进FIFO,等一个数据包接收完后进入处理。需要发送数据,把数据填入fifo,中断里自动发送。我一般都是采用这种中断加fifo的收发方式。中断就像是后台操作一样,在主程序的流程中,不用刻意去关注中断方式发送了没有,何时接收等,而查询方式是在主程序流程中不断查看是否接收到了数据,一般用while不断循环查看。中断方式可以更高效利用CPU ,节省CPU的时间,查询就会增加CPU负担。

按键模块

查看原理图,矩阵按键管脚如下

功能管脚
第一行PE8
第二行PE9
第一列PE10
第二列PE11

矩阵键盘管脚定义及初始化:

#define	ROW0_GPIO_PORT GPIOE
#define	ROW0_GPIO_PIN GPIO_Pin_8
#define	ROW0_GPIO_MODE GPIO_Mode_IPU

#define	ROW1_GPIO_PORT GPIOE
#define	ROW1_GPIO_PIN  GPIO_Pin_9
#define	ROW1_GPIO_MODE GPIO_Mode_IPU


#define	COL0_GPIO_PORT GPIOE
#define	COL0_GPIO_PIN  GPIO_Pin_10
#define	COL0_GPIO_MODE GPIO_Mode_Out_PP

#define	COL1_GPIO_PORT GPIOE
#define	COL1_GPIO_PIN  GPIO_Pin_11
#define	COL1_GPIO_MODE GPIO_Mode_Out_PP

#define SetRow0() GPIO_SetBits(ROW0_GPIO_PORT, ROW0_GPIO_PIN)
#define ResetRow0() GPIO_ResetBits(ROW0_GPIO_PORT, ROW0_GPIO_PIN)
#define ReadRow0() GPIO_ReadInputDataBit(ROW0_GPIO_PORT,ROW0_GPIO_PIN)

#define SetRow1() GPIO_SetBits(ROW1_GPIO_PORT, ROW1_GPIO_PIN)
#define ResetRow1() GPIO_ResetBits(ROW1_GPIO_PORT, ROW1_GPIO_PIN)
#define ReadRow1() GPIO_ReadInputDataBit(ROW1_GPIO_PORT,ROW1_GPIO_PIN)

#define SetCOL0() GPIO_SetBits(COL0_GPIO_PORT, COL0_GPIO_PIN)
#define ResetCOL0() GPIO_ResetBits(COL0_GPIO_PORT, COL0_GPIO_PIN)
#define ReadCOL0() GPIO_ReadInputDataBit(COL0_GPIO_PORT,COL0_GPIO_PIN)

#define SetCOL1() GPIO_SetBits(COL1_GPIO_PORT, COL1_GPIO_PIN)
#define ResetCOL1() GPIO_ResetBits(COL1_GPIO_PORT, COL1_GPIO_PIN)
#define ReadCOL1() GPIO_ReadInputDataBit(COL1_GPIO_PORT,COL1_GPIO_PIN)
void keyboard_Init(void)
{

	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;	

	GPIO_InitStructure.GPIO_Pin = ROW0_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = ROW0_GPIO_MODE; 
	GPIO_Init(ROW0_GPIO_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = ROW1_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = ROW1_GPIO_MODE; 
	GPIO_Init(ROW1_GPIO_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = COL0_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = COL0_GPIO_MODE; 
	GPIO_Init(COL0_GPIO_PORT, &GPIO_InitStructure);
	SetCol(0);
	
	GPIO_InitStructure.GPIO_Pin = COL1_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = COL1_GPIO_MODE; 
	GPIO_Init(COL1_GPIO_PORT, &GPIO_InitStructure);
	SetCol(1);
}

矩阵键盘键值读取:

u8 keyboard_GetCurrentKey
(
	void
)
{
    u8 kbVal;
	u8 rowIndex;
	u8 colIndex;
    
    kbVal = KEY_noKey;
    
	for(colIndex = 0; colIndex < COL_NUM; colIndex ++)//按列查询
	{		
		ClrCol(colIndex);//列线置低
		for (rowIndex=0; rowIndex < ROW_NUM; rowIndex ++)//按行扫描
		{
			if (!RowVal(rowIndex))//读当前行电平
			{
				kbVal = KEY_VAL[rowIndex][colIndex];//有值返回,并跳出循环
				break;
			}
		}
		SetCol(colIndex);//列线置高
		if (kbVal != KEY_noKey)
		{
			break;
		}
	}
	return(kbVal);
}

可以在主函数中轮询键值进行处理,也可以在定时器中断中轮询键值。在中断中的不要写耗时操作或者加延时。在定时中断轮询键值一般也是配合fifo使用。代码如下:

//2ms定时器
void TIM5_IRQHandler(void)
{
	if (TIM_GetFlagStatus(TIM5, TIM_IT_Update))
  {
		TIM_ClearFlag(TIM5, TIM_IT_Update);
		ScanKeyDown();//处理按键中断
	}
}
void ScanKeyDown(void)
{
	g_keyPara.currentLcdKey = keyboard_GetCurrentKey();	//读取键值
	g_keyPara.keyFlag <<= 1;
	if(g_keyPara.currentLcdKey != KEY_noKey)
	{
		g_keyPara.keyFlag ++;
	}
				
	if (!g_keyPara.longLcdKeyFlag)//长按和短按处理  和短按的去抖处理
	{
		if (g_keyPara.currentLcdKey == KEY_noKey)
		{
			if ((g_keyPara.shortLcdKeyTimer == 0)&&g_keyPara.shortLcdKey != 0)
			{			
				fifo_DataIn(KB_FIFO,g_keyPara.shortLcdKey);	//键值进fifo队列等待处理
			}
			g_keyPara.enterLongLcdKeyTimer = ENTER_LONG_KEY_TIME;
			g_keyPara.shortLcdKeyTimer = SHORT_KEY_TIME;
		}
		else
		{
			if (g_keyPara.shortLcdKeyTimer > 0)
			{
				g_keyPara.shortLcdKeyTimer --;
				if (g_keyPara.shortLcdKeyTimer == 0)
				{
					g_keyPara.shortLcdKey = g_keyPara.currentLcdKey;
				}
			}
				
			if(g_keyPara.enterLongLcdKeyTimer > 0)
			{
				g_keyPara.enterLongLcdKeyTimer --;
				if (g_keyPara.enterLongLcdKeyTimer == 0)
				{
					g_keyPara.longLcdKeyIntervalTimer = LONG_KEY_INTERVAL_TIME;
					g_keyPara.longLcdKeyFlag = 1;
					g_keyPara.longLcdKey = g_keyPara.currentLcdKey;	

					fifo_DataIn(KB_FIFO,g_keyPara.longLcdKey+0x20);//键值进fifo队列等待处理
				}
			}
		}
	}
	else
	{
		if (g_keyPara.currentLcdKey == g_keyPara.longLcdKey)
		{
			if (g_keyPara.longLcdKeyIntervalTimer > 0)
			{
				g_keyPara.longLcdKeyIntervalTimer --;
				if (g_keyPara.longLcdKeyIntervalTimer == 0)
				{
					fifo_DataIn(KB_FIFO,g_keyPara.longLcdKey+0x20);	//键值进fifo队列等待处理				
				}
			}
		}
		else
		{
			g_keyPara.longLcdKeyFlag = 0;
			g_keyPara.longLcdKey = KEY_noKey;
			g_keyPara.shortLcdKey = KEY_noKey;
			g_keyPara.shortLcdKeyTimer = SHORT_KEY_TIME;
			g_keyPara.enterLongLcdKeyTimer = ENTER_LONG_KEY_TIME;
			g_keyPara.longLcdKeyIntervalTimer = LONG_KEY_INTERVAL_TIME;
		}
	}
}

结尾

本文主要介绍了串口模块和矩阵键盘模块的管脚配置和初始化,并简要介绍了各模块的中断使用方法。
如有疑问,欢迎留言讨论。
在这里插入图片描述
扫描上方二维码关注“嵌入式案例Show”公众号,看更多嵌入式案例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值