蓝桥杯嵌入式学习笔记

news2025/5/10 13:51:55

用博客来记录一下参加蓝桥杯嵌入式第十六届省赛的学习经历

  • 工具
    • 环境准备
      • cubemx配置
        • 外部高速时钟使能
        • 设置串口
        • 时钟配置
        • 项目配置
      • keil配置
        • 烧录方式
        • 注意
        • 代码规范
        • 头文件配置
  • 模块
    • led
      • cubemx配置
      • keil代码
        • 实现点亮一只灯
        • 实现具体操作的灯,以及点亮还是熄灭
    • 按键
      • cubemx配置
      • keil代码
    • LCD
      • keil代码
      • 引脚冲突问题
    • LED闪烁
      • cubemx配置
      • keil代码
    • 按键长按
      • cubemx配置
      • keil代码
    • LCD高亮显示
      • keil代码
    • PWM输出
      • 计算公式
      • cubemx配置
      • keil代码
    • 输入捕获测量引脚输出PWM波周期
      • 原理图
      • 要求:用PA7来测量PA1生成PWM波的频率
      • cubemx配置
      • keil代码
    • 输入捕获测量555定时器频率
      • 原理图
      • cubemx配置
      • keil代码
    • ADC测量
      • 原理图
      • cubemx配置
      • keil代码
    • 串口发送和接收
      • 原理图
      • cubemx配置
      • keil代码
    • 利用定时器进行串口不定长数据接收
      • 原理
      • cubemx配置
      • keil代码
    • EEPROM读写
      • 原理
      • keil代码

工具

keil5,stm32cube

环境准备

根据赛事方提供的板子来选择cubemx上的器件
。。。

cubemx配置

外部高速时钟使能

外部高速时钟使能

设置串口

在这里插入图片描述

时钟配置

时钟配置为24MHZ因为外部晶振为24MHZ,其余的暂时不清楚原因
在这里插入图片描述

项目配置

配置为MDK
需要注意,固件地址需要设置为下载解压后的文件地址(提前从数据包中下载)
在这里插入图片描述
勾选生成.c.h文件
在这里插入图片描述
注意一下,下载好软件后,要解压固件包,然后在help中设置地址
在这里插入图片描述

keil配置

烧录方式

在这里插入图片描述
烧录设置
在这里插入图片描述
要是没有芯片包,需要从赛事数据包中解压,会自动生成
在这里插入图片描述
keil代码自动补全
点击edit,configraution选择跟编译相关的Text Completion,然后勾线第三个
在这里插入图片描述

注意

代码需要写入begin - end之间

代码规范

新建code文件夹
右键CMSIS,点击Manage Project,新建一个文件夹
在这里插入图片描述
点击魔术棒,c++,include path,添加文件夹路径
在这里插入图片描述
然后添加fun.c,fun.h,headfile.h 三个文件
头文件可能显示不出来,所以需要添加existing file 再勾选all file,即可添加

头文件配置

headfile.h
在这里插入图片描述
fun.h
在这里插入图片描述

fun.c
在这里插入图片描述
main.c
在这里插入图片描述

模块

led

led引脚分配图
通过观察开发板led电路图可以发现,led的的引脚控制由PC8~15低电平点亮,同时PD2锁存器控制开关,需要置高电平

cubemx配置

将PC8~15设置为输出,PD2设置为输出
在GPIO配置中,将引脚全部配置为高电平,使得上电时LED灭
在这里插入图片描述

keil代码

实现点亮一只灯

fun.c主要代码

void led_show()
{
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8, GPIO_PIN_RESET);
	
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);


}

fun.h代码

void led_show(void);

main.c代码(需要在主函数内)

led_show();
实现具体操作的灯,以及点亮还是熄灭

fun.c代码

void led_show(uint8_t led, uint8_t mode)
{
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	if(mode)
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8 << (led - 1), GPIO_PIN_RESET); //GPIO_PIN_8的二进制代码为0000 0001 0000 0000 GPIO_PIN_9的二进制代码为0000 0010 0000 0000,所以用左移移位符
	else
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8 << (led - 1), GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);


}

fun.h代码(需要加上那个头函数)

#include "stm32g4xx.h"                  // Device header


void led_show(uint8_t led, uint8_t mode);

main.c代码(灯1, 4,8亮)

		led_show(1, 1);
		led_show(4, 1);
		led_show(8, 1);

