多路ADC

在CubeMX里配置Number Of Conversion为通道数,给几个rank都配置自己的通道,把sampling time改成最大640.5 cycles
stm1.png
myadc.c

double v1,v2;
uint v1_value,v2_value;
void getADC(ADC_HandleTypeDef *hadc){
    HAL_ADC_Start(hadc);
    HAL_ADC_PollForConversion(hadc,50);//阻塞等待rank1转换完成才能继续
    v1_value=HAL_ADC_GetValue(hadc);
    v1=v1_value*3.3/4095.0;
    
    HAL_ADC_PollForConversion(hadc,50);
    v2_value=HAL_ADC_GetValue(hadc);
    v2=v2_value*3.3/4095.0;
}

ADC校准

在ADC初始化之后,第一次采样之前,只执行一次即可,可以解决采集到最大电压只有3.25的问题

HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);

ADC过采样

什么时候需要呢,一般开完校准大部分情况就够用了,如果12位精度满足不了,可以尝试一下过采样提升精度到16位
配置连续256次采样,右移4位取平均,得到的是最大4096*16的整数,只要在软件里/16就可以把有效位数提升至16位

stm3.png
然后采样时加一句等待转换完成

HAL_ADC_Start(&hadc2);
HAL_ADC_PollForConversion(&hadc2,10);//10表示等待时间,要大于转换时间
v=HAL_ADC_GetValue(&hadc2)*3.3/16.0/4065.0;
HAL_ADC_Stop(&hadc2);//也可以不加

显示反转

需要时重新init

lcd.c

void REG_932X_Init_FB(void)
{
    LCD_WriteReg(R227, 0x3008);   // Set internal timing
    LCD_WriteReg(R231, 0x0012); // Set internal timing
    LCD_WriteReg(R239, 0x1231);   // Set internal timing
    LCD_WriteReg(R1, 0x0100);   // 反转就是这个          //0x0100
    ....
    LCD_WriteReg(R96, 0xA700);  // 这个就是反转          0xA700
    ...
}
void LCD_Init_FB(void)
{
    LCD_CtrlLinesConfig();
    dummy = LCD_ReadReg(0);

    if(dummy == 0x8230)
    {
        REG_8230_Init();
    }
    else
    {
        REG_932X_Init_FB();
    }
    dummy = LCD_ReadReg(0);

}

串口

interrupt.c

char rxdata[20];
uint8_t rx_po,rxdat;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
    rxdata[rx_po++]=rxdat;
    HAL_UART_Receive_IT(huart,&rxdat,1);
}

logic.c

extern uint8_t rx_po,rxdat;
extern char rxdata[];
if(rx_po!=0){
    int temp=rx_po;
    HAL_Delay(1);
    if(temp==rx_po){
        //解析代码
        ...
        if(strncmp(rxdata,"PPP",3)==0){ //strncmp检查到字符串相等返回0,别忘了==0
            sscanf(rxdata,"PPP%3d",rxnum);//匹配PPP后的三位数字
        }
        else{
            sprintf(text,"error\r\n");
            HAL_UART_Transmit(&huart1,(u8*)text,strlen(text),50)
        }
        rx_po=0;
        memset(rxdata,0,20);
    }
}

RTC时钟

cubemx里找到RTC

直接Activate Clock Source和Activate Calendar,下面可以设置初始时间初始日期,Data Format设置二进制格式,根据需要开启内部闹钟和使能中断
stm2.png

RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;//声明结构体

//需要调用时间时,从硬件获取时间
HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&sDate,RTC_FORMAT_BIN);
//需要更改时间时,也要把硬件的时间修改
sTime.Hours=h;
sTime.Minutes=m;
sTime.Seconds=s;
HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN); 
sDate.Year=y;
sDate.Month=m;
sDate.Date=d;
HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)//如果需要闹钟中断
{

}

占空比测量

在cubemx中再开启一个通道,选择间接测量,下面配置中此通道改为下降沿触发
stm4.png

void HAL_TIM_IC_RxCpltCallback(TIM_HandleTypeDef *htim){
    if(htim->Instance==TIM3){
        if(htim->CHANNEL==HAL_TIM_ACTIVE_CHANNEL_1){
            ccr1a=HAL_TIM_ReadCaptureValue(htim,TIM_CHANNEL_1);
            ccr1b=HAL_TIM_ReadCaptureValue(htim,TIM_CHANNEL_2);
            duty1=ccr1b*100/ccr1a;
            fre1=1000000/ccr1a;
            TIM3->CNT=0;
            HAL_TIM_IC_Start_IT(htim,TIM_CHANNEL_1);
            HAL_TIM_IC_Start_IT(htim,TIM_CHANNEL_2);
        }
    }
}

MCP4017读写

void mcp_write(u8 dat){
    I2CStart();
    I2CSendByte(0x5e);
    I2CWaitAck();
    I2CSendByte(dat);
    I2CWaitAck();
    I2CStop();
}
u8 mcp_read(void){
    u8 dat;
    I2CStart();
    I2CSendByte(0x5f);
    I2CWaitAck();
    dat=I2CReceiveByte();
    I2CSendNotAck();
    return dat;
}

按键双击

单击和长按那版的基础上改的

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
    if(htim->Instance==TIM4){
        keys[0].sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN0);
        keys[1].sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN1);
        keys[2].sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN2);
        keys[3].sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN0);
        
        for(int i=0;i<4;i++){
            switch(keys[i].jud){
                case 0:
                    if(keys[i].sta==0){
                        keys[i].jud=1;
                    }
                    break;
                case 1:
                    if(keys[i].sta==0){
                        keys[i].jud=2;
                        keys[i].time=0;
                    }
                    else{
                        keys[i].jud=0;
                    }
                    break;
                case 2:
                    if(keys[i].sta==1){
                        if(keys[i].time<200){
                            keys[i].sign=1;  //如果严格区分单双击按键功能可以放到后面
                            keys[i].cnt++;
                        }
                        keys[i].jud=3;
                    }
                    else{
                        keys[i].time++;
                        if(keys[i].time>200){
                            keys[i].lon=1;
                            keys[i].cnt=0;
                        }
                    }
                case 3:
                    keys[i].dtime++;
                    if(keys[i].dtime>200){  //结算时间0.2s过期不候
                        if(keys[i].cnt==2){
                            keys[i].dsign=1;
                        }
                        keys[i].dtime=0;
                        keys[i].cnt=0;
                        keys[i].jud=0;
                    }
                    else if(keys[i].sta==0){
                        keys[i].jud=1;        //开启第二趟判断
                    }
                    break;
            }
        }
    }
}