GoDm@'s Blog

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

版权信息

:::warning

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

:::


时间片轮询调度


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/时钟频率))= 时钟频率/(记录差值)

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;
        }
    }
}

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

共享变量

#include "SharedData.h"

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

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

按键

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 低电平不允许写入,高电平允许写入。

#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);
	
}

串口重定向与数据处理

#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转换

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

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;
}   

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

挂一张梦老师保佑!


共计约1.5k字。于2025/04/10首次发布,最后更新于2025/06/08。

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

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