按键

原理图,如图,需要将PB0~2,PA0配置为上拉输入模式。按下为低电平,松开为高电平
在这里插入图片描述

cubemx配置

,PA0

keil代码

fun.c代码

void key_scan()
{
	B1_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
	B2_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
	B3_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
	B4_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
	if(B1_state == 0 && B1_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(1, 1);
	}
	if(B2_state == 0 && B2_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(1, 0);
	}
	if(B3_state == 0 && B3_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(2, 1);
	}
	if(B4_state == 0 && B4_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(2, 0);
	}
	B1_last_state	= B1_state;
	B2_last_state	= B2_state;
	B3_last_state	= B3_state;
	B4_last_state	= B4_state;


}

fun.h代码

void key_scan(void);//里面没有这个void会有waring,但影响似乎不大

main.c代码
同样放入while循环中
在这里插入图片描述

LCD

首先在赛事方提供的数据包中找到液晶驱动参考程序,HAL库版本,找到lcd.c,font.h,lcd,h,复制到code文件夹,打开keil工程文件,添加这三个文件
在这里插入图片描述
初始准备
main.c函数
在这里插入图片描述
headfile.h函数
在这里插入图片描述

keil代码

实现了在第一行显示字符,按键按下时,第三行数字变化的功能
fun.c代码

#include "headfile.h"

int count = 0;

void led_show(uint8_t led, uint8_t mode)
{
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
	if(mode)
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8 << (led - 1), GPIO_PIN_RESET);
	else
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_8 << (led - 1), GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);


}



uint8_t B1_state;
uint8_t B1_last_state;
uint8_t B2_state;
uint8_t B2_last_state;
uint8_t B3_state;
uint8_t B3_last_state;
uint8_t B4_state;
uint8_t B4_last_state;


void key_scan()
{
	B1_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
	B2_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
	B3_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
	B4_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
	if(B1_state == 0 && B1_last_state == 1)//°´¼üB1°´ÏÂ
	{
		count ++;
	}
	if(B2_state == 0 && B2_last_state == 1)//°´¼üB1°´ÏÂ
	{
		count --;
	}
	if(B3_state == 0 && B3_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(2, 1);
	}
	if(B4_state == 0 && B4_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(2, 0);
	}
	B1_last_state	= B1_state;
	B2_last_state	= B2_state;
	B3_last_state	= B3_state;
	B4_last_state	= B4_state;


}

char text[20]; //linemax = 20

void lcd_show()
{
	sprintf(text, "zlfsgdsg");//¿ÉÒÔµ÷Õû×Ö·û´®Î»Ö㬵«ÊÇ×î¶à20¸ö×Ö·û
	LCD_DisplayStringLine(Line0, (uint8_t *)text);
	sprintf(text, "     count: %d     ", count);
	LCD_DisplayStringLine(Line3, (uint8_t *)text);

}

fun.h代码

#ifndef _fun_h
#define _fun_h

#include "stm32g4xx.h"                  // Device header


void led_show(uint8_t led, uint8_t mode);

void key_scan(void);

void lcd_show(void);

#endif


main.c代码

在这里插入图片描述

引脚冲突问题

lcd和led的引脚有冲突
在这里插入图片描述
解决办法:将PD2引脚提前置低电平,这样数据就不能传入led,led就不会点亮了,但是下次点亮led时依旧会出现这样的问题,所以需要将所有用到的LCD.C函数加上

uint16_t temp = GPIOC->ODR;
GPIOC->ODR = temp;

解决方法如下
main.c (在初始化部分)

	HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);

lcd.c (部分代码)
在这里插入图片描述
在这里插入图片描述
问题成功解决!

LED闪烁

两个重要参数:PSC,ARR
其中系统频率f为80MHZ,如果要让T=1,则可令ARR = 9999,PSC = 7999;即可确定定时周期为1
在这里插入图片描述
初始化
在headfile.h文件中添加tim.h文件头
在这里插入图片描述
main.c
在这里插入图片描述

cubemx配置

需要配置定时器,正常使用通用定时器TIM2即可,重点是修改PSC和ARR
在这里插入图片描述
在这里插入图片描述
生成代码后可发现多了tim.c函数,找到stm32g4xx_hal_tim.h文件里面的call_back回调函数
在这里插入图片描述
将其复制到fun.c中

