15届蓝桥杯国赛嵌入式赛前准备
15届蓝桥杯国赛嵌入式赛前准备
考纲
考前组委会临时通知,15届国赛不需要使用拓展版,所以考纲应该和省赛相同。
客观题
设计题
LED操作
CubeMX中设置PC8~15为GPIO Output(与LCD引脚复用),PD2也为GPIO Output,不需要设置其他的
LED控制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36/* USER CODE BEGIN PV */
// led
uint8_t ledBuffer = 0;
uint16_t ledPinList[8] = {GPIO_PIN_8, GPIO_PIN_9, GPIO_PIN_10, GPIO_PIN_11, GPIO_PIN_12, GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15};
/* USER CODE END PV */
/* USER CODE BEGIN PFP */
void LED_Ctrl(uint8_t ctrl);
/* USER CODE END PFP */
/* USER CODE BEGIN 2 */
LED_Ctrl(0);
HAL_Delay(3000);
for(int i = 0; i < 8; i++) // 功能:LED流水灯
{
ledBuffer = 0;
ledBuffer |= (0x01 << i);
LED_Ctrl(ledBuffer);
HAL_Delay(500);
}
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void LED_Ctrl(uint8_t ctrl){
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET); // Unlock
for(int i = 0; i < 8; i++){
HAL_GPIO_WritePin(GPIOC, ledPinList[i], ((ctrl & (0x01 << i)) ? GPIO_PIN_RESET : GPIO_PIN_SET));
}
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET); // Lock
}
/* USER CODE END 4 */LED隔500ms闪烁,持续5秒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64// 在上述代码的基础上增加下面的内容
// main.c
uint8_t errorWarningFlag = 0;
uint16_t errorWarningCount = 0;
uint8_t blinkFlag = 0;
void LED_Blink(void);
ledBuffer = 0;
LED_Ctrl(0);
HAL_Delay(1000);
errorWarningFlag = 1;
while (1)
{
if(blinkFlag){
blinkFlag = 0;
LED_Blink();
}
}
void LED_Blink(void){
ledBuffer ^= 0x01; // 异或,闪烁
LED_Ctrl(ledBuffer);
}
// stm32g4xx_it.c
/* USER CODE BEGIN PV */
// led
extern uint8_t errorWarningFlag;
extern uint16_t errorWarningCount;
extern uint8_t blinkFlag;
/* USER CODE END PV */
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
if(errorWarningFlag){
if(errorWarningCount == 4999){ // 5s
errorWarningFlag = 0;
errorWarningCount = 0;
}else{
errorWarningCount ++;
if(errorWarningCount % 500 == 1){ // 500ms
blinkFlag = 1; // Blink led
}
}
}
/* USER CODE END SysTick_IRQn 1 */
}
STM32G431微控制器内部资源
IO
中断
见串口
ADC
CubeMX添加ADC,选择SingleEnded,添加GPIO_Analog,关闭Force DMA channels interrupts,关闭adc dma中断, 开启DMA,设置Circular,半字
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// Init
// ADC
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&R38_value, 1); // R38
HAL_ADC_Start_DMA(&hadc2, (uint32_t *)&R37_value, 1); // R37
// ADC
float R37_voltage, R38_voltage;
uint16_t R37_value, R38_value;
void adc_sample()
{
R37_voltage = R37_value * 3.3f / 4096;
R38_voltage = R38_value * 3.3f / 4096;
// HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&R38_value, 1); // R38
// HAL_ADC_Start_DMA(&hadc2, (uint32_t *)&R37_value, 1); // R37
}
I2C
定时器
基础定时
输入捕获
STM32 HAL库 CUBEMX 定时器双通道 高精度捕获PWM波 - 小小小p鱼 - 博客园 (cnblogs.com)
- Clock Source: Internal Clock
- Channel1: Input Capture direct mode
- Channel2: Input Capture indirect mode
- Prescaler: 80-1
- auto-reload preload: Enable
- Input Capture Channel 1 - Polarity Selection: Rising Edge
- Input Capture Channel 2 - Polarity Selection: Falling Edge
NVIC: √
1
2
3
4
5
6
7
8
9
10
11__HAL_TIM_SET_COUNTER(&htim2, 0);
__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
captureValue = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_COUNTER(&htim2, 0);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
}正负脉宽
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57// input capture
//[0]low [1]high
uint16_t R39_CaptureValue[2];
uint8_t R39_CaptureCount;
uint16_t R40_CaptureValue[2];
uint8_t R40_CaptureCount;
uint16_t R39_PulseWidthPositive_us;
uint16_t R39_PulseWidthNegative_us;
uint16_t R39_Period_us;
uint16_t R39_Frequency_Hz;
float R39_DutyCycle;
void compute_parameters_R39()
{
R39_Period_us = R39_CaptureValue[0];
R39_PulseWidthPositive_us = R39_CaptureValue[1];
R39_PulseWidthNegative_us = R39_Period_us - R39_PulseWidthPositive_us;
R39_Frequency_Hz = 1000000 / R39_Period_us;
R39_DutyCycle = (float)R39_PulseWidthPositive_us / R39_Period_us;
}
uint16_t R40_PulseWidthPositive_us;
uint16_t R40_PulseWidthNegative_us;
uint16_t R40_Period_us;
uint16_t R40_Frequency_Hz;
float R40_DutyCycle;
void compute_parameters_R40()
{
R40_Period_us = R40_CaptureValue[0];
R40_PulseWidthPositive_us = R40_CaptureValue[1];
R40_PulseWidthNegative_us = R40_Period_us - R40_PulseWidthPositive_us;
R40_Frequency_Hz = 1000000 / R40_Period_us;
R40_DutyCycle = (float)R40_PulseWidthPositive_us / R40_Period_us;
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3){ // R39
__HAL_TIM_SET_COUNTER(&htim3, 0);
R39_CaptureValue[0] = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1); // Period
R39_CaptureValue[1] = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_2); // +Pulse
compute_parameters_R39();
}else if(htim->Instance == TIM2){ // R40
__HAL_TIM_SET_COUNTER(&htim2, 0);
R40_CaptureValue[0] = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); // Period
R40_CaptureValue[1] = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2); // +Pulse
compute_parameters_R40();
}
}
// Init
// Input Capture
// 在上升沿和下降沿捕获值,在上升沿进入中断,清空计数值,并计算参数,通道一得到周期,通道二得到正脉宽
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // R39
HAL_TIM_IC_Start(&htim3, TIM_CHANNEL_2);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // R40
HAL_TIM_IC_Start(&htim2, TIM_CHANNEL_2);
输出比较
CubeMX里TIM选择Internal Clock,设置PWM,分频,AutoReload等等
1
2
3
4
5
6
7
8
9
10
11// Init
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
pwm_freq_duty(PA11_freq, PA11_duty);
uint16_t PA11_freq = 1000;
float PA11_duty = 0.5f;
void pwm_freq_duty(uint16_t freq, float duty)
{
__HAL_TIM_SET_AUTORELOAD(&htim1, 1000000 / freq - 1);
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 1000000 / freq * duty);
}
串口
不定长指令处理
CubeMX配置:USART1,异步,波特率9600其他不改,DMA添加TX和RX,其他保持默认,打开global interrupt
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48// main.c
// UART
uint8_t rxBuffer[20] = {0};
uint8_t txBuffer[20] = {0};
void USER_UART_IRQHandler(UART_HandleTypeDef *huart);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1, rxBuffer, 20);
void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
HAL_UART_DMAStop(huart);
char ret, num;
ret = sscanf((char *)rxBuffer, "R3%c", &num);
if(ret == 1){
if(num == '7' || num == '8' || num == 'a')
{
sprintf((char *)txBuffer, "%c", num);
HAL_UART_Transmit_DMA(&huart1, txBuffer, strlen((char *)txBuffer));
}
}
memset(rxBuffer, 0, sizeof(rxBuffer));
HAL_UART_Receive_DMA(&huart1, rxBuffer, 20);
}
}
}
// stm32g4xx_it.c
extern void USER_UART_IRQHandler(UART_HandleTypeDef *huart);
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
USER_UART_IRQHandler(&huart1);
/* USER CODE END USART1_IRQn 1 */
}
DMA
见串口
按键
独立按键
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52GPIO_TypeDef *keyGPIOPort[4] = {GPIOB, GPIOB, GPIOB, GPIOA};
uint16_t keyGPIOPin[4] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_0};
uint8_t key_detect(void){
static uint8_t keyPressedFlag = 0;
for(uint8_t i = 0; i < 4; i++){
if(!keyPressedFlag && HAL_GPIO_ReadPin(keyGPIOPort[i], keyGPIOPin[i]) == GPIO_PIN_RESET){
HAL_Delay(10);
if(HAL_GPIO_ReadPin(keyGPIOPort[i], keyGPIOPin[i]) == GPIO_PIN_RESET){
keyPressedFlag = 1;
return i + 1;
}
}
}
for(uint8_t i = 0; i < 4; i++){
if(HAL_GPIO_ReadPin(keyGPIOPort[i], keyGPIOPin[i]) == GPIO_PIN_SET){
continue;
}else{
return 0;
}
}
keyPressedFlag = 0;
return 0;
}
void key_process(void){
uint8_t key = key_detect();
if(key){
LCD_Clear(Black);
}
switch(key)
{
case 1:
LCD_DisplayStringLine(Line3, (uint8_t *)" B1 ");
break;
case 2:
LCD_DisplayStringLine(Line3, (uint8_t *)" B2 ");
break;
case 3:
LCD_DisplayStringLine(Line3, (uint8_t *)" B3 ");
break;
case 4:
LCD_DisplayStringLine(Line3, (uint8_t *)" B4 ");
break;
default:
break;
}
}
// 主函数调用key_process();单双击 & 长短按处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54GPIO_TypeDef *keyPort[4] = {GPIOB, GPIOB, GPIOB, GPIOA};
uint16_t keyPin[4] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_0};
uint8_t Key_Scan(void){
for(int i = 0; i < 4; i++){
if(HAL_GPIO_ReadPin(keyPort[i], keyPin[i]) == GPIO_PIN_RESET){
return i + 1;
}
}
return 0;
}
uint32_t lastTick;
uint8_t lastKey;
uint32_t longShortTick;
uint32_t singleDoubleTick;
void Key_Proc(){
if(HAL_GetTick() - lastTick < 50) return; // Period: 50ms
lastTick = HAL_GetTick();
uint8_t key = Key_Scan();
uint8_t keyPressEdge = key & (lastKey ^ key);
uint8_t keyReleaseEdge = ~key & (lastKey ^ key);
lastKey = key;
if(keyPressEdge){
if(HAL_GetTick() - singleDoubleTick < 500){ // Double press
// Do something
LCD_Clear(Black);
LCD_DisplayStringLine(Line4, (uint8_t *)" Double press");
}else{ // Single press
longShortTick = HAL_GetTick();
// Do something
LCD_Clear(Black);
LCD_DisplayStringLine(Line4, (uint8_t *)" Single press");
}
}
if(keyReleaseEdge){
if(HAL_GetTick() - longShortTick > 1000){ // Long press
// Do something
LCD_Clear(Black);
LCD_DisplayStringLine(Line4, (uint8_t *)" Long press");
}else{ // Detect double press only when short press
singleDoubleTick = HAL_GetTick();
}
}
}
void app_main(void){
Key_Proc();
}
TFT-LCD
GPIO配置,相关引脚全部设置为GPIO Output
拷贝
lcd.c
到Src
;拷贝lcd.h
和fonts.h
到Inc
Keil双击主文件夹,添加
lcd.c
main.c
添加1
2
3
4
5
6
7
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
传感器
存储(E2PROM)
导入软件i2c底层文件
添加代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47// i2C_hal.h
void eeprom_write_byte(uint8_t data_address, uint8_t data);
uint8_t eeprom_read_byte(uint8_t data_address);
// i2c_hal.c
void eeprom_write_byte(uint8_t data_address, uint8_t data)
{
I2CStart();
I2CSendByte(0xA0);
I2CWaitAck();
I2CSendByte(data_address);
I2CWaitAck();
I2CSendByte(data);
I2CWaitAck();
I2CStop();
delay1(500);
}
uint8_t eeprom_read_byte(uint8_t data_address)
{
uint8_t read_data;
I2CStart();
I2CSendByte(0xA0);
I2CWaitAck();
I2CSendByte(data_address);
I2CWaitAck();
I2CStart();
I2CSendByte(0xA1);
I2CWaitAck();
read_data = I2CReceiveByte();
I2CSendNotAck();
I2CStop();
delay1(500);
return read_data;
}
// main.c
I2CInit();
uint8_t count = eeprom_read_byte(0x01);
count ++;
eeprom_write_byte(0x01, count);
数据存储、统计与分析计算
嵌入式综合应用程序设计与调试
注意事项
- 串口发送,没要求的字符不要发(比如”\r\n”),注意发送数量strlen(),去除最后的’\0’,用串口调试器验证一下
- 注意别少单位
- 编程完成后,从头到尾仔细验证一遍功能
- ADC使用前要校准
- 不能使用ARMCC V6编译器,否则会没分,只能用慢慢的V5
- 比赛一般要求主频80MHz,使用HSI->PLL->SYSCLK=80MHz即可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 MoBlog 子睦的博客!
评论