基于 基于 CT117E 嵌入式竞赛板 嵌入式竞赛板 的 “电子秤”程序设计与调试
赛题硬件框图
由于赛题细节多,赛题PDF要求书放入了附件,链接后续加入。本文主要讲述本届题目的困难点:扩展板的AD按键,串口输出。
1. 由于本届赛题用到了扩展版的AD按键,只要读取AD的值,并根据不同按键AD值就可以判断出是哪个按键按下,但本届的难点在于既是AD按键,又要求按键长短按有不同的功能,所以相对来说就需要设计按键的思路了。本文用到的方法其实就是在原中断按键扫描的基础上进行替换设计的。无非是把按键扫描函数换成了AD值的判断。实现方法如下:
#ifndef __ADC_KEY_H
#define __ADC_KEY_H
#include "stm32f10x.h"
#include "bsp_adc.h" //需要依赖adc配置
#include "bsp_GeneralTim.h" //需要依赖时钟提供时许
#include "Def_config.h" // 需要依赖宏定义
typedef struct{
u8 KEY_VALUE; //使用数据后及时清除到0xff !!!
JUDGE_ENUM IS_Key_ShortPress; //使用数据后及时清除到FAUSE状态!!!
JUDGE_ENUM IS_Key_LongPress; //判断长按否? 不需要认为清除
JUDGE_ENUM IS_Key_LongFresh; //长按刷新,长按多久按键计算快速累加或累减一次使用数据后及时清除到FAUSE状态!!!!
JUDGE_ENUM IS_Key_Touch; //按键是否按下
}KEYSCANF_TypeDef;
extern KEYSCANF_TypeDef KEYSCANF_Structure;
/********************当前设置的按键扫描中断为 5MS************/
#define KEY_LONGSTATUS_Time 800000/GENERAL_TIM_SetUs // 多久算长按
#define KEY_EliShaking_Time 10000/GENERAL_TIM_SetUs // 按键消抖值
#define Key_LongTimeSet 100000/GENERAL_TIM_SetUs //按键长按后多久更新一次 比如长按后加一,本设置为多久加一
extern u8 Key_Scan(void);
extern void Key_Interrup(void);
#endif
#include "Adc_Key.h"
extern __IO uint16_t ADC_ConvertedValue[NOFCHANEL];
u8 Key_Scan(void)
{
if(ADC_ConvertedValue[1] < 100)
{
return 1;
}
else if(ADC_ConvertedValue[1] < 800)
{
return 2;
}
else if(ADC_ConvertedValue[1] < 1500)
{
return 3;
}
else if(ADC_ConvertedValue[1] < 2000)
{
return 4;
}
else if(ADC_ConvertedValue[1] < 2500)
{
return 5;
}
else if(ADC_ConvertedValue[1] < 3200)
{
return 6;
}
else if(ADC_ConvertedValue[1] < 3700)
{
return 7;
}
else if(ADC_ConvertedValue[1] < 4020)
{
return 8;
}
else{
return 0xff;
}
}
u8 keyCheck = 0;
uint8_t keyState = 0;
uint16_t keyPrev = 0xff;
u32 keyLongCheck = 0;
u16 Key_LongTime = 0;
u8 keyCountTime = 0;
KEYSCANF_TypeDef KEYSCANF_Structure = {0xff,FALSE,FALSE,FALSE,FALSE};
void Key_Interrup(void)
{
uint8_t keyPress = 0xff;
keyCountTime ++;
if(keyCountTime >= KEY_EliShaking_Time)
{
keyCountTime = 0;
keyCheck = 1;
}
if(keyCheck == 1)
{
keyCheck = 0;
keyPress = Key_Scan();
switch(keyState)
{
case 0://按键未按下态
if(keyPress != 0xff)
{
keyPrev = keyPress;
keyState = 1;
}
else
{
keyState = 0;
}
break;
case 1://表示有按键按下,判断当前值和上一次的值是否一样,若不一样则为抖动!
if(keyPress == keyPrev)
{
keyState = 2;
}else{
keyState = 0;
keyPrev = 0xff;
}
break;
case 2:
if(keyPress != keyPrev)
{
if(keyLongCheck < KEY_LONGSTATUS_Time)
{
KEYSCANF_Structure.IS_Key_ShortPress = TRUE;
KEYSCANF_Structure.KEY_VALUE = keyPrev;
}
KEYSCANF_Structure.IS_Key_LongPress = FALSE;
keyPrev = 0xff;
keyState = 0;
keyLongCheck = 0;
Key_LongTime = 0;
}else{
keyLongCheck++;
if(keyLongCheck >= KEY_LONGSTATUS_Time)
{
keyLongCheck = KEY_LONGSTATUS_Time + 1;
KEYSCANF_Structure.KEY_VALUE = keyPrev;
KEYSCANF_Structure.IS_Key_ShortPress = FALSE;
KEYSCANF_Structure.IS_Key_LongPress = TRUE;
}
}
break;
default : break;
}
}
if(KEYSCANF_Structure.IS_Key_LongPress == TRUE)
{
Key_LongTime ++;
if(Key_LongTime >= Key_LongTimeSet)
{
Key_LongTime = 0;
KEYSCANF_Structure.IS_Key_LongFresh = TRUE;
}
}
}
2. 按键是AD采集,用到了ADC的DMA读取。
#ifndef __ADC_H
#define __ADC_H
#include "stm32f10x.h"
#include "Def_config.h"
// ADC 编号选择
// 可以是 ADC1/2,如果使用ADC3,中断相关的要改成ADC3的
#define ADCx ADC1
#define ADC_CLK RCC_APB2Periph_ADC1
// ADC GPIO宏定义
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
#define ADC_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADC_PORT GPIOA
#define ADC_PIN (GPIO_Pin_4 | GPIO_Pin_5)
// ADC 中断相关宏定义
#define ADC_IRQ ADC1_2_IRQn
#define ADC_IRQHandler ADC1_2_IRQHandler
#define ADC1_DR_Address ((uint32_t)0x4001244C)
// 转换通道个数
#define NOFCHANEL 2
// ADC1转换的电压值通过MDA方式传到SRAM
extern __IO uint16_t ADC_ConvertedValue[NOFCHANEL];
extern void ADCx_Init(void);
extern void ADC_Read(u8 Channel_x);
#endif /* __ADC_H */
#include "bsp_adc.h"
__IO uint16_t ADC_ConvertedValue[NOFCHANEL]={0,0};
static void ADCx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( ADC_GPIO_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = ADC_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_PORT, &GPIO_InitStructure);
}
static void ADCx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = NOFCHANEL;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
static void ADCx_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd( ADC_CLK, ENABLE );
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;
ADC_Init(ADCx, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
ADC_RegularChannelConfig(ADCx, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADCx, ADC_Channel_5, 2, ADC_SampleTime_55Cycles5);
ADC_DMACmd(ADCx, ENABLE);
ADC_Cmd(ADCx, ENABLE);
ADC_ResetCalibration(ADCx);
while(ADC_GetResetCalibrationStatus(ADCx));
ADC_StartCalibration(ADCx);
while(ADC_GetCalibrationStatus(ADCx));
ADC_SoftwareStartConvCmd(ADCx, ENABLE);
}
void ADCx_Init(void)
{
ADCx_GPIO_Config();
ADCx_DMA_Config();
ADCx_Mode_Config();
}
3. 串口调用,其实就是接收数据、判断、发送的过程。
#ifndef __USART2_H
#define __USART2_H
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "Def_config.h"
#define USART2_RecTab_Max 50
typedef struct{
char USART2_RecTab[USART2_RecTab_Max];
u8 USART2_RecNum ;
JUDGE_ENUM IS_USART2_ReceiveStart;
JUDGE_ENUM IS_USART2_RecSucess;
}USART2REC_TypeDef;
extern USART2REC_TypeDef USART2REC_Structure; //USART2 接收定义。在别的函数调用需要extern
extern void USART2_Config(u16 USATR2_Boads);
extern void USART2_SendString(char *str);
#endif
#include "usart2.h"
#include <stdio.h>
#include <string.h>
#include "Def_config.h"
USART2REC_TypeDef USART2REC_Structure;
void NVIC_USART2Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_Priority_Structure.USART2_Priority;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART2_Config(u16 USATR2_Boads)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
NVIC_USART2Configuration();
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = USATR2_Boads;
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(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Cmd(USART2, ENABLE);
}
void USART2_SendString(char *str)
{
uint8_t index = 0;
do
{
USART_SendData(USART2,str[index]);
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE) == RESET);
index++;
}
while(str[index] != 0);
}
void USART2_Receive(void)
{
char ucTemp=0;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
ucTemp = USART_ReceiveData(USART2);
if(ucTemp == 'C' || ucTemp == 'S')
{
USART2REC_Structure.USART2_RecNum = 0;
USART2REC_Structure.IS_USART2_ReceiveStart = TRUE;
memset(USART2REC_Structure.USART2_RecTab,0x00,USART2_RecTab_Max);
}
if(USART2REC_Structure.IS_USART2_ReceiveStart == TRUE)
{
USART2REC_Structure.USART2_RecTab[USART2REC_Structure.USART2_RecNum++] = USART_ReceiveData(USART2);
}
if(USART2REC_Structure.USART2_RecNum > USART2_RecTab_Max - 1)
{
USART2REC_Structure.USART2_RecNum = 0;
}
if(ucTemp == '\n' || USART2REC_Structure.USART2_RecNum > 0)
{
USART2REC_Structure.IS_USART2_RecSucess = TRUE;
USART2REC_Structure.IS_USART2_ReceiveStart = FALSE;
}
}
}
int fputc(int ch, FILE *f)
{
USART_SendData(USART2,(unsigned char) ch);
while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET);
return (ch);
}
int fgetc(FILE *f)
{
while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART2);
}