1. 时间片轮询调度
  2. 频率测量
  3. 共享变量
  4. 按键
  5. LCD
  6. LED
  7. 串口重定向与数据处理
  8. ADC:使用定时器TRGO信号触发ADC转换
  9. at24c02
  10. 韩信在干嘛?韩信在偷塔!能做到吗?哇他出了一个名刀·司命!猫雷!

[自用]16届烂桥杯嵌入式赛道模块代码整理

版权信息

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


时间片轮询调度

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

uint8_t TaskCount = 0;//用于记录任务数量

typedef enum {
run,wait,stop
}TaskStatus;//任务状态

typedef struct {
uint8_t TaskRunTime;
uint8_t TaskTimer;
TaskStatus Status;
void (*FC)();
}TaskStrust;//任务信息块:注意两点1.注意Timer和runtime的数据类型为uchar,最大为255 2.时间片为2ms
TaskStrust TaskList[]={0};

void OS_Init(TIM_HandleTypeDef* htim){
HAL_TIM_Base_Start_IT(htim);
TaskCount = sizeof(TaskList)/sizeof(TaskList[1]);
if(TaskCount > MAX_TASK) while(1);
}

void OS_IT_Run(void){
uint8_t i;
for(i = 0;i < TaskCount;i++){
if(TaskList[i].Status == wait){
if(++TaskList[i].TaskTimer >= TaskList[i].TaskRunTime){
TaskList[i].TaskTimer = 0;
TaskList[i].Status = run;
}
}
}

}
//自带死循环,主函数里运行
void OS_Run(void){
uint8_t j = 0;
while(1){
if(TaskList[j].Status == run){
TaskList[j].FC();
TaskList[j].Status = wait;
}
if(++j >= TaskCount) j = 0;
}
}

频率测量

核心思路:

1/((这一次的计数值-上一次记录的计数值)x (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
58
59
60
61
62
63
64
65
66
67
68
69
typedef struct {
uint8_t i;//记录当前是第几次。
int former;//前一次记录的计数值
int now;//现在记录的计数值
int result;//频率结果
uint8_t ElapsedTime;//定时器溢出次数
}FreqInfo;

reqInfo F1 = {0};
FreqInfo F2 = {0};

//启动频率测量
void Freq_Start(void){
HAL_TIM_Base_Start_IT(&htim3);//对应F1
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_Base_Start_IT(&htim8);//对应F2
HAL_TIM_IC_Start_IT(&htim8,TIM_CHANNEL_1);
}

static void Freq_Measure(FreqInfo* Fx,TIM_HandleTypeDef *htim){
if(Fx->i == 0){
Fx->result = 1000000/(Fx->now - Fx->former);
__HAL_TIM_ENABLE(htim);
}
}

void Freq_Fuc(void){
Freq_Measure(&F1,&htim3);
Freq_Measure(&F2,&htim8);
}

//中断
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){

if(htim->Instance == TIM3){
switch(F1.i){
case 0:
F1.former = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1) + 65535 * F1.ElapsedTime;
F1.i = 1;
break;
case 1:
F1.now = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1) + 65535 * F1.ElapsedTime;
__HAL_TIM_DISABLE(&htim3);
F1.ElapsedTime = 0;
F1.i = 0;
break;
default:break;
}
}

else if(htim->Instance == TIM8){
switch(F2.i){
case 0:
F2.former = HAL_TIM_ReadCapturedValue(&htim8,TIM_CHANNEL_1) + 65535 * F2.ElapsedTime;
F2.i = 1;
break;
case 1:
F2.now = HAL_TIM_ReadCapturedValue(&htim8,TIM_CHANNEL_1) + 65535 * F2.ElapsedTime;
__HAL_TIM_DISABLE(&htim8);
F2.ElapsedTime = 0;
F2.i = 0;
break;
default:break;
}
}
}

//在溢出中断中记录溢出次数

共享变量

1
2
3
4
5
6
7
8
9
10
#include "SharedData.h"

