【国六总结】蓝桥杯复习资料(含外设代码)
时间:2022-10-02 09:30:00
瞎叨叨
好消息十三届国赛300元买了3XL抹布!
这一次是套路,真的不需要扩展版,但是调试麻烦。
今年是参加蓝桥杯的最后一年,走出考场??快板挂小黄鱼,出去搓一顿。
其实这个资料是省赛前自己写的。不幸的是,今年没有参加扩展版的外设考试。如果你来找扩展版的外设,你可以Ctrl W下一篇。
客观题
好像官方有一本书,上面有客观题,不清楚。 这个东西基本靠平时的积累,考前不可能完全突击。以下是十二届前总结的一些客观题。
STM32相关
0.有些问题的答案可以直接回答Cube或者在手册中找;
1.RS至少需要232通信3根线;
2.STM32提供的是3哈佛结构的流水线;
3.STM32不支持双字数据类型;
4.STM32内部ADC工作原理是逐次逼近;
5.8080接口不属于串行通信;
6.RS逻辑1电平电压在232通信中 -5V~-15V ;
7.实现A/D转换的方法有逐次逼近法、计数法、双积分法;
8.根据总线传输的信息类型,嵌入式系统可分为数据总线、地址总线、控制总线等;
9.菊花链连接的接口是SPI、JTAG;
10.使用STM32开发USB外设应使用HSE时钟源;
11.Cortex-M小端格式访问代码可用于3中;
12.存储芯片的存储容量为8KB,数据线8根,地址线13根(8KB = 8 * 1024Byte = 2 ^ 13);
13.STM32F103RBT6每个DMA通道具有3事件标志;
14.功能简单但需要频繁调用的函数更适用内联函数;
15.STM32片内FLASH可以写一次16位;
16.RS485最少需要2通信根线;
17.高性能不是一般嵌入式系统设计的主要目标;
18.ARM处理器有7工作模式;
19.常用于用二进制数表示十进制数 BCD 编码;
20.Unicode 编码可用于表示汉字;
21.ASCII共128个字符;
22.STM32 微控制器 USART1 波特率通过PCLK2提供;
23.优先级中断4位编辑(0x00~0xFF -> 0000 ~ 1111);
24.Cortex M3 支持系列处理器 Thumb-II 指令集
25.Cortex M3 读写操作可以支持单个比特位的操作
26.在 STM32 一个处理器 DMA 请求,至少占用2个周期的 CPU 访问系统总线时间;
27.STM32 处理器 APB2 上的 IO 引脚最大翻转速度为18MHz;
28.USB通信速率 大于 RS232;
29.Cortex-M3处理器中的寄存器R14代表链接寄存器,R15代表程序计数寄存器;
30.NAND FLASH存储器和 NOR FLASH存储器的区别在于NAND FLASH 擦除单元较小;
31.Corte-M3有2同时只能调用一个堆栈;
32.微控制器断电后重新上电、NRST低电平引脚、微控制器看门狗定时器计数终止、清除复位寄存器的特定位置可以触发 STM32 微控制器复位;
33.外设分为内核外设和片上外设,内核外设有Systick,NVIC,SCB,MPU;
34.
大部分数电模电
1.数字时序逻辑电路输出电路原状态和当前输入有关;
2.能够实现线与的是OC门;
3.当温度升高时,二极管的反向饱和电流会升高增大;
4.设计一个8421BCD至少需要代码计数器4个触发器;
5.高阻抗信号源和低阻抗负载间适合接入共集电路阻抗匹配;
6.将三角波转换为矩形波需要使用特殊触发器施密;
7.将矩形波输入积分电路可获得三角波;
8.场效应管导电阻 V G S V_{GS} VGS 和 温度 有关;
9.N 在由触发器组成的计数器中,最有效的状态2^N个;
10.为了提高电压比较器的抗干扰能力,应选择滞回比较器;
11. 两个电压放大倍数相同(电路相同,晶体管相同) A 和 B 在负载开路的条件下,放大同一信号源的电压并测量电路 A 电路输出电压小,不考虑仪器的测量误差,说明 A 电路输入电阻小;
12. RLC 串联电路谐振频率为=当频率为1000时, 800Hz 激励正弦电压源时,电路呈现容性;
13. 信号不全都可以用确定的时间函数来描述;
14. 由 5 个 D 由触发器组成的环形计数器的计数长度为5;
15. 在数字电路中,三极管相当于开关,通常工作饱和、截止状态;
16. 如果两个逻辑函数是恒定的,它们必须是唯一的真值表;
17. 没有压电效应的滤波器是LC滤波器、RC滤波器;
18. 脉冲波形常用于施密特触发器整形;
19. 运算放大器差模增益高、输入阻抗高、失调较小、输出阻抗低;
20. 单个操作放大器和多个电阻不能构成振荡器、乘法器;
21. 分析操作放大器的基础是 U N ≈ U P U_N≈U_P
22. 三态门的输出状态包括:高电平,低电平,高阻态;
23.
三极管
工作参数计算
I C M I_{CM} ICM:集电极最大允许电流;
P C M P_{CM} PCM:集电极最大允许功率, P C = I C ∗ U C E P_{C}=I_C*U_{CE} PC=IC∗UCE;
U ( B R ) C E O U_{(BR)CEO} U(BR)CEO:反向击穿电压, U C E > U ( B R ) C E O U_{CE}>U_{(BR)CEO} UCE>U(BR)CEO会导致 I C I_C IC急剧增大,三极管烧毁;
例题:
题目给了四组参数,没有任何一组 U C E U_{CE} UCE过大,但B选项 I C I_C IC过大,先选上;然后开始计算剩下各组 P C P_{C} PC,C选项120mW大于最大功率,故答案选BC。
工作状态判断
对于NPN型三极管
放大区: U C > U B > U E U_C>U_B>U_E UC>UB>UE,发射结正偏,集电结反偏;
截止区: U B < U C , U B < U E U_B
饱和区: U C E < U B E U_{CE}
三极管用作开关管时,通常工作在饱和区和截止区。
例题:
A选项明显符合放大区要求,B选项在截止区,C选项在饱和区,D选项注意负号,实际也是在放大区,答案选AD。
运算放大器
首先是虚短和虚断的概念。简单说,虚短就是运放工作在线性区时,两输入端可被视为等电位(实际并不能物理短路两输入端);虚断是由于理想运放输入电阻无穷大,所以几乎没有电流流入运放输入端,可以被视作断路。
最常规的例1
根据虚短,反相输入端电流全部流过80K反馈电阻,可以对红色箭头所指节点应用KCL, 0.1 V 10 K + 0.2 V 10 K = U O 80 K \frac{0.1V}{10K} + \frac{0.2V}{10K} = \frac{U_{O}}{80K} 10K0.1V+10K0.2V=80KUO,最后算出 U O = 2.4 V U_O = 2.4V UO=2.4V,反向输入端加上负号,故答案是-2.4V。
挖了大坑的例2
有些题目会强调单电源供电,此时结果就不符合上面的公式了。例题:
正常思路,前面是同向电压跟随器,后面是反向放大器,算出来-4V,选C。但这个题强调了是DC 12V单电源供电,所以不选C,选D。Multisim仿真结果是1.36V,具体咋算我也不大清楚(祈祷别出个这玩意儿给你算吧🙏)。
还能这样玩的例3
带RC电路的题目,需要计算上限/下限频率。例题:
RC电路截止频率 f = 1 / ( 2 π R C ) f=1/(2{\pi}RC) f=1/(2πRC),其中电容C的单位为法拉F。对于这个题,先换算电容单位,10uF=10^-6F=0.00001F,带入公式计算,约为159.155Hz,按题目要求取整,答案为159Hz。放大倍数 = - 10K / 100 = -100,答案为-100倍。
我看不懂但大受震撼的例4
还有一种差分减法器,如题:
这个就只有问百度了,真考到听天由命🙏
十三届第一场省赛的例5
写这部分的的时候是2022年5月8日,离第二场省赛好像还有6天。因为看不懂一个多月前写的虚短虚断是啥玩意儿,于是把第一场省赛的题拿来分析一遍。
先说仿真结果,3V。
然后再来分析,如下图:
前一个部分,典型的电压跟随器,输出电压(蓝色节点)为+1V。后半部分,由运放虚短可知,绿色节点电压与正向端输入电压相等,应为+2V;由运放虚断,可提出题目下面的那个手绘模型,接下来对绿色节点用KCL就解出来了:流出电流1mA,流入也应为1mA,红色节点电压应为绿色电压+I*R=+3V,分析完毕。
外设代码
HAL库还是LL库?
个人认为,评测系统中的一个指标是代码大小。在用HAL库狠凹第一套模拟题之后,各项功能已经是1:1复刻官方参考答案了,但仍只有89.2分(官方LL库答案98.2分),据此猜测代码大小也是评分指标之一,当然也只是猜测。之后我也尝试用LL库编写程序,但奈何水平不够,各个功能整合起来之后稳定性不如HAL库,以后有机会一定好好学学LL库。
FPU
相传在MDK的options for target的target选项卡下可以直接设置,但还是感觉用宏定义更安心一点,反正也费不了一分钟。在模拟测评中开不开对分数没有影响。
//MDK->Options for Target->C/C++->Define
__FPU_USED=1U,__TARGET_FPU_VFP,ARM_MATH_CM4
LED
LED部分最简单的操作方法就是操作ODR寄存器,有两种方法,在模拟评测系统上分数是一样的,但方法二更直观一些,临场不容易写错+易分析,故优先采用方法2。寄存器内容参考RM0440参考手册(英文手册实际上也没那么难看懂,至少GPIO部分不会有很多很多专业词+生僻词)。
//方法一:按位运算得到ODR的值
uint8_t code = 0x00; //注意两个方法初始code恰好相反
void LED_SetStatus(uint8_t status, uint8_t serial)
{
if(status == LED_ON)
{
code = code | (0x01 << (serial - 1));
GPIOC->ODR = ~(code << 8);
}
else if(status == LED_OFF)
{
uint8_t temp = ~code;
temp = temp | (0x01 << (serial -1));
code = ~temp;
GPIOC->ODR = (temp << 8);
}
else
{
code = 0x00;
GPIOC->ODR = (0xFF << 8);
}
HAL_GPIO_WritePin(LE_GPIO_Port, LE_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LE_GPIO_Port, LE_Pin, GPIO_PIN_RESET);
}
//方法二:操作BSRR控制ODR
void LED_SetStatus(uint8_t serial, uint8_t status)
{
if(status == LED_ON)
{
GPIOC->ODR = code << 8;
GPIOC->BSRR = 0x01 << (23 + serial); //Set BRx
code = GPIOC->ODR >> 8;
}
else if(status == LED_OFF)
{
GPIOC->ODR = code << 8;
GPIOC->BSRR = 0x01 << (7 + serial); //Set BSx
code = GPIOC->ODR >> 8;
}
else
{
code = 0xFF;
GPIOC->ODR = (code << 8);
}
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
}
按键
实现方案很多,还有用定时中断检测按键的(这个试了试效果不是很稳定,就弃了,也可能是我写的姿势不对)。除了写的这个方法,还可以用读IDR实现。经实验,分数也是没差别。长短按?省赛没写,国赛也不是用自己电脑,就简单说个思路吧:用1ms(1KHz)的定时器中断数数,判断是长按还是短按。
uint8_t KEY_CheckStatus(void)
{
keyPressed = 0;
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
{
keyPressed = 1;
}
}
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)
{
keyPressed = 2;
}
}
if(HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) == GPIO_PIN_RESET)
{
keyPressed = 3;
}
}
if(HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin) == GPIO_PIN_RESET)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin) == GPIO_PIN_RESET)
{
keyPressed = 4;
}
}
if(keyPressed != keyPrevious) //检测与上次按键是否相同
{
keyPrevious = keyPressed; //不同则代表是第一次检测到,更新上次键值
}
else
{
keyPressed = 0; //相同则代表之前已经检测过了,按下键值返回0
}
return keyPressed;
}
UART
一步登顶,直接DMA+空闲中断,省得纠结。
void UART_Init(void) { __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); HAL_UART_Receive_DMA(&huart1, rxBuf, sizeof(rxBuf)); } void UART_IRQHandler(UART_HandleTypeDef *huart) //记得添加到it文件里 { if(huart->Instance == USART1) //串口1中断,如果是扔对应IRQHandler里的话这个if实际上没必要 { if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) //串口1空闲 { HAL_UART_DMAStop(&huart1); uint8_t lenMsgReceived = sizeof(rxBuf) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); memcpy(rxDest, rxBuf, lenMsgReceived); //copy数据,不在中断里处理数据 memset(rxBuf, 0x00, sizeof(rxBuf)); rxFlag = 1; __HAL_UART_CLEAR_IDLEFLAG(&huart1); HAL_UART_Receive_DMA(&huart1, rxBuf, sizeof