keil代码

在fun.c中定义一个变量

uint8_t led_mode;

在这里插入图片描述

按键长按

原理,用一个计数器cnt(取10000)计时1s,20000对应2s,5000对应0.5s来计时,达到识别按键长按的目的

cubemx配置

此时TIM3不使能!!
在这里插入图片描述

keil代码

fun.c代码

uint8_t B1_state;
uint8_t B1_last_state;
uint8_t B2_state;
uint8_t B2_last_state;
uint8_t B3_state;
uint8_t B3_last_state;
uint8_t B4_state;
uint8_t B4_last_state;


void key_scan()
{
	B1_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
	B2_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
	B3_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
	B4_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
	if(B1_state == 0 && B1_last_state == 1)//°´¼üB1°´ÏÂ
	{
		TIM3->CNT = 0;
	}
	else if(B1_state == 0 && B1_last_state == 0) //°´¼üB1Ò»Ö±°´ÏÂ
	{
		if(TIM3->CNT >= 10000)//°´¼üB1³¤°´
		{
				count++;
		}
	}
	else if	(B1_state == 1 && B1_last_state == 0)//°´¼üB1ËÉ¿ª
	{
		if(TIM3->CNT < 10000)//°´¼üB1¶Ì°´
		{
			count += 2;
		}
	}
	if(B2_state == 0 && B2_last_state == 1)//°´¼üB1°´ÏÂ
	{
		count --;
	}
	if(B3_state == 0 && B3_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(2, 1);
	}
	if(B4_state == 0 && B4_last_state == 1)//°´¼üB1°´ÏÂ
	{
		led_show(2, 0);
	}
	B1_last_state	= B1_state;
	B2_last_state	= B2_state;
	B3_last_state	= B3_state;
	B4_last_state	= B4_state;


}

main.c代码
需要注意!给TIM3使能,但因为不是中断,是定时器,所以不用IT
在这里插入图片描述

LCD高亮显示

其实就是调用 LCD_SetBackColor(Color);这个函数,把背景颜色改成黄色

keil代码

定义变量

uint8_t lcd_highshow;

按键b1按下时

			lcd_highshow ++;
			lcd_highshow %= 3;	

逻辑流程
应该蛮简单的,就不写注释了

void lcd_show()
{
	sprintf(text, "zlfsgdsg");//¿ÉÒÔµ÷Õû×Ö·û´®Î»Ö㬵«ÊÇ×î¶à20¸ö×Ö·û
	LCD_DisplayStringLine(Line0, (uint8_t *)text);
	if(lcd_highshow == 0)
	{
	LCD_SetBackColor(Yellow);
	sprintf(text, "     count: %d     ", count);
	LCD_DisplayStringLine(Line3, (uint8_t *)text);
	LCD_SetBackColor(Black);
	sprintf(text, "     txysgdmn     ");
	LCD_DisplayStringLine(Line4, (uint8_t *)text);
	
	sprintf(text, "     baozi     ");
	LCD_DisplayStringLine(Line5, (uint8_t *)text); 
	}
	else if(lcd_highshow == 1)
	{

	sprintf(text, "     count: %d     ", count);
	LCD_DisplayStringLine(Line3, (uint8_t *)text);
	LCD_SetBackColor(Yellow);
	sprintf(text, "     txysgdmn     ");
	LCD_DisplayStringLine(Line4, (uint8_t *)text);
	LCD_SetBackColor(Black);
	sprintf(text, "     baozi     ");
	LCD_DisplayStringLine(Line5, (uint8_t *)text); 
	
	}

PWM输出

一般要求:让PA1输出频率为1000HZ,占空比为50%的方波

计算公式

在这里插入图片描述

cubemx配置

在这里插入图片描述

keil代码

在生成代码后,发现一个问题:文件中没有头文件!
后面找到解决办法, 编译之后,就能知道每个文件依赖于哪些头文件!还是第一次遇到这种问题
先在main.c函数中将定时器使能

这个函数在头文件里找–stm32g4xx_hal_tim.h

	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);

设置占空比

	TIM2->CCR2 = 50;

原理图(抄袭版),不懂的话看嵌入式的书去
在这里插入图片描述

输入捕获测量引脚输出PWM波周期

原理图

在这里插入图片描述

要求:用PA7来测量PA1生成PWM波的频率