//初始化
static Data1 shareddata1 = {0,0};

//获取Data1指针的接口
Data1* get_shared_data(void) {
return &shareddata1;
}

按键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uint8_t KeyBit(void){
uint8_t keybit = 0x00;
//读取B1的电平状态并把他左移到bit0位
keybit |= HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin) << 0;
//同上
keybit |= HAL_GPIO_ReadPin(B2_GPIO_Port,B2_Pin) << 1;
keybit |= HAL_GPIO_ReadPin(B3_GPIO_Port,B3_Pin) << 2;
keybit |= HAL_GPIO_ReadPin(B4_GPIO_Port,B4_Pin) << 3;
return keybit;
}

void task_Key(void){
static uint8_t Trg,Cont;
unsigned char ReadData = KeyBit() ^ 0x0f; // 取反:在没有按键按下的情况下,其始终为0x00
Trg = ReadData & (ReadData ^ Cont); // 2
Cont = ReadData;
if(Trg && Cont){};
if(Trg && Cont == 0){};
}

LCD

引脚初始化为全低就行

LED

LD口 为 PD2 低电平不允许写入,高电平允许写入。

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
#include "LED.h"
#include "SharedData.h"

uint16_t LD_state = 0; //记录LD各位状态
uint16_t LD_state_before = 0;

void LED_init(void){
HAL_GPIO_WritePin(LD1_GPIO_Port,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(LD_load_GPIO_Port,LD_load_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LD_load_GPIO_Port,LD_load_Pin,GPIO_PIN_RESET);
}

void LED_lightLD(uint8_t LDnum){

LD_state |= (LD1_Pin << LDnum); //其他位不变,需要改变的位变为1


}

void LED_closeLD(uint8_t LDnum){

LD_state &= ~(LD1_Pin << LDnum); //其他位不变,需改变的位变为0

}


void Call_ChangeLD(void(*fc)(uint8_t),uint8_t LDnum){

LDnum -= 1;
fc(LDnum);
if(LD_state_before != LD_state){
HAL_GPIO_WritePin(LD_load_GPIO_Port,LD_load_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LD1_GPIO_Port,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(LD1_GPIO_Port,LD_state,GPIO_PIN_RESET);
HAL_GPIO_WritePin(LD_load_GPIO_Port,LD_load_Pin,GPIO_PIN_RESET);
LD_state_before = LD_state;
}
else return;

}

void LED_Fuc(void){

if(Read_Show_index(0)) {
Call_ChangeLD(LED_closeLD,1);
Call_ChangeLD(LED_lightLD,2);
}
else {
Call_ChangeLD(LED_closeLD,2);
Call_ChangeLD(LED_lightLD,1);
}
if(Read_Show_index(1)){
Call_ChangeLD(LED_lightLD,8);
}
else Call_ChangeLD(LED_closeLD,8);

}

串口重定向与数据处理

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#define BUFFER_SIZE 50
#define CMDNUM 6


void TF1(void);
void TF2(void);
void PF1(void);
void PF2(void);
void returnF1(void);
void returnF2(void);


char rx_DataBuffer[BUFFER_SIZE] = {0};
char tx_DataBuffer[BUFFER_SIZE] = {0};
char cmd[BUFFER_SIZE] = {0};

uint16_t tx_len = 0; // 当前缓冲区数据长度
uint8_t tx_busy = 0; // 发送状态标志

uint8_t rx_handle = 0; //处理接收数据标志
uint8_t rx_len = 0;

typedef struct{
uint8_t* cmd_str;
void (*fc)();
}cmdinfo;


cmdinfo cmdlist[] = {
{"TF1",TF1},
{"TF2",TF2},
{"PF1",PF1},
{"PF2",PF2},
{"F1",returnF1},
{"F2",returnF2}
};

void TheUART_start(void){
HAL_UARTEx_ReceiveToIdle_DMA(&huart1,(uint8_t*)rx_DataBuffer,0x0f);
}

void TheUART_dma_transmit(void)
{
// 如果 DMA 还在忙,则不启动新的传输
if (tx_busy) return;

tx_busy = 1; // 标记为发送中
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)tx_DataBuffer,tx_len-1);//-1为了防止傻逼烂桥杯的验证程序不通过
}


