瞎叨叨
好消息十三届国赛300元买了3XL抹布! 这一次是套路,真的不需要扩展版,但是调试麻烦。 今年是参加蓝桥杯的最后一年,走出考场??快板挂小黄鱼,出去搓一顿。 其实这个资料是省赛前自己写的。不幸的是,今年没有参加扩展版的外设考试。如果你来找扩展版的外设,你可以Ctrl W下一篇。
客观题
好像官方有一本书,上面有客观题,不清楚。 这个东西基本靠平时的积累,考前不可能完全突击。以下是十二届前总结的一些客观题。
STM32相关
0.有些问题的答案可以直接回答Cube或者在手册中找; 1.RS至少需要232通信根线; 2.STM32提供的是哈佛结构的流水线; 3.STM32不支持数据类型; 4.STM32内部ADC工作原理是; 5.不属于串行通信; 6.RS逻辑1电平电压在232通信中 、
大部分数电模电
1.数字时序逻辑电路输出
三极管
工作参数计算
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, U_B<U_E UB<UC,UB<UE,发射结和集电结反偏; 饱和区: U C E < U B E U_{CE}<U_{BE} UCE<UBE,发射结和集电结正偏; 三极管用作开关管时,通常工作在
运算放大器
首先是虚短和虚断的概念。简单说,虚短就是运放工作在线性区时,两输入端可被视为等电位(实际并不能物理短路两输入端);虚断是由于理想运放输入电阻无穷大,所以几乎没有电流流入运放输入端,可以被视作断路。
最常规的例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
还能这样玩的例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