记性差,写下来方便看;
本人主要学习配套视频是电子设计工坊的视频,觉得代码很精简,受益匪浅;
//关闭所有灯
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_All, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
//打开指定的灯
HAL_GPIO_WritePin(GPIOC, led_value<<8, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
若是需要对led以0.1s闪烁,可以以0.1s一次执行LED_Process(void);
在LED_Process中led进行异或操作,可以使得电平不断翻转;
1.STM32CUBEMX:
配置按键连接引脚为GPIO_Input模式;
无需配置无上下拉模式;
PS:按键引脚已经上拉到高电平;
IO口:未按下,高电平;按下,低电平;
1.key编写;理解一下,多打几遍就记住了;
#define KB1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KB2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KB3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define KB4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define KEYPORT KB1 | (KB2<<1) | (KB3<<2) | (KB4<<3) | 0xf0
u8 Trg; //单次触发
u8 Cont; // 长按
void Key_Read(void)
{
u8 ReadData = (KEYPORT)^0xff;//括号一定要有 ^的优先级很低
Trg = ReadData & (ReadData ^ Cont);
Cont = ReadData;
}
2.使用方法;
__IO uint32_t KEYTick = 0;
void Key_Process(void)
{
if(uwTick - KEYTick < 10) return ;
KEYTick = uwTick;
Key_Read();
//短按
if(Trg & 0x01) //B1
{
do somethings...
}
if(Trg & 0x02) //B2
{
do somethings...
}
//长按
if(Cont & 0x01) //B1
{
do somethings...
}
}
1.STM32CUBEMX:
设置ADC的GPIO为ADC输入模式;
设置成单端模式(Single-edned);
特别注意:在时钟树界面查看,ADC的时钟是否打开,为80M;
2.从模板中移植adc.c和adc.h代码到编程工程;(移植流程非常固定)
2.1在main函数当中添加adc.h,并添加ADC初始化代码;
2.2配置stm32g4xx_hal_conf.h文件,取消注释#define HAL_ADC_MODULE_ENABLE);
2.3开启ADC的外设时钟,并添加结构体定义;
2.4添加ADC相关的HAL库驱动文件(stm32gxx_hal_adc.c和stm32gxx_hal_adc_ex;
3.调用函数:
(1)HAL_ADC_Start:启动ADC转换;
(2)HAL_ADC_GetValue:读取ADC采集的值;
PS:测量一次ADC就要启动一起ADC;
HAL_ADC_Start(&hadc1);
ADC1_Val = HAL_ADC_GetValue(&hadc1);
ADC1_R38_Voltage = ADC1_Val / 4096.0f * 3.3f;
有些题目需要对adc采集到的数据进行滤波处理;
进行均值滤波,采集10个数据求平均即可;
1.移植资源数据包里的i2c.h和i2c.c文件;初始化I2CInit();
2.在i2c.c文件当中编写AT24C02读写函数;并声明函数;
3.测试读写函数;
1.AT24C02的读写函数;
AT24C02的设备地址是0xA0;
特别注意:写操作之后需要延时一段时间,给芯片写的时间;
//写24C02
void EEPROM_Write(u8 add,u8 dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
HAL_Delay(5); //延时5ms
}
//读24C02
u8 EEPROM_Read(u8 add)
{
u8 dat;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(add);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
dat = I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return(dat);
}
以下函数可实现板子复位次数,在main函数中进行初始化,不需要再while中一直执行;
void EEPROM_Init(void)
{
if(EEPROM_Read(0x22) == 0x78) //不是第一次开机,open_num++
{
open_num = EEPROM_Read(0x00); //上电读取open_num的值
open_num++;
EEPROM_Write(0x00, open_num);
}
if(EEPROM_Read(0x22) != 0x78) //是第一次开机,open_num写为1
{
EEPROM_Write(0x22, 0x78);
EEPROM_Write(0x00, 1);
open_num = EEPROM_Read(0x00); //上电读取open_num的值
}
}
1. STM32CUBEMX:
设置ADC的GPIO为ADC输入模式;
并设置成单端模式(Single-edned);
2.设置ADC的转换通道数目(Numbei Of Conversion)大于1时;要设置Rank和Sample Time;
3.将新生成的代码移植到工程文档;
4.在i2c.c文件当中编写MCP4017读写函数;并声明函数;测试完成读写程序;
5.调用函数:
(1)HAL_ADC_Start:启动ADC转换;
(2)HAL_ADC_GetValue:读取ADC采集的值;
1.MCP4017的读写函数;
MCP4017的设备地址是0x5E;
void MCP4017_Write(u8 Val)
{
I2CStart();
I2CSendByte(0x5E);
I2CWaitAck();
I2CSendByte(Val);
I2CWaitAck();
I2CStop();
}
//读MCP4017
u8 MCP4017_Read(void)
{
u8 val;
I2CStart();
I2CSendByte(0x5F);
I2CWaitAck();
val = I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return val;
}
以下代码adc1转换有两个通道,adc2转换有一个通道;
//adc1
u16 adc1_r38_value;
u16 adc1_mcp_value;
u8 mcp_data;
float adc1_r38_vol;
float adc1_mcp_vol;
//adc2
u16 adc2_r37_value;
float adc2_r37_vol;
void ADC_Process(void)
{
MCP4017_Write(0x6f); //写数据
mcp_data = MCP4017_Read(); //读数据验证
//获取adc1通道5的电压值 通道5排序第1
HAL_ADC_Start(&hadc1);
adc1_mcp_value = HAL_ADC_GetValue(&hadc1);
adc1_mcp_vol = adc1_mcp_value / 4096.0f * 3.3f;
//获取adc1通道11的电压值 通道11排序第2
HAL_ADC_Start(&hadc1);
adc1_r38_value = HAL_ADC_GetValue(&hadc1);
adc1_r38_vol = adc1_r38_value / 4096.0f * 3.3f;
//获取adc2通道15的电压值
HAL_ADC_Start(&hadc2);
adc2_r37_value = HAL_ADC_GetValue(&hadc2);
adc2_r37_vol = adc2_r37_value / 4096.0f * 3.3f;
}
1.LCD分辨率320*240;最多显示十行,
从Line0~Line9;每行最多显示20个字符;
2.sprintf函数:需包含头文件“stdio.h>”;
注意避免长数据对短数据的覆盖;
3.使用赛场资源历程HAL_06_LCD可以不用再配置LED的引脚,可直接使用;
但是需要配置PD2为输出模式;低电平保持,高电平可修改;
1.sprintf函数的基本使用方法;定义char display_buf[20]数据存放数组;
1.STM32CUBEMX:
配置DAC输的GPIO为DAC输出通道;
模式为输出到外部引脚(Connected to external pin only);
PA4-DAC1_CH1 PA5-DAC1_CH2;
2.将新生成的代码移植到工程文档;无需开启外设时钟;
3.调用函数:
(1)HAL_DAC_SetValue:设置DAC转换的值;
(2)HAL_DAC_Start:启动DAC转换;
<<数据手册>>4.4.10
DAC_CH1_Voltage = (1.1f/3.3f)*4095;
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, DAC_CH1_Voltage);
HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);
1.STM32CUBEMX:
激活RTC的时钟和日历功能;(打两个勾即可)
2.配置RTC的时钟,初始化状态,将RTC模块的时钟配置成1Hz,对应1s;
3.RTC采用内部低速时钟(LIS);即32KHz,默认就是,不需要修改;
4.将rtc.c和rtc.h移植到工程文档;开启外设时钟;
5.调用函数:
(1)HAL_RTC_GetTime:获取时间;
(2)HAI_RTC_GetData:获取日期;
1.获取年月日和时分秒毫秒;可分闰年大月小月;
2,注意编码格式是BCD编码;调试和LCD显示时用十六进制%x进行显示;
RTC_TimeTypeDef sTime;//时间结构体
RTC_DateTypeDef sDate;//日期结构体
void RTC_Process(void)
{
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BCD); //获取时间
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BCD); //获取日期
//显示日期
sprintf((char *)display_buf, "%02x-%02x-%02x", sDate.Year, sDate.Month, sDate.Date);
LCD_DisplayStringLine(Line2, display_buf);
//显示时间
sprintf((char *)display_buf, "%02x-%02x-%02x", sTime.Hours,sTime.Minutes,sTime.Seconds);
LCD_DisplayStringLine(Line3, display_buf);
}
1.STM32CUBEMX:
配置USART1的PA9和PA10为串口收发引脚;
USART的Mode选择Asynchronous;
2.根据需求,配置USART的波特率,数据位长度,奇偶校验位,停止位和时钟;
3.将uart.c和uart.h移植到工程文档;开启外设时钟;
4.调用函数:
(1)HAL_UART_Transmit:串口发送函数;
硬件连接已确定,只能使用PA9和PA10;
1.在usart.c当中编写fputc函数;
int fputc(int ch, FILE *f)
{
/* Your implementation of fputc(). */
HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, 50);
return ch;
}
可以在菜单栏的Help的uVision help查找fputc;
里面有大部分的代码,修改成上方那样即可;
2.在main函数中调用printf可输出字符和变化的数据; 用STC-ISP进行接收数据
PS:字符的长度需要-1,因为包含了结束符;
u8 USART_Arr[]={"你好\r\n"};//无需定义数组的大小 否则产生结束符影响下一次串口发送
void USART_Process(void)
{
HAL_UART_Transmit(&huart1,(unsigned char*)"HelloWorld\r\n",sizeof("HelloWorld\r\n")-1,50); //有结束符
HAL_UART_Transmit(&huart1,(unsigned char *)USART_Arr,sizeof(USART_Arr)-1,50);
printf("D = %d \r\n", 123); //无结束符
printf("F = %f \r\n", 123.123);
printf("HelloWorld \r\n");
}
1.勾选NVIC Setting中的使能USART1的中断;
2.初始化里,开启中断HAL_UART_Receive_IT(&huart1,usart_buf,1);
3.main.c文件当中重定义接收回调函数void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
4.在回调函数中打断点,硬件仿真用STC_ISP发送数据测试能不能进入中断里面;
5.编写接收回调函数,函数需要调用HAL_UART_Receive_IT(&huart1,usart_buf,1);
1.定义一个两位数组,用于中断接收收到的数据;
2.再定义一个多位数组,存放每次两位数组接收到的数据;
3.使用 if 函数对换行符进行判断,判断接收到的数据是否结束;换行符为‘\n’ ;0a 0d
4.发送数据时,需要在数据结尾带上0a 0d;
5.编写串口清除函数RxIdle_Process();
函数功能:对存放数据的数组进行清除,防止数据的堆叠;保证每次接收到的数据从第一位开始;
6.可以将接收到数据进行处理,但是要避免出现0a 0d这两个数据处理外设;
u8 usart_buf[2]; //中断接收数组
u8 rx_buf[10]; //数据存放数组
u8 rx_cnt = 0; //存放数组位
u8 rx_flag_lcd = 0;
__IO uint32_t USARTTick = 0;
//串口接收
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//do somethings
USARTTick = uwTick; //更新计时
rx_buf[rx_cnt] = usart_buf[0]; //存放接收字符
rx_cnt++;
if(usart_buf[0] == '\n')//换行符 0a 0d
{
rx_cnt = 0;
if( (rx_buf[1] == 0x0a) || (rx_buf[1] == 0x0d))//避免换行符提前出现,若是多个位置如何避免
{
rx_buf[1] = 0x00;
}
LED_Disp(rx_buf[1]);
rx_flag_lcd = 1;
}
HAL_UART_Receive_IT(&huart1, usart_buf, 1);//开启下一次串口中断
}
//串口接收清除
void RxIdle_Process(void)
{
u8 i;
if(uwTick - USARTTick < 50) return ; //50ms到?
USARTTick = uwTick;
rx_cnt = 0;//清除数组内容
rx_flag_lcd = 0;
memset(rx_buf, '\0', sizeof(rx_buf));//数组清除函数,需要包含头文件"string.h"头文件
}
1.STM32CUBEMX:
开启TIM的GPIO的定时器功能;
通道模式配制成输入捕获模式(Input Capture direct mode);打开NVIC;
配置预分频值PSC为80,每1us自增1;根据需求配置ARR的值;
配置ARR为0xFFFF(65535);对应测量取值范围为15H-1MHZ;
配置ARR为0xFFFF_FFFF(2^32);对应测量取值范围为0.00023H-1MHZ;
2.将tim.c和tim.h的代码移植到工程文档;
3.开始定时器PWM捕获中断HAL_TIM_IC_Start_IT(&htimx,TIM_CHANNEL_y);
4.编写回调函数;
5.在回调函数当中设置断点,能不能进入中断当中
6.编写PWM回调函数,再次调用HAL_TIM_IC_Start_IT(&htimx,TIM_CHANNEL_y);
7.调用函数:
(1)__HAL_TIM_SetCounter:设置计数器的值;一般用来清零;
(2)__HAL_TIM_GetCounter:获取计数器的值;获取占空比和周期;
(3)TIM2->CCER:修改上升或者下降沿触发中断;
最后在回调函数中进行数据处理得到测量信号的频率和占空比;
1.回调函数中获取测量信号的频率和占空比;
u32 pwm_tim3_cnt1;
u32 pwm_tim3_cnt2;
u8 pwm_tim3_state;
u32 pwm_tim3_freq;
float pwm_tim3_duty;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//如果是多个通道输入捕获,则进行if判断
if(htim == &htim3)
{
if(pwm_tim3_state == 0)
{
__HAL_TIM_SetCounter(&htim3, 0); //开始计时
TIM3->CCER |= 0x0002; //转为下降沿触发中断
pwm_tim3_state = 1;
}
else if(pwm_tim3_state == 1)
{
pwm_tim3_cnt1 = __HAL_TIM_GetCounter(&htim3); //获取占空比时间
TIM3->CCER &= ~0x0002; //转为上升沿触发中断
pwm_tim3_state = 2;
}
else if(pwm_tim3_state == 2)
{
pwm_tim3_cnt2 = __HAL_TIM_GetCounter(&htim3); //获取周期时间
pwm_tim3_freq = 1000000 / pwm_tim3_cnt2;
pwm_tim3_duty = pwm_tim3_cnt1 * 100.0f / pwm_tim3_cnt2;
pwm_tim3_state = 0;
}
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
}
}
CCER进行修改的值根据不同的定时器通道的值也不同,根据参考手册查看
1.STM32CUBEMX:
开启TIM的GPIO的定时器功能;
通道模式为PWM产生通道(PWM Generation CH1);
配置预分频值PSC为80,每1us自增1;
根据需求配置ARR的值和Pluse的值;
PWM模式选mode1;极性选择高;
以上都根据需要而定,可以组合成不同频率和占空比的信号;
2..将tim.c和tim.h的代码移植到工程文档;
3..调用函数:
(1)HAL_TIM_PWM_Start(&htimx, TIM_CHANNEL_y):启动指定定时器通道生成PWM;
(2)TIM16->ARR = 499:修改输出信号频率;
(3)TIM17->CCRx = 500:修改指定通道输出信号的占空比;注意写上通道值;
无
//KB4长按
if(Cont & 0x08) //按键长按
{
kb4_cnt++; //长按计时
}
if((Trg == 0x00) && (Cont == 0x00)) //无按键按下
{
if(kb4_cnt >= 65) //2s到?
{
kb4_cnt = 0; //清零
do somethings...
}
else if (kb4_cnt != 0) //2s未到
{
kb4_cnt = 0; //清零,防止累加
}
}
//按键程序每31ms执行一次;所以200/31=65;当长按计次到65时代表大约2s到了
1.工程需包含#include “stdio.h”和 #include “string.h” ,#include “lcd.h” ;方便之后编写代码;
2.STM32CUBEMX新生成的工程,下载设置中添加128k的芯片类型;
3.串口接收到的数据为ASCII码,转换为普通的数据需要进行 -‘\0’处理;
4.结构体定义的方法
typedef struct
{
u8 tyep[5];
u8 code[5];
u8 in_year;
u8 in_month;
u8 in_day;
u8 in_hour;
u8 in_minute;
u8 in_second;
u8 pos; //停车位置 1-8
} TYPE_CAR_INFO;
TYPE_CAR_INFO car_infomation[8];
文章浏览阅读388次。数据库的url后面添加&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC例如,完整代码为:url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&useJDBCCompliant..._gradle :
文章浏览阅读3.9k次,点赞20次,收藏86次。显卡的工作是负责画面的渲染和输出,例如你在玩一个大型游戏,CPU的工作是根据游戏预设的各种算法计算出接下来会发生什么,并折合成海量的数据发送给显卡,显卡再对这些数据进行计算,渲染成1帧1帧的图像,传输到显示器,从而将画面呈现在我们眼前,而且显卡是在实时计算渲染,所以对显卡性能的要求就非常高,因此这就是我们平常所说的你想打游戏,就得有一张好的显卡。而看视频就不一样了,视频资源是已经被计算好的数据,先看只负责简单处理再输出就可以了,不需要再自己计算。就类比写作业,玩游戏就是自己计算然后写在本子上,看视频就是抄作_显卡分类
文章浏览阅读1.7k次,点赞4次,收藏25次。背景ICP算法是点云配准(registration)领域的主流算法,在学习过程中我尝试使用C++复现了ICP算法。我参考的是经典ICP论"P. Besl, N. McKay. ‘A Method for Registration of 3-D Shapes,’ IEEE Trans. on Pattern Analysis and Machine Intel., vol. 14, no. 2, pp. 239-256,1992"。建议论文和代码搭配阅读。环境windows10 + pcl1.11.1_method for registration of 3-d shapes
文章浏览阅读3.7k次。web.xml配置如下: springm_spring 过滤js代码
文章浏览阅读8.5k次,点赞2次,收藏17次。1、任务分类 游戏中任务可以分为主线任务、支线任务、日常任务、周任务、节日活动任务,其中周任务和节日任务可以根据设定的日期进行开启关闭,属于重复性任务。2、游戏条件 各类任务开启条件:1、玩家等级 2、玩家攻击力3、前置任务等等 完成条件:玩家进行任务进度是否达成 任务关闭:1、玩家领取奖励后关闭任务。2、时间过期3..._游戏任务种类
文章浏览阅读6.4k次,点赞8次,收藏35次。1. codeforces codeforces(这个网站每天会有比赛,一起打CF吧!)http://codeforces.com/problemset2. topcoder:http://www.topcoder.com/challenges/3. kattis 这个网站有历年的ICPC真题 https://open.kattis.com/4. 洛谷:..._清华大学 acm网站
文章浏览阅读7.9k次,点赞4次,收藏9次。1、like 2、contains 3、instr 4、regexp_like_oracle包含某个字段
文章浏览阅读1.1k次,点赞2次,收藏10次。paper:https://arxiv.org/pdf/2011.03972.pdfcode:https://github.com/sunsmarterjie/SDL-Skeleton摘要传统的物体骨架检测网络通常是手工制作的。 虽然有效,但它们需要密集的先验知识来为不同粒度的对象配置具有代表性的特征。 本文提出了自适应线性跨度网络(AdaLSN),在神经架构搜索(NAS)的驱动下,自动配置和集成目标骨架检测的尺度感知特征。AdaLSN是用线性跨度理论建立的,它为多尺度深度特征融合提供了最早的解释之一_线性跨度网络
文章浏览阅读662次。python的内存管理机制先从较浅的层面来说,Python的内存管理机制可以从三个方面来讲(1)垃圾回收(2)引用计数(3)内存池机制一、垃圾回收:python不像C++,Java等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对Python语言来讲,对象的类型和内存都是在运行时确定的。这也是为什么我们称Python语言为动态类型的原因(这里我们把动态类型可以简单的归结为对变量内存地址_python 创建金字塔 显示大内存图片
文章浏览阅读547次。试用QT中的多语言翻译基本原理:为了在使用多语言,必须做两件事情:1,把想要翻译的字符串放入tr()中,如 QString str=tr("Legal");2, 在开始的时候载入预先制作好的翻译文件(.qm)。以下利用Qt提供的工具,把翻译文件制作出来,并在程_qt translate file语言选什么
文章浏览阅读433次。Atitit 性能指标与性能提升的5个原则与性能提升模型 1. 性能的几个指标主要是响应时间(Response time) 2.吞吐量(Throughput)12. 性能提升的5个原则与性能提升模型 22.1. 分类优先级,与分区域,减少要操作或提取的内容与范围 22.2. div分而治之 聚沙成塔 分布式并发 22.3. 大力提升单体处理能力 22.4. _升级性能指标
文章浏览阅读5.3k次。12月17日,华为终端官方微博公布了新品信息,预告新款华为智慧屏将于12月21日下午14时的华为全屋智能及智慧屏新品发布会上正式亮相。华为智慧屏宣传海报文案透露出此次新款华为智慧屏将在语音及智能交互上带来惊喜体验。且在此之前,华为消费者业务IoT产品线总裁支浩曾透露发布于年底的全新华为智慧屏系列将定位大众娱乐。另外,随着鸿蒙系统2.0的升级,华为智慧屏系列也正式成为首批搭载鸿蒙2.0的终端产品。根..._华为电视安装第三方软件