int fputc(int ch, FILE *f)
{
// 确保缓冲区不会溢出
if (tx_len < BUFFER_SIZE - 1)
{
tx_DataBuffer[tx_len++] = (uint8_t)ch;
}

// 如果遇到换行符,或者缓冲区接近满,就启动 DMA 发送
// 注意咯,如果缓冲区没满的情况下,只有检测到/n才会触发DMA的发送。所以在缓
// 冲区没满的情况下,如果你想要发送字符串"abc",printf("abc")是没用的,
// printf("abc\n")才有用哦。


if (ch == '\n' || tx_len >= BUFFER_SIZE - 1)
{
TheUART_dma_transmit();
}

return ch;
}


void TheUART_HandleRxdata(void){

if(rx_handle == 1){

uint8_t i = 0;
strcpy(cmd,rx_DataBuffer);
cmd[rx_len] = '\0';
if(!Read_Show_index(1)){
other();
rx_len = 0;
rx_handle = 0;
TheUART_start();
return;
}
for(i = 0;i < CMDNUM;i++){
if(strcmp((const char*)cmd,(const char*)cmdlist[i].cmd_str) == 0){
cmdlist[i].fc();
rx_len = 0;
rx_handle = 0;
TheUART_start();
return;
}
}
other();
rx_len = 0;
rx_handle = 0;
TheUART_start();
}
else return;
}

//命令函数
void TF1(void){

}

void TF2(void){

}

void PF1(void){

}

void PF2(void){

}

void returnF1(void){

}

void returnF2(void){

}

void other(void){
}

//中断
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1) // 确保是目标串口
{
tx_len = 0; // 清空缓冲区
tx_busy = 0; // 标记为可用
}
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){

if(huart->Instance == USART1){
rx_len = Size;
rx_handle = 1;
}

}

ADC:使用定时器TRGO信号触发ADC转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
double ADC_Data = .0;

void ADCFuc_Init(ADC_HandleTypeDef *hadc){
HAL_ADCEx_Calibration_Start(hadc,ADC_SINGLE_ENDED);//校准
HAL_ADC_Start_IT(hadc);
}


char ADCFuc_ReturnResult(void){
double result = ADC_Data;
result = (result / 4095.0) * 3.3;
}


//中断-------------------------------------------------------------//
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc){

if(hadc->Instance == ADC2){
ADC_Data = HAL_ADC_GetValue(&hadc2);
}
}

at24c02

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
include "i2c_hal.h"

#define COMMAND_W 0xA0
#define COMMAND_R 0xA1

void at24c02_Init(void){
I2CInit();
}

/// @brief at24c02写数据
/// @param address 要写入的地址
/// @param data 要写入的数据
void at24c02_Write(uint8_t address,uint8_t data){

I2CStart();

I2CSendByte(COMMAND_W);
I2CWaitAck();

I2CSendByte(address);
I2CWaitAck();

I2CSendByte(data);
I2CWaitAck();

I2CStop();
}

/// @brief at24c02读数据
/// @param address 要读数据的地址
/// @return 读出的数据
uint8_t at24c02_Read(uint8_t address){
uint8_t data = 0;
I2CStart();

I2CSendByte(COMMAND_W);
I2CWaitAck();

I2CSendByte(address);
I2CWaitAck();

I2CStart();//不释放总线的情况下重启通信,发送读命令。

I2CSendByte(COMMAND_R);
I2CWaitAck();

data = I2CReceiveByte();
I2CSendNotAck();//通知从机不再发送数据

I2CStop();

return data;
}

韩信在干嘛?韩信在偷塔!能做到吗?哇他出了一个名刀·司命!猫雷!

挂一张梦老师保佑!