12月过了大半,学校电赛初选在即,准备写一篇文章来确认自己的能力以及记录一下方法方便以后复习。
(总体基于stm32cubemx和keil5使用hal库开发。)
以下就是校初选的大概任务列表
一,显示模块驱动:
由于学习尚浅,对于单片机的基础理解并不太深,暂时无法自主编写屏幕驱动。
1,对于7针oled:直接移用官方u8g2库。
关于oled图片显示,可以看看朋友写的文章具体图像取模和显示-CSDN博客。
还有关于oled的静动态波形显示HAL库U8g2实现静动态波形显示-CSDN博客
2,对于st7735驱动的tftlcd屏幕:见网上大佬编写的。STM32驱动ST7735彩色屏幕(任意分辨率),驱动不了你顺着网线来打我az
在此之上我还添加了几个函数用于显示汉字,标点,建坐标轴等(其实只要看懂作者代码都能编写)
void zuobiaozhou(uint8_t x,uint8_t y,uint8_t x1, uint8_t y1){
//x,y起点,x1,y1终点(由于从x=0,y=128处开始,因此y1小于y,x1大于x)
int a=x,b=y;//暂存x,y的值
for(;x<x1;x++){
ST7735_DrawRectangle(x, b, 1, 1, ST7735_WHITE);
}
for(;y>y1;y--){
ST7735_DrawRectangle(a, y, 1, 1, ST7735_WHITE);
}
//建坐标轴
ST7735_DrawRectangle(x1+1, b, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x1, b-1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x1-1, b-2, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x1-2, b-3, 1, 1, ST7735_WHITE); //画箭头
ST7735_DrawRectangle(a, y1-1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(a-1, y1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(a-2, y1+1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(a-3, y1+2, 1, 1, ST7735_WHITE);
}
//参数
//x y 起点坐标
//num 要显示的汉字对应于字库中的数值
//size 要显示的汉字的大小
void LCD_ShowChinese(uint8_t x,uint8_t y,uint8_t num,uint8_t size)
{
uint8_t temp,t,t1;
uint8_t y0=y;
uint8_t csize=(size/8 + ((size%8)?1:0)) * size; //16/8+1
for(t=0;t<csize;t++)
{
if(size==16)
{
temp=chinese[num][t];
}
else
return;
for(t1=0;t1<8;t1++)
{
if(temp&0x80)
ST7735_DrawRectangle(x, y, 1, 1, ST7735_WHITE);
else
ST7735_DrawRectangle(x, y, 1, 1, ST7735_BLACK);
temp<<=1;
y++;
if((y-y0)==size)
{
y=y0;
x++;
break;
}
}
}
}
void DIAN(int x,int y){
ST7735_DrawRectangle(x, y, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x+1, y, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x-1, y, 1, 1, ST7735_WHITE); //标点标大一点点
ST7735_DrawRectangle(x, y+1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x, y-1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x+1, y+1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x+1, y-1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x-1, y+1, 1, 1, ST7735_WHITE);
ST7735_DrawRectangle(x-1, y-1, 1, 1, ST7735_WHITE);
}
其中显示 汉字需要用到汉字取模软件PCtoLCD2002。可以自行到浏览器上搜索下载。
只需要汉字取模后存在一个二维数组中即可。
显示图像其实同理,不再赘述。
二,蜂鸣器,彩色led灯驱动
相比与屏幕驱动这个就好理解很多也容易非常多。
1,蜂鸣器
首先我们要了解蜂鸣器的构造
对于我们购买的蜂鸣器驱动模块一般都包含以下几个部分:一个三极管、一个蜂鸣器、一个续流二极管和一个电源滤波电容。
对于蜂鸣器模块的使用,只需要知道VCC接正极,GND接地,I/O接单片机io口。蜂鸣器分为高电平驱动和低电平驱动,根据模块自己选择通入高电平和低电平即可。(当然以后想自己pcb时,就需要学会上述原理图了)
对于改变蜂鸣器的音调,实则改变正极的电压即改为用io口pwm输入,改变pwm占空比即可改变蜂鸣器音调。
2,彩色led
一般我们使用的彩色led都是四脚共阳led,显而易见,就是一个管脚连接正极,另三个管脚连接单片机io口,初始置高,后续分别改变io口状态即可控制颜色。
三,外部输入设备驱动
1,矩阵键盘
矩阵键盘最常见的就是4x4矩阵键盘,但是无论是几x几都是同理。
为了节省单片机的io口,一般我们选择将矩阵键盘的每行每列连在一起,市面上很多现成矩阵键盘模块也是如此
如此连接后,需要每根线连接一个io口,虽然io口需求任然很大,但是相比与每个按钮单独一个io口就节省很多。
我使用矩阵键盘一般是用最简单的逐行逐列扫描法,即将行(或列)对于io口设置为上拉输入,列(或行)设置为推挽输出。在key.c中分别对每个io口置高然后检测输入io口电平,即可确定按下位置。
以下是一个2x2矩阵键盘扫描代码的例子。(其中延时目的是消抖。)
int GETKEY(){
int k;
HAL_GPIO_WritePin(X1_GPIO_Port,X1_Pin,1);
HAL_GPIO_WritePin(X2_GPIO_Port,X2_Pin,0);
if(HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==1&&HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==0){
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==1&&HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==0){
k=2;
}
}
else if(HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==1&&HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==0){
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==1&&HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==0){
k=1;
}
}
HAL_GPIO_WritePin(X1_GPIO_Port,X1_Pin,0);
HAL_GPIO_WritePin(X2_GPIO_Port,X2_Pin,1);
if(HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==1&&HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==0){
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==1&&HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==0){
k=4;
}
}
else if(HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==1&&HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==0){
HAL_Delay(10);
if(HAL_GPIO_ReadPin(Y2_GPIO_Port,Y2_Pin)==1&&HAL_GPIO_ReadPin(Y1_GPIO_Port,Y1_Pin)==0){
k=3;
}
}
return k;
}
2,串口屏()
(作者还没用过,因此这里一片空白,以后再来补档)
四,定时器编程
定时器在所有单片机外设里都是很重要的一环,属于是离开了他单片机就没法活了。因此其相关的知识也是数不胜数,这里就不多介绍了。想了解定时器的相关内容可以搜索b站江协科技(标准库)或者原子哥(hal库)学习定时器的基础知识,也可以看看站内大佬写的定时器讲解STM32-定时器详解,这里就讲讲用法。
1,输入捕获(以捕获pwm波为例)
首先是在cubemx中的设置
(1)模式选择复位模式(2)触发源选择TI1FP1(3)选择时钟源为内部时钟(4)将通道一和通道二分别设置成输入捕获直接和间接模式(5)开启定时器中断
在设置中将通道一二分别设置为上升沿触发和下降沿触发。
接下来的代码编写也非常简单。
以下是main函数外编写部分
float duty=0; //占空比
float freq=0; //频率
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1&&htim->Instance==htimx.Instance)
{
freq= 84000000*1.0f/(TIMx->CCR1+1);
//由于作者使用的是stm32f401,因此此处使用84000000.需要根据单片机型号更改
duty=(float)(TIMx->CCR2+1)/(TIMx->CCR1+1);
}
} //定义中断回调函数,实时计算占空比和频率
//其中想x使用的哪个定时器就改成哪个
还需要在main函数中开启输入捕获
HAL_TIM_IC_Start_IT(&htimx, TIM_CHANNEL_x);
//同样根据使用时钟不同更改x
这样过后就可以得到所测pwm波占空比duty和频率freq。
2,pwm产生
首先还是cubemx中的配置
(1)时钟源选择内部时钟(2)选择一个通道开启pwm输出(3)根据需要改变pwm的频率和占空比
代码部分
HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_x);//开启对应pwm通道
TIMx->CCR1=x;//修改ccr1值来改变pwm波占空比
3,定时器的定时作用(定时器中断)
借鉴了大佬写的文章【STM32】HAL库 STM32CubeMX教程六----定时器中断_hal_tim_irqhandler-CSDN博客
定时器的定时作用,顾名思义,就是一个定时作用。其中最常用的就是定时器中断了。比如说我们要使用红外模块,在检测到东西的时候执行操作,但是正常在执行主函数while循环的时候cpu是无法执行其他事情的,就需要一个定时器中断,像闹钟一样叫cpu一声,然后cpu放下手头工作来看一眼然后再接着执行。
具体使用方法:
cubemx中设置和pwm输出大同小异,同样是选择内部时钟源,并且修改预分频系数和自动重装载值,使能自动重装载,打开中断,然后进入文件。
cubemx已经帮我们做好了一切,现在只需要进行如下步骤(函数中的内容就看自己操作了)
HAL_TIM_Base_Start_IT(&htimx); //在主函数中开启定时器
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static unsigned char ledState = 0;
if (htim == (&htimx))
{
if (ledState == 0)
/
else
ledState = !ledState; //编写中断回调函数
}
}
五,串口传输
这个就直接看我以前写的文章啦stm32通过串口(轮询,中断,DMA)发送信息来控制单片机(hal库+cubemx)_stm32 hal 串口dma发送-CSDN博客
六,ADC模块(测电压)
adc(模数转换器)模块其实就是一个电压表,通过输入一个电压与地极比较得到测量值,再通过计算就能得到电势差即电压值。像我们测量正弦波这些都是在此之上进行数据处理得到。再者,由于大部分模块例如温度测量模块这些大多是通过测电势差然后处理计算才得到的相应数据,因此也会用到adc。
因此,这里就讲一下如何用adc测电压。打开cubemx,开始操作。
(1)打开一个adc通道。(2)开启连续转换模式。
由于这里我们只讲测电压方法,因此不再需要其他操作,直接打开文件。
uint16_t ADC_Value; //定义一个变量来储存测量值
uint16_t V;//定义一个变量来储存电压
HAL_ADC_Start(&hadc1);//在主函数中打开adc通道
HAL_ADC_PollForConversion(&hadc1, 50);
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
//可以放在while中循环测量
{
ADC_Value = HAL_ADC_GetValue(&hadc1);//得到adc测量值
V=ADC_Value*3.3f/4096;//通过公式计算电压
}
注意:(1)adc测量电压的范围默认是 VREF- ≤ VIN ≤ VREF+,我们使用stm32一般来说就是0-3.3v若接入过大电压可能烧坏芯片。
(2)ADC时钟不可以超过14M,因此在分频的时候要注意。
(3)采样时间一般越长越精准。
当然,未涉及的还很多,比如说开启adc的dma降低cpu负载,还有adc的多通道采集,扫描模式等。由于这是一篇总结性文章就不多赘述了,想了解可以看看站内大神的文章手把手教你开发stm32——ADC(基于hal库)_hal库adc-CSDN博客
STM32 (九)ADC_adc中断-CSDN博客
七,外置传感器(超声波模块为例)
在众多外置传感器模块中,超声波模块非常典型。那这就浅讲一下超声波模块。
先看看接线图和时序图。
通过两图我们可以知道,给超声波供电后,只需要一个gpio口给trig口一个10us的高电平,就可以驱动超声波发送。再者我们还需要用到上文提到的定时器输入捕获功能,捕获超声波模块的返回信号,再通过计算时间来得到距离测量值。
首先是cubemx配置,其他与上文输入捕获一致,还需另开一个gpio输出给trig信号。
然后打开文件,开始编写代码。
float pulseWidth;//定义一个变量储存脉宽
void Trig()//触发函数
{
HAL_GPIO_WritePin(GPIOX,GPIO_PIN_x,GPIO_PIN_SET);
HAL_Delay(1);//延时1ms
HAL_GPIO_WritePin(GPIOX,GPIO_PIN_x,GPIO_PIN_RESET);
}
//编写函数给trig发送信号。
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1&&htim->Instance==htim2.Instance)
{
freq= 84000000*1.0f/(TIM2->CCR1+1);
duty=(float)(TIM2->CCR2+1)/(TIM2->CCR1+1);
pulseWidth=(float)TIM2->CCR2/84000000*1.0f; //在上文的基础上增加这句计算脉宽的
}
}
这样下来距离就等于pulseWidth*34000/2。
八,总结
学会上述模块后,只需要在考试时综合一下就好啦。(当然还需要学会示波器等仪器的使用)
校初选加油!!!!