导图社区 蓝桥杯嵌入式编程基础
蓝桥杯嵌入式编程基础,初始化GPIO的时候在最后要对设置的位进行置位,否则控制不了--如果后面LED的状态发生变化就能正常控制,最好在控制LED的时候初始化LED状态,对LED的端口直接置位。
编辑于2022-02-06 21:45:06基础知识
客观题
题型
填空
有时无
单选
多选
考察知识
数电
模电
运放电路
二极管
三极管电路
编程题
2、准备工作
工程模板
使用比赛提供的工程模板
调试方法
watch window观察变量
分步调试
3、始终以及LED操作方法
发现的问题
1、初始化GPIO的时候在最后要对设置的位进行置位,否则控制不了--如果后面LED的状态发生变化就能正常控制,最好在控制LED的时候初始化LED状态,对LED的端口直接置位
2、LED控制函数
void LED_Control(u16 led_ctrl,u8 statue){ GPIO_ResetBits(GPIOC, led_ctrl<<8);//0xfe let the first led on if(statue) GPIO_SetBits(GPIOC, ~led_ctrl<<8);//all off expect for led_ctrl. GPIO_SetBits(GPIOD, GPIO_Pin_2); GPIO_ResetBits(GPIOD, GPIO_Pin_2);//lock }
4、端口重映射与控制蜂鸣器
端口重映射
注意
尽量不要全部重映射,因为可能会影响程序的下载
重映射可能对应有多个引脚,可以通过寄存器配置
P4控制蜂鸣器需要用端口重映射功能才能实现,原来功能是SWJ的下载功能
控制程序
void Buzzer_Init(void){ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能PB,PE端口时钟 GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 ; //LED0-->PB.5 端口配置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB.5 //GPIO_SetBits(GPIOB,GPIO_Pin_4); //PE.5 输出高 }
注意
最后一句的GPIO_SetBits(GPIOB,GPIO_Pin_4);非必须,这点和LED初始化有区别。
5、嘀嗒定时器和定时器
嘀嗒定时器
寄存器
注意
文件在M3文件中,不在F103中
程序配置
SysTick_Config(SystemCoreClock/1000);//72000000/1000=72000-----1ms
说明
1s对应72M个机器周期,每1000个机器周期进一次中断对应的就是1ms
定时器
通用定时器
包括
2 3 4 5
特性
16位可编程向上向下/双边计数
程序
void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Enable the TIM2 global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
说明
优先级的配置的数越小优先级越高
问题
优先级怎么配置?
每一个中断都需要配置吗
配置说明
1. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 2. 也就是说可以配置成四个抢占优先级,在这四个抢占优先级中还可以配置四个响应优先级
① 在底层配置前先设置中断优先级分组 → void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); ②整个系统执行过程中,只设置一次中断分组; ③NVIC_PriorityGroupConfig只是对一个寄存器操作。多次操作以最后一次设置为准; ④针对每个中断,设置对应的抢占优先级和响应优先级; ⑤如果不设置中断优先级分组,则中断优先级分组默认为0,即0位抢占优先级,4位响应优先级。
总结
1、分组只进行一次
2、每个中断源的优先级分组各自配置
3、由分组决定能配置几阶中断嵌套和中断源的优先级配置
举例
分组2
主 0 1 2 3
从 0 1 2 3
高级定时器
包括
1 8
基础定时器
包括
6 7
6、独立按键
多练习
硬件电路
接口
K1
PA0
K2
PA8
K3
PB1
K4
PB2
软件
基本配置
上拉输入或者浮空输入都可
通过宏定义读取引脚电平
#define KB1 GPIO_ReadInputDataBit
延时消抖
软件delay实现
5ms左右
状态机
unsigned char Read_Key(void){ static char key_statue = 0; unsigned char key_press,key_return = 0; key_press = key_read;//avoid read much switch(key_statue){ case 0: if(key_press != 0xff) key_statue = 1; break; case 1: if(key_press == 0xff) {key_statue = 0; break;}//wrong else if(key_press == 0xfe) key_return = 1; else if(key_press == 0xfd) key_return = 2; else if(key_press == 0xfb) key_return = 3; else if(key_press == 0xf7) key_return = 4; key_statue = 2; break; case 2: if(key_press == 0xff){key_statue = 0;} break; } return key_return; }
三行代码法
失败
问题
1、设置中断分组的函数在哪找的?
在misc.h的第一个函数
2、TIM2的时钟忘开了
7、IIC协议
1、E2PROM电路
2、IIC协议介绍
3、AT24C02编程
unsigned char Read_24C02(unsigned char addr){ unsigned char data; I2CStart(); I2CSendByte(0xa0);//0write 1read I2CWaitAck(); I2CSendByte(addr); I2CWaitAck(); //from write to read need to read once again. I2CStart(); I2CSendByte(0xa1);//0write 1read I2CWaitAck(); data = I2CReceiveByte(); I2CWaitAck(); I2CStop(); return data; }
易错
在从写到读的过程中需要再进行一次启动
8、LCD显示
信息
2.4寸TFT屏幕
分辨率240*320
编程要点
格式转化
void Display_Eeprom(u8 eeprom_val) { uint8_t temp[20]; sprintf(temp,"eeprom val:%5d",eeprom_val); LCD_DisplayStringLine(Line1,temp); }
注意里的5d
void Display_AD(float ad_val) { uint8_t temp[20]; sprintf(temp,"ad val:%.2f",ad_val); LCD_DisplayStringLine(Line2,temp); }
出错
u8->u16 前面的函数声明也要修改
加减数据要有范围检测
常用显示函数
1、设置背景颜色
2、设置字体颜色
3、在指定行显示字符串
写入16位数据
自己写的
Write_24C02(0x00,(unsigned char)(e2_value&0xff)); Delay_Ms(10); Write_24C02(0x01,(unsigned char)((e2_value>>8)&0xff)); Delay_Ms(10);
优化
Write_24C02(0x00,e2_value%256); Delay_Ms(10); Write_24C02(0x01,e2_value/256); Delay_Ms(10);
出现的问题
写入数据的高低位%/写反了
9、串口通信
多练习
硬件
TX2
PA2
RX2
PA3
编程
需要用到的文件
代码
初始化配置
GPIO初始化
void STM_EVAL_COMInit(USART_InitTypeDef* USART_InitStruct) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable GPIO clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);//THIS IS ALSO NEED ON GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOA, &GPIO_InitStructure); /* USART configuration */ USART_Init(USART2, USART_InitStruct);//没有传入参数,需要用printf函数的串口初始化函数进行初始化 /* Enable USART */ USART_Cmd(USART2, ENABLE); }
参考代码来源
串口初始化
void UART2_Init(void){ USART_InitTypeDef USART_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; STM_EVAL_COMInit(&USART_InitStructure); NVIC_Configuration(); }
参考代码来源
数据发送
print重映射
int fputc(int ch, FILE *f) { /* Place your implementation of fputc here */ /* e.g. write a character to the USART */ USART_SendData(USART2, (uint8_t) ch); /* Loop until the end of transmission */ while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET) {} return ch; }
参考代码来源
数据接收
注意
串口发送
1、使用串口二对应的PA2、PA3需要进行端口复用,但不需要端口重映射到PD5、PD6,这一点与蜂鸣器不相同
1、stm32打印的第一个字母出不来
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET){//USART_FLAG_TC REPRESENT TRANSPORT OVWE. USART_FLAG_TXE REPRESENT BUFFER EMPTY
3、printf重定向要在target里勾选使用microlib
4、\n\r或者\r\n是回车
串口接收
中断NVIC配置
static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Enable the USARTz Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); //ENABLE USART_IT USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); }
参考代码来源
中断处理函数
void USART2_IRQHandler(void) { if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { USART_ClearITPendingBit(USART2,USART_IT_RXNE); } }
参考代码来源
未解决的问题
void USART2_IRQHandler(void){ if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET){ USART_ClearITPendingBit(USART2,USART_IT_RXNE); rx_buf[rx_count++] = USART_ReceiveData(USART2); if(rx_buf[0] == 'A'){ if(rx_count > 5){ if(rx_buf[1] == 'B' && rx_buf[2] == 'C'){ result = (rx_buf[3]-'0')*100 + (rx_buf[4]-'0')*10 +(rx_buf[5]-'0'); printf("result:%d\r\n",result); }else{ printf("error!\r\n"); } rx_count = 0; } }else{ rx_count = 0; //printf("error!\r\n"); } } }
正确输入数据会多一行ERROR!
思路整理
串口发送
如果仅仅实现串口发送功能,只需进行GPIO(参考Ultil文件中的COMIint函数)和串口(printf函数)的初始化
如果要实现printf的重定向,使用printf函数中的重定向函数int fputc(int ch, FILE *f)函数,并且包含stdio.h,并且使用MicroLib库
串口接收
参考DMAinterrupt对NVIC配置,并写中断服务函数
别忘了使能接收中断!!!--USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//////
例程里没有清除中断标志位,这个要手动设置
10、ADC
1、简介与电路
1、简介
2、电路
编程
配置
GPIO配置
static void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//ENABLE CLOCK /* Configure PC.04 (ADC Channel14) as analog input -------------------------*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOB, &GPIO_InitStructure); }
注意:参考代码里时钟没打开 需要自己写
ADC配置
void ADC_Channel8_Init(void){ ADC_InitTypeDef ADC_InitStructure; GPIO_Configuration(); /* ADC1 configuration ------------------------------------------------------*/ 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 = 1; ADC_Init(ADC1, &ADC_InitStructure); /* ADC1 regular channel8 configuration */ ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_55Cycles5); /* Enable ADC1 DMA */ ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); /* 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)); /* Start ADC1 Software Conversion */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); }
注意:ADC的时钟别忘了开启
代码参考
优化建议
1、一般中断中只进行标志位的设置或者进行数值的加减而不涉及寄存器或者函数的使用,这样是为了提高工作的效率
但是为了编程的简单,还是可以把定时检测的数码管刷新和LED定时反转放在中断当中
11、RTC
多练习
1、简介
编程
注意事项
编程
结合两个例程,其中calendar为主导,使用calib是为了使用LSI(内部的时钟)对calendar进行调整
RTC_Configuration函数全部替换
调用NVIC函数进行配置
中断处理函数
范围的检测控制
if(flag_uart){//receive over u8 shi,fen,miao; flag_uart = 0; if(rx_buf[2] == ':' && rx_buf[5] == ':'){ shi = (rx_buf[0]-'0')*10 + (rx_buf[1]-'0'); fen = (rx_buf[3]-'0')*10 + (rx_buf[4]-'0'); miao = (rx_buf[6]-'0')*10 + (rx_buf[7]-'0'); if(shi < 24 && fen < 60 && miao < 60) Time_Adjust(shi,fen,miao); } USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); }
参数
uint32_t THH,TMM,TSS;这三个参数都是读出的参数,不要直接用来赋值设置时钟,需要设置时钟参数的话设置shi,fen,miao即可
未解决问题
加上RTC后,好像会影响LCD显示,解决的办法是吧RTC初始化放在初始化函数的最后面
前面好像有个函数的初始化不能被打断
12、PWM输出与输入捕获
输出
简介
由ARR寄存器确定频率,由CCRx确定占空比
编程
配置
时钟与GPIO
/* System Clocks Configuration */ RCC_Configuration(); /* GPIO Configuration */ GPIO_Configuration();
注意:GPIO配置没有使用管脚映射
PWM主题
/* Compute the prescaler value */ PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1; /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = 665; TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); /* PWM1 Mode configuration: Channel2 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = CCR2_Val; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC2Init(TIM3, &TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); /* TIM3 enable counter */ TIM_Cmd(TIM3, ENABLE);
参考代码
易错:
一个定时器(如TIM2)不能既做定时器使用又用来产生PWM
开启AFIO时钟,使用复用推挽模式
输入捕获
参考代码
STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\TIM\PWM_Input
配置
RCC
/* System Clocks Configuration */ RCC_Configuration();
GPIO
/* Configure the GPIO ports */ GPIO_Configuration();
NVIC
/* NVIC configuration */ NVIC_Configuration();
TIM配置
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0x0; TIM_PWMIConfig(TIM3, &TIM_ICInitStructure); /* Select the TIM3 Input Trigger: TI2FP2 */ TIM_SelectInputTrigger(TIM3, TIM_TS_TI2FP2); /* Select the slave Mode: Reset Mode */ TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); /* Enable the Master/Slave Mode */ TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); /* TIM enable counter */ TIM_Cmd(TIM3, ENABLE); /* Enable the CC2 Interrupt Request */ TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);
中断处理
比较输入捕获和输出
都有RCC和GPIO的配置
输入捕获有NVIC配置,输出比较没有中断的相关配置
特别注意
PWM输入捕获只能通过通道1和通道2
如果设置TIM_SelectInputTrigger(TIM3, TIM_TS_TI2FP2);那么CCR2作为周期,CCR1作为占空比,相反类似
提纲
熟读竞赛大纲
考点
准备方法
略去的部分
1、串口的时序