蓝桥杯竞赛板使用笔记

更新时间:2023-03-13 11:39:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

一、 新建工程

略有不同的是程序的下载方法,需要装驱动。。。

二、 LED

电路原理图:

比赛板用的是stm32f103rb芯片,64管脚,LED与LCD同用了管脚PC8~PC15(对应LED1~LED8),所以增用了一个锁存器——M74HC573。在控制LED的时候,只需在输出对应电平后,给锁存器的LE(N—LE,对应PD2)管脚一个上升沿脉冲即可把对应的电平锁存到锁存器的输出端(Q1~Q8),从而控制LED。

需要注意的是:1、LCD的操作会影响LED的亮灭,如LCD的行写会点亮所有的LED,

需进行程序处理;2、对锁存器LE管脚的操作,给一个上升沿脉冲后应把对应的控制引脚(PD2)置低电平,以免LCD的操作影响LED的亮灭混乱。 有关

蜂鸣器的使用比较简单,在这里一起说明:比赛板的蜂鸣器连在PB4上,

需注意的是Pb4管脚是JTAG调试接口之一,需要在程序中失能JTAG才可以使用Pb4管脚,如下语句: /* Disable the Serial Wire Jtag Debug Port SWJ-DP */

GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);

然而程序中用了这一句后,下次用JTAG下载程序时就下载不进去了,解决办法是:先按着开发板的RESET 按键别松手,再点击keil中的下载,然后松手按键,程序就可正常下载进去了。

三、 USART

CT1117开发板的USART2与下载程序的USB方口集成在一起,使用时只需装好对应的驱动即可实现电脑与开发板的串口通信(部分串口调试工具可能搜不到串口)。

比赛时对串口的使用方式可能性比较大的是,通过电脑发送指令控制stm32以执行相应操作,所以我结合LED编写了一个练习程序,使用串口发送指令控制LED亮灭,同时在LCD上显示最近一次所接收到的命令。串口的接收命令使用的是中断的方式,代码如下:

//配置串口2

void USART_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//PA2 Tx GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3 Rx GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &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(USART2, &USART_InitStructure); USART_Cmd(USART2, ENABLE);//使能USART2

/* Configure the NVIC Preemption Priority Bits */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

/* Enable the USARTy Interrupt */

NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }

USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//使能接受中断

//中断服务函数

void USART2_IRQHandler(void) {

if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {

/* Read one byte from the receive data register */ RxBuffer[RxCounter] = USART_ReceiveData(USART2); if(RxBuffer[RxCounter]=='x')//指令格式led2_onx,以’x’作为命令的结尾 { rxflag=1; //接受到命令的标志 RxCounter=0; } else RxCounter++;

if(RxBuffer[0]!='l')//此句避免接受命令混乱

RxCounter=0;

} USART_ClearITPendingBit(USART2,USART_IT_RXNE);// }

//对于通过串口向电脑发送数据,采用printf()函数的重定向 int fputc(int ch,FILE *f) { USART_SendData(USART2,(uint8_t)ch); while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); // delay_ms(10); return ch;

}//库函数中有,复制即可使用

主函数省略,其中主要是调用了一个对命令处理的函数,每次调用处理函数,先判断命

令接受的标志位,若置为1,则对存放命令的数组内容进行判断,从而操作LED并显示当前的命令。

Take care:

1、 sprintf();函数的使用: 字符串转换函数

sprintf(string,”%s%d%c”,”djkal”,a,buffer[5]);

其中string可以为u8 string[20];或者u8 *string;类型;但是使用指

针时程序总会卡死在这个函数中 ,调试时换为已指明大小的数组则避免了卡死问题;

2、 怀疑点:串口调试助手在发送数据到MCU时并不仅仅发送你输入的字符串,而有附

加。

3、 通过电脑的串口助手或者超级终端发送指令控制开发板,指令最好自己定义格式。

例如通过串口设置时间,可定义命令格式:A12:02:05B 其中’A’,’B’分别为命令的起始位和停止位,注意电脑发送的是字符串(ASCII码),受到数据后需要转换成数字 , 如数字2和’2’相差48。若只是简单的控制不涉及数字,可以直接对字符进行识别即可。

四、 SysTick

不涉及系统而言,主要用于较精准的延时,用最新的3.5库实现非常容易。

首先调用SysTick_Config(SystemCoreClock/1000);其中SystemCoreClock是内核系统时钟频率,未做修改的话为72M(此函数并不关心此值大小),除以1000,则实现1ms中断一次。然后编写延时函数

void delay_ms(u16 nTime) {

TimingDelay=nTime;//TimingDelay设置为全局变量 while(TimingDelay!=0); }

中断服务函数

void SysTick_Handler(void) { if(TimingDelay>0) TimingDelay--; }

具体代码实现过程:在SysTick_Config中配置了SysTick的中断频率,每次进入中断函数都把全局变量TimingDelay减一,而在延时函数中不断循环查询TimingDelay变量,直至TimingDelay变为0延时结束。

有时需要考虑SysTick的中断优先级问题,如:如果EXTI 的优先级高于Systick中断,则在按键消抖时会造成,在延时10ms消抖的过程中会不断被按键电路的毛刺影响而多次重新进入按键中断,而每次都会重新调用延时10ms,等到按键松手后再延时10ms已经不能读到对应按键的电平状态,不能实现消抖。解决此问题的方法很简单,就是更改SysTick_Config函数中对中断优先级的配置使它高于按键的优先级,具体实现见EXTI_KEY中的讲述。(写的比较啰嗦,大致道理还是比较简单)

五、 EXTI_KEY

本次程序是在USART 的基础之上,加上利用按键控制Led,实现按键与串口同时控制lED的亮灭。较为简单,直接结合原理图给程序。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//切记打开复用时钟

//K1.K2.K3.K4分别连接stm32上的Pa0,Pa8,Pb1,Pb2,配置为上拉输入

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_8; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; GPIO_Init(GPIOB,&GPIO_InitStructure);

//key1的配置,其他同样配置即可

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//key1 EXTI_InitStructure.EXTI_Line=EXTI_Line0; EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Falling;//下降沿中断 EXTI_InitStructure.EXTI_LineCmd =ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE; NVIC_Init(&NVIC_InitStructure);

void EXTI9_5_IRQHandler(void)//key2的中断函数,其他类似 {

if(EXTI_GetITStatus(EXTI_Line8) != RESET) {

delay_ms(10);//延时消抖,此函数用得是Systick的中断消抖,后面会讲

注意问题

if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)==0) { keyflag=1;//有按键按下的标志,按键处理函数中消除 key2flag=!key2flag;//具体按键的标志,具体按键处理后消除 } While(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)==0);//松手检测 EXTI_ClearITPendingBit(EXTI_Line8); } }

按键的处理函数略,主要就是先判断按键标志keyflag以判断是否有按键按下,若有再继续辨别具体按键标志,控制Led。

Take care之处:

1、使用外部中断需要打开复用时钟;

2、消抖。当进入中断服务函数之后,先判断对应中断线的标志是否置位(能进入则是置位了的),然后延时10ms后,使用if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)==0)此句读取对应按键相连的管脚是否被外部按键拉低,以实现消抖。然而我们知道,延时函数使用的是Systick,它是通过在滴答定时器中断中递减相应变量实现延时的,对systick的配置我们通常使用的是Cm3内核函数uint32_t SysTick_Configmy(uint32_t ticks);具体函数代码如下:

/* Initialise the system tick timer and its interrupt and start the * system tick timer / counter in free running mode to generate

* periodical interrupts. */

static __INLINE uint32_t SysTick_Config(uint32_t ticks) {

if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */

NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0

System Interrupts */

SysTick->VAL = 0; /* Load the SysTick Counter Value */

SysTick->CTRL=SysTick_CTRL_CLKSOURCE_Msk|SysTick_CTRL_TICKINT_Msk |

SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */

return (0); /* Function successful */

}

其中NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */这一句是对Systick抢占优先级中断的配置,这里配置的是7,最低,我们需要把它修改为NVIC_SetPriority (SysTick_IRQn, 1);抢占优先级为1(只要比按键的中断优先级高即可)。注意比赛时工程中所使用的内核文件代码不允许修改,只要把这个函数复制到用户文件中,函数名稍做修改再调用即可。此时按键消抖稳定。

六、 LCD

主要总结、记录一些对官方提供LCD驱动代码的使用方法

rtccounter=RTC_GetCounter(); sprintf(string,\`);

七、 ADC

Stm32f103rb内带2个12位的ADC(2个ADC的通道0~15在管脚上完全重合),每个ADC具有16个外部通道,时钟频率不能超过14M,竞赛板的ADC12的通道8(Pb0)与一个电位器相连接,所以在这里配置ADC的通道8为单通道连续转换模式,对ADC的配

置如下:

ADC_InitTypeDef ADC_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div8); //分频:72M/8=9MHz

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置为模拟输入模式 GPIO_Init(GPIOB, &GPIO_InitStructure); /* ADC1 configuration ------------------------------------------------------*/ ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//此处配置单通道独立

模式或多通道扫描模式

ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描模式禁止

ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换使能

ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外

部触发

ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//字节右对齐 ADC_InitStructure.ADC_NbrOfChannel = 1;//转换通道数为1 ADC_Init(ADC1, &ADC_InitStructure);

/* ADC1 regular channel14 configuration */

ADC_RegularChannelConfig(ADC1,ADC_Channel_8,1,ADC_SampleTime_55Cycles5); //设置ADC通道转换

/* Enable ADC1 reset calibration register */ ADC_ResetCalibration(ADC1);

/* Check the end of ADC1 reset calibration register */ while(ADC_GetResetCalibrationStatus(ADC1));

/* Start ADC1 calibration */ ADC_StartCalibration(ADC1);

/* Check the end of ADC1 calibration */

while(ADC_GetCalibrationStatus(ADC1)); //对ADC的校准,库中复制过来即可

/* Start ADC1 Software Conversion */

ADC_SoftwareStartConvCmd(ADC1, ENABLE);// 软件触发ADC转换开始

然后在主函数中通过函数不断查询的到转换结果并显示: while(1) {

}

ADCValue=ADC_GetConversionValue(ADC1)*3.30/0xfff;

sprintf(str,\注意此函数把浮点数转换成字符串的格式 LCD_DisplayStringLine(Line5,str);

八、 TIMER

Stm32的定时器是比较复杂,但绝不能想的太复杂,被官方给的一百余页介绍文档给吓倒。主要就这个这个原理图,看懂就全懂了。

对于一般的应用(时基计数、PWM输出、输入捕获),弄清楚三个寄存器即可,分别是自动重装载寄存器、CNT计数器、捕获/比较1234寄存器。

1、 时基计数功能:经过分频设置好时钟源后,CNT就开始(递增或递减)计数,若设

置了更新中断,则当CNT递减至0时,产生更新中断,同时自动重装载寄存器的值会给到CNT计数寄存器,重新下一周期的计数。(设置为计数器递增模式时类似)。 具体对定时器的设置代码如下:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_TimeBaseStructure.TIM_Period = 999;//设置自动重载值

TIM_TimeBaseStructure.TIM_Prescaler = 7199;//设置分频值:72M/(7199+1) TIM_TimeBaseStructure.TIM_ClockDivision = 0;//时钟分割,用于滤波,不用设0即可 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_Cmd(TIM2, ENABLE);//使能Tim

此时定时器的基本设置已好,然后打开中断,在中断服务函数中设置计数变量累加,最简单的定时功能就实现了。。。。关于中断的频率,简单一句话:定时器以所设置的分频后的时钟频率,每计数至自动重载值(或由重载值减至零)时中断一次。。

//NVIC的设置省略 /* TIM IT enable */

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能更新中断

void TIM2_IRQHandler(void) {

if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)//判断更新中断置位与否 { Timcnt++; } }

2、 PWM输出功能 从原理图上可以看出,每个定时器都有独立的四个通道,但是共用

一个CNT,即对每个定时器的四个通道而言,其时基是相同的,不同的是各通道比较寄存器的值,从而每个定时器都可产生4路频率相同而占空比不同的PWM波(特别注意这里是对每一个定时器而言的)。

具体原理过程是,CNT以设置好的频率计时,CNT的数值范围是0至重载寄存器的值(即PWM的周期),对PWM模式1且输出极性为高(。。。)而言,当CNT的值在0至 [捕获寄存器值]时,输出高点平,当CNT的值在[捕获寄存器值]至[重载寄存器值]时,输出低电平。所以不难看出,通过对预分频寄存器、自动重载寄存器、比较寄存器进行设置,即可生成不同频率不同占空比的PWM。 具体代码如下:

//对时基的设置同前…。。。

//每个TIM通道都对应一个外部GPIO,对应设置为GPIO_Mode_AF_PP复用推挽输出模式(略)

TIM_OCInitTypeDef TIM_OCInitStructure;

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM输出模式1 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; TIM_OCInitStructure.TIM_Pulse = 300;//设置比较寄存器的值

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性高 TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

TIM_OC2Init(TIM2, &TIM_OCInitStructure);

完毕。。

若在程序运行中要更改占空比,调用如下函数:

TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除更新中断

本文来源:https://www.bwwdw.com/article/1wfx.html

Top