需要用杜邦线连接PA7和PA1.

cubemx配置

这里PA7用TIM17或者TIM2都行,我就根据视频来了
在这里插入图片描述
使能定时器
在这里插入图片描述

keil代码

为了省事,将code文件复制到这个工程目录下,记得在main.c文件中添加头文件定义
在这里插入图片描述
main.c函数
使能中断17

	HAL_TIM_IC_Start_IT(&htim17, TIM_CHANNEL_1);

fun.c代码
参考着原理图一起理解

#include "headfile.h"

char	text[20]; 

uint32_t fre, capture_value;

void lcd_show()
{
	sprintf(text, "zlfsgdsg");//¿ÉÒÔµ÷Õû×Ö·û´®Î»Ö㬵«ÊÇ×î¶à20¸ö×Ö·û
	LCD_DisplayStringLine(Line0, (uint8_t *)text);
	sprintf(text, "   fre:%d   ", fre);
	LCD_DisplayStringLine(Line3, (uint8_t *)text);
	}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM17)
	{
		capture_value = TIM17->CCR1;
		TIM17->CNT = 0;
		fre = 80000000/(80*capture_value);
	
	}


}

main.c代码
注意不要忘记了给LCD初始化

  /* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Black);
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	
	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
	TIM2->CCR2 = 50;
	
	HAL_TIM_IC_Start_IT(&htim17, TIM_CHANNEL_1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		lcd_show();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

输入捕获测量555定时器频率

实现效果:能测量出两个信号发生器输出的PWM波的频率,两个频率可以通过R39,和R40调节

原理图

在这里插入图片描述

cubemx配置

将PA15设置为TIM2_CH1,PB4设置为TIM16_CH1;
分别将两个定时器设置为输入捕获模式,PCC设置为80-1,并且使能
在这里插入图片描述
在这里插入图片描述

keil代码

main.c
需要注意,不要忘记了LCD初始化,不要忘记了中断使能,不要忘记了把lcd_show函数放到循环里

  /* USER CODE BEGIN 2 */
	LCD_Init();
	LCD_Clear(Black);
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	
	
	
	
	HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
	HAL_TIM_IC_Start_IT(&htim16, TIM_CHANNEL_1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		lcd_show();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

&&& 不要忘记了添加头文件!

#include "headfile.h"

fun.c函数
具体逻辑,我现在看是蛮简单的,不管了,吃饭去了,自己悟去吧

#include "headfile.h"

char	text[20]; 

uint32_t fre1, capture_value1, fre2, capture_value2 ;

void lcd_show()
{
	sprintf(text, "zlfsgdsg");//¿ÉÒÔµ÷Õû×Ö·û´®Î»Ö㬵«ÊÇ×î¶à20¸ö×Ö·û
	LCD_DisplayStringLine(Line0, (uint8_t *)text);
	sprintf(text, "   R39_fre1:%d   ", fre1);
	LCD_DisplayStringLine(Line2, (uint8_t *)text);
	sprintf(text, "   R40_fre2:%d   ", fre2);
	LCD_DisplayStringLine(Line4, (uint8_t *)text);
	}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM16)// R39
	{
		capture_value1 = TIM16->CCR1;
		TIM16->CNT = 0;
		fre1 = 80000000/(80*capture_value1);
	
	}
	
		if(htim->Instance == TIM2)//R40
	{
		capture_value2 = TIM2->CCR1;
		TIM2->CNT = 0;
		fre2 = 80000000/(80*capture_value2);
	
	}


}

ADC测量

原理图

通过R37和R38两个旋钮,测量AD的电压值
在这里插入图片描述

cubemx配置

分别对引脚PB15和PB12进行配置。
在这里插入图片描述

在这里插入图片描述

keil代码

记得在headfile.h文件中添加ADC头文件
	在这里插入图片描述
fun.c代码

#include "headfile.h"




char text[20];	
void lcdshow()
{
	sprintf(text, "      zlfsgdsg      ");
	LCD_DisplayStringLine(Line0, (uint8_t *)text);
	
	HAL_ADC_Start(&hadc1); 
	uint32_t	adc_value =  HAL_ADC_GetValue(&hadc1); //R38
	
	sprintf(text, "     value:%d      ", adc_value);
	LCD_DisplayStringLine(Line3, (uint8_t *)text);

}

main.c
记得将LCD初始化,然后在主函数中调用lcdshow();

在这里插入图片描述
即可在lcd中实时读取adc1的值

keil代码2
若是要读取两个ad的电压值
fun.c代码

#include "headfile.h"




char text[20];	
void lcdshow()
{
	sprintf(text, "      zlfsgdsg      ");
	LCD_DisplayStringLine(Line0, (uint8_t *)text);
	
	
	sprintf(text, "     R37_volt:%.2f      ", get_vol(&hadc2));
	LCD_DisplayStringLine(Line3, (uint8_t *)text);
	
	sprintf(text, "     R38_volt:%.2f      ", get_vol(&hadc1));
	LCD_DisplayStringLine(Line5, (uint8_t *)text);

}




double get_vol(ADC_HandleTypeDef *hadc)
{
	
	HAL_ADC_Start(hadc); 
	uint32_t	adc_value =  HAL_ADC_GetValue(hadc); //R38
	return	3.3*adc_value/4096; //因为AD是12位,所以ad最大值是4096

}

	

fun.h

void lcdshow(void);

double get_vol(ADC_HandleTypeDef *hadc);

串口发送和接收

原理图

在这里插入图片描述

cubemx配置

在这里插入图片描述
在这里插入图片描述

keil代码

串口发送
main.c代码
记得在前面添加头文件

  while (1)
  {
		char text01[20];
		sprintf(text01, "zlfsgdgs\r\n");
		HAL_UART_Transmit(&huart1, (uint8_t *)text01, sizeof(text01), 50);
		HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

串口接收
fun.c代码

#include "headfile.h"

uint8_t rec_data;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		
		HAL_UART_Transmit(huart, &rec_data, 1, 50);
		
		HAL_UART_Receive_IT(huart, &rec_data, 1);
		
	}


}

fun.h代码
加一个外部变量定义

extern uint8_t rec_data;

main.c代码
在这里插入图片描述

利用定时器进行串口不定长数据接收

原理

在这里插入图片描述

cubemx配置

主要是把波特率设置为9600
在这里插入图片描述
在这里插入图片描述

keil代码

main.c
把定时器使能!!!&&调用函数
在这里插入图片描述
fun.c函数

#include "headfile.h"

char	send_buff[20];
uint8_t rec_data, count;
uint8_t	rec_flag;
uint8_t	rec_buff[20];


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		
		//HAL_UART_Transmit(huart, &rec_data, 1, 50);
		TIM4->CNT = 0;
		rec_flag = 1;
		rec_buff[count]	= rec_data;
		count ++;
		HAL_UART_Receive_IT(huart, &rec_data, 1);
		
	}

}

void	uart_data_rec()
{
		if(rec_data)
		{
			if(TIM4->CNT > 15)
			{
				if(rec_buff[0] == 'z'	&& rec_buff[1] == 'l' && rec_buff[2] == 'f')
				{
					sprintf(send_buff, "zlf\r\n");
					HAL_UART_Transmit(&huart1, (uint8_t *)send_buff, sizeof(send_buff), 50);
				}
				else if(rec_buff[3] == 's'	&& rec_buff[4] == 'g' && rec_buff[5] == 'd')
				{
					sprintf(send_buff, "sgd\r\n");
					HAL_UART_Transmit(&huart1, (uint8_t *)send_buff, sizeof(send_buff), 50);
				}
				else if(rec_buff[6] == 's'	&& rec_buff[7] == 'g')
				{
					sprintf(send_buff, "sgd\r\n");
					HAL_UART_Transmit(&huart1, (uint8_t *)send_buff, sizeof(send_buff), 50);
				}
				else
				{
					sprintf(send_buff, "error\r\n");
					HAL_UART_Transmit(&huart1, (uint8_t *)send_buff, sizeof(send_buff), 50);
				}
				rec_flag = 0;
				for(int	i=0; i<count; i++)
					rec_buff[i] = 0;
					count	= 0;
			}
		
		}

}
	

fun.h
在这里插入图片描述
headfile.h
要添加usart.h的头文件
在这里插入图片描述

EEPROM读写

原理

原理就不说了,学嵌入式I2C的时候学了挺久

keil代码

i2c_hal_c

void eeprom_write(uint8_t addr, uint8_t data)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CSendByte(data);
	I2CWaitAck();
	I2CStop();
	
	HAL_Delay(20);
	
}

uint8_t eeprom_read(uint8_t addr)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	I2CStop();
	
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	uint8_t data = I2CReceiveByte();
	I2CSendNotAck();
	I2CStop();
	
	return	data;
}

i2c_hal_h代码
主要就是添加两个文件

void eeprom_write(uint8_t addr, uint8_t data);
uint8_t eeprom_read(uint8_t addr);

main.c代码
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2324737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Blender多摄像机怎么指定相机渲染图像

如题目所说&#xff0c;当blender的场景里面有摄像机的时候&#xff0c;按F12可以预览渲染结果&#xff0c;但是当有多个摄像机的时候就不知道使用哪个进行渲染了。 之前在网上没有找到方法&#xff0c;就用笨方法&#xff0c;把所有的摄像机删除&#xff0c;然后设置自己需要…

从 MySQL 到时序数据库 TDengine:Zendure 如何实现高效储能数据管理?

小T导读&#xff1a;TDengine 助力广州疆海科技有限公司高效完成储能业务的数据分析任务&#xff0c;轻松应对海量功率、电能及输入输出数据的实时统计与分析&#xff0c;并以接近 1 : 20 的数据文件压缩率大幅降低存储成本。此外&#xff0c;taosX 强大的 transform 功能帮助用…

观察者模式:解耦对象间的依赖关系

观察者模式&#xff1a;解耦对象间的依赖关系 JDK 中曾直接提供对观察者模式的支持&#xff0c;但因其设计局限性&#xff0c;现已被标记为“过时”&#xff08;Deprecated&#xff09;。不过&#xff0c;观察者模式的思想在 JDK 的事件处理、spring框架等仍有广泛应用。下面我…

windows第二十章 单文档应用程序

文章目录 单文档定义新建一个单文档应用程序单文档应用程序组成&#xff1a;APP应用程序类框架类&#xff08;窗口类&#xff09;视图类&#xff08;窗口类&#xff0c;属于框架的子窗口&#xff09;文档类&#xff08;对数据进行保存读取操作&#xff09; 直接用向导创建单文档…

通信协议之串口

文章目录 简介电平标准串口参数及时序USART与UART过程引脚配置 简介 点对点&#xff0c;只能两设备通信只需单向的数据传输时&#xff0c;可以只接一根通信线当电平标准不一致时&#xff0c;需要加电平转换芯片&#xff08;一般从控制器出来的是信号是TTL电平&#xff09;地位…

Java入门知识总结——章节(二)

ps&#xff1a;本章主要讲数组、二维数组、变量 一、数组 数组是一个数据容器&#xff0c;可用来存储一批同类型的数据 &#x1f511;&#xff1a;注意 类也可以是一个类的数组 public class Main {public static class Student {String name;int age; // 移除 unsignedint…

Verilog 中寄存器类型(reg)与线网类型(wire)的区别

目录 一、前言 二、基本概念与分类 1.寄存器类型 2.线网类型 三、六大核心区别对比 四、使用场景深度解析 1.寄存器类型的典型应用 2. 线网类型的典型应用 五、常见误区与注意事项 1. 寄存器≠物理寄存器 2.未初始化值陷阱 3.SystemVerilog的改进 六、总结 …

【Linux加餐-验证UDP:TCP】-windows作为client访问Linux

一、验证UDP-windows作为client访问Linux UDP client样例代码 #include <iostream> #include <cstdio> #include <thread> #include <string> #include <cstdlib> #include <WinSock2.h> #include <Windows.h>#pragma warning(dis…

Rust vs. Go: 性能测试(2025)

本内容是对知名性能评测博主 Anton Putra Rust vs. Go (Golang): Performance 2025 内容的翻译与整理, 有适当删减, 相关数据和结论以原作结论为准。 再次对比 Rust 和 Go&#xff0c;但这次我们使用的是最具性能优势的 HTTP 服务器库---Hyper&#xff0c;它基于 Tokio 异步运…

JDBC的详细使用

1. JDBC概述 JDBC[Java Database Connectivity]是 Java 语言中用于连接和操作数据库的一套标准 API。它允许 Java 程序通过统一的方式与各种关系型数据库&#xff0c;如 MySQL、Oracle、SQL Server 等交互&#xff0c;执行 SQL 语句并处理结果。 1.1 JDBC原理 JDBC的核心原理…

瑞芯微 RKrga接口 wrapbuffer_virtualaddr 使用笔记

一、源码 官方在librga中给了很多 demo 以供参考&#xff0c;例如 imresize 操作&#xff1a; /** Copyright (C) 2022 Rockchip Electronics Co., Ltd.* Authors:* YuQiaowei <cerf.yurock-chips.com>** Licensed under the Apache License, Version 2.0 (the &qu…

【数据结构】[特殊字符] 并查集优化全解:从链式退化到近O(1)的性能飞跃 | 路径压缩与合并策略深度实战

并查集的优化 导读一、合并优化1.1 基本原理1.2 按大小合并1.3 按秩合并1.4 两种合并的区别**1.4.1 核心目标****1.4.2 数据存储****1.4.3 合并逻辑****1.4.4 树高控制****1.4.5 适用场景****1.4.6 路径压缩兼容性****1.4.7 极端案例对比****1.4.8 小结**二、查找优化2.1 路径压…

如何在 AI 搜索引擎(GEO)霸屏曝光,快速提升知名度?

虽然大多数人仍然使用 Google 来寻找答案&#xff0c;但正在发生快速转变。ChatGPT、Copilot、Perplexity 和 DeepSeek 等 LLM 已成为主流。这主要是因为每个都有自己的免费和公共版本&#xff0c;并且总是有重大的质量改进。 许多人每天都使用这些工具来提问和搜索互联网&…

VLAN综合实验二

一.实验拓扑&#xff1a; 二.实验需求&#xff1a; 1.内网Ip地址使用172.16.0.0/分配 2.sw1和SW2之间互为备份 3.VRRP/STP/VLAN/Eth-trunk均使用 4.所有Pc均通过DHCP获取IP地址 5.ISP只能配置IP地址 6.所有…

Kubernetes》k8s》Containerd 、ctr 、cri、crictl

containerd ctr crictl ctr 是 containerd 的一个客户端工具。 crictl 是 CRI 兼容的容器运行时命令行接口&#xff0c;可以使用它来检查和调试 k8s 节点上的容器运行时和应用程序。 ctr -v 输出的是 containerd 的版本&#xff0c; crictl -v 输出的是当前 k8s 的版本&#x…

SQL语句及其应用(中)(DQL语句之单表查询)

SQL语句的定义: 概述: 全称叫 Structured Query Language, 结构化查询语言, 主要是实现 用户(程序员) 和 数据库软件(例如: MySQL, Oracle)之间交互用的. 分类: DDL: 数据定义语言, 主要是操作 数据库, 数据表, 字段, 进行: 增删改查(CURD) 涉及到的关键字: create, drop, …

算法题(111):k与迷宫

审题&#xff1a; 本题需要我们寻找迷宫中的所有出口&#xff0c;若有出口需要输出距离最近的出口的距离&#xff0c;若没有就输出-1 时间复杂度&#xff1a;由于边距为1&#xff0c;我们本题采用bfs算法&#xff0c;在最坏的情况下我们需要遍历所有位置&#xff0c;时间复杂度…

Redis-常用命令

目录 1、Redis数据结构 2、命令简介 2.1、通用命令 DEL EXISTS EXPIRE 2.2、String命令 SET和GET MSET和MGET INCR和INCRBY和DECY SETNX SETEX 2.3、Key的层级结构 2.4、Hash命令 HSET和HGET HMSET和HMGET HGETALL HKEYS和HVALS HINCRBY HSETNX 2.5、List命…

从虚拟现实到可持续设计:唐婉歆的多维创新之旅

随着线上线下融合逐渐成为全球家居与建材行业的发展趋势,全球市场对高品质、个性化家居和建材产品的需求稳步攀升,也对设计师提出更高的要求。在这一背景下,设计师唐婉歆将以产品设计师的身份,正式加入跨国企业AmCan 美加集团,投身于备受行业瞩目的系列设计项目。她将负责Showr…

音视频 四 看书的笔记 MediaPlayerService

Binder机制看这里 Binde机智 这是一个分割符 Binder机智 分割(goutou) Binder机制 MediaPlayerService多媒体框架中一个非常重要的服务。MediaPlayerService 我原称之为链接之王 图片来源 MediaPlayer 是客户端 C/S 中的CMediaPlayerService MediaPlayerService::Client 是服…