🌍首页
📁归档
⏳时光机
🚩友链
📫留言
📧订阅
🌟推荐
📕考研课程
🏜️ 免费壁纸
❤ 捐助本站
💰资助名单
🎵音乐实验
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
20,146 阅读
2
论文写作中如何把word/wps里面所有数字和字母替换为新罗马字体
10,704 阅读
3
【高数】形心计算公式讲解大全
9,080 阅读
4
Vivado-FPGA Verilog烧写固化教程
8,072 阅读
5
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
7,854 阅读
🪶微语&随笔
励志美文
我的随笔
工作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
/
注册
18(共108篇)
找到
108
篇与
18
相关的结果
- 第 6 页
HAL_STM32_DS18B20开发
摘要本文介绍了如何使用HAL_STM32库和DS18B20温度传感器进行温度测量。文章详细说明了如何设置接口、初始化传感器、读取温度数据,并提供了相应的C语言代码示例。关键词:HAL_STM32, DS18B20, 温度传感器, 温度读取, 程序调用1.设置一个如PA0作为DS18B20模块接口 2.添加下面2个文件,注意程序已经集成us延时 ds18b20.c #include "ds18b20.h" static inline void delay_ms(uint32_t delay) { HAL_Delay(delay); } #define CPU_FREQUENCY_MHZ 72 // CPU主频,根据实际进行修改 static void delay_us(uint32_t delay) { int last, curr, val; int temp; while (delay != 0) { temp = delay > 900 ? 900 : delay; last = SysTick->VAL; curr = last - CPU_FREQUENCY_MHZ * temp; if (curr >= 0) { do { val = SysTick->VAL; } while ((val < last) && (val >= curr)); } else { curr += CPU_FREQUENCY_MHZ * 1000; do { val = SysTick->VAL; } while ((val <= last) || (val > curr)); } delay -= temp; } } //复位DS18B20 void DS18B20_Rst(void) { DS18B20_IO_OUT(); //SET PA0 OUTPUT DS18B20_DQ_OUT=0; //拉低DQ delay_us(750); //拉低750us DS18B20_DQ_OUT=1; //DQ=1 delay_us(15); //15US } //等待DS18B20的回应 //返回1:未检测到DS18B20的存在 //返回0:存在 uint8_t DS18B20_Check(void) { uint8_t retry=0; DS18B20_IO_IN();//SET PA0 INPUT while (DS18B20_DQ_IN&&retry<200) { retry++; delay_us(1); }; if(retry>=200)return 1; else retry=0; while (!DS18B20_DQ_IN&&retry<240) { retry++; delay_us(1); }; if(retry>=240)return 1; return 0; } //从DS18B20读取一个位 //返回值:1/0 uint8_t DS18B20_Read_Bit(void) // read one bit { uint8_t data; DS18B20_IO_OUT();//SET PA0 OUTPUT DS18B20_DQ_OUT=0; delay_us(2); DS18B20_DQ_OUT=1; DS18B20_IO_IN();//SET PA0 INPUT delay_us(12); if(DS18B20_DQ_IN)data=1; else data=0; delay_us(50); return data; } //从DS18B20读取一个字节 //返回值:读到的数据 uint8_t DS18B20_Read_Byte(void) // read one byte { uint8_t i,j,dat; dat=0; for (i=1;i<=8;i++) { j=DS18B20_Read_Bit(); dat=(j<<7)|(dat>>1); } return dat; } //写一个字节到DS18B20 //dat:要写入的字节 void DS18B20_Write_Byte(uint8_t dat) { uint8_t j; uint8_t testb; DS18B20_IO_OUT();//SET PA0 OUTPUT; for (j=1;j<=8;j++) { testb=dat&0x01; dat=dat>>1; if (testb) { DS18B20_DQ_OUT=0;// Write 1 delay_us(2); DS18B20_DQ_OUT=1; delay_us(60); } else { DS18B20_DQ_OUT=0;// Write 0 delay_us(60); DS18B20_DQ_OUT=1; delay_us(2); } } } //开始温度转换 void DS18B20_Start(void)// ds1820 start convert { DS18B20_Rst(); DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0x44);// convert } //初始化DS18B20的IO口 DQ 同时检测DS的存在 //返回1:不存在 //返回0:存在 uint8_t DS18B20_Init(void) { DS18B20_Rst(); return DS18B20_Check(); } //从ds18b20得到温度值 //精度:0.1C //返回值:温度值 (-550~1250) short DS18B20_Get_Temp(void) { uint8_t temp; uint8_t TL,TH; short tem; DS18B20_Start (); // ds1820 start convert DS18B20_Rst(); DS18B20_Check(); DS18B20_Write_Byte(0xcc);// skip rom DS18B20_Write_Byte(0xbe);// convert TL=DS18B20_Read_Byte(); // LSB TH=DS18B20_Read_Byte(); // MSB if(TH>7) { TH=~TH; TL=~TL; temp=0;//温度为负 }else temp=1;//温度为正 tem=TH; //获得高八位 tem<<=8; tem+=TL;//获得底八位 tem=(float)tem*0.625;//转换 if(temp)return tem; //返回温度值 else return -tem; } ds18b20.h #ifndef __DS18B20_H #define __DS18B20_H #include "main.h" #include "stm32f1xx_hal.h" //#include "delay.h" #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) #define GPIOA_ODR_Addr (GPIOA_BASE+12) #define GPIOB_ODR_Addr (GPIOB_BASE+12) #define GPIOC_ODR_Addr (GPIOC_BASE+12) #define GPIOD_ODR_Addr (GPIOD_BASE+12) #define GPIOE_ODR_Addr (GPIOE_BASE+12) #define GPIOF_ODR_Addr (GPIOF_BASE+12) #define GPIOG_ODR_Addr (GPIOG_BASE+12) #define GPIOA_IDR_Addr (GPIOA_BASE+8) #define GPIOB_IDR_Addr (GPIOB_BASE+8) #define GPIOC_IDR_Addr (GPIOC_BASE+8) #define GPIOD_IDR_Addr (GPIOD_BASE+8) #define GPIOE_IDR_Addr (GPIOE_BASE+8) #define GPIOF_IDR_Addr (GPIOF_BASE+8) #define GPIOG_IDR_Addr (GPIOG_BASE+8) #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //IO方向设置 #define DS18B20_IO_IN() {GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=8<<0;} #define DS18B20_IO_OUT() {GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=3<<0;} //IO操作函数 #define DS18B20_DQ_OUT PAout(0) //数据端口 PA0 #define DS18B20_DQ_IN PAin(0) //数据端口 PA0 uint8_t DS18B20_Init(void); //初始化DS18B20 short DS18B20_Get_Temp(void); //获取温度 void DS18B20_Start(void); //开始温度转换 void DS18B20_Write_Byte(uint8_t dat);//写入一个字节 uint8_t DS18B20_Read_Byte(void); //读出一个字节 uint8_t DS18B20_Read_Bit(void); //读出一个位 uint8_t DS18B20_Check(void); //检测是否存在DS18B20 void DS18B20_Rst(void); //复位DS18B20 #endif程序进行调用 使用温度=DS18B20_Get_Temp();进行调用数值,可见精度达到要求了 图片 图片
嵌入式&系统
刘航宇
4年前
4
1,576
12
2022-01-31
【10】基于STM32CubeMX-STM32ADC与OLED开发
摘要本文介绍了在XMF07A或XMF07C开发板上,使用STM32CubeMX和Keil5开发环境,实现ADC与OLED综合训练的过程。通过OLED显示图片、灯光检测、数据采集和显示等功能,展示了STM32CubeMX和OLED的协同工作。关键词:STM32CubeMX, STM32ADC, OLED, 开发板, 灯光检测目录 实训案例:ADC与OLED综合训练 实训案例:ADC与OLED综合训练 在XMF07A或XMF07C开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能: 【1】 上电开机后,首选在OLED上显示“强国图志”图片,然后让LED1与LED2依次点亮,然后熄灭,进行灯光检测。灯光检测结束后,OLED切换至数据显示界面,分3行: 第1行显示:“ sciarm.com ” 第2行显示:“采样值:” 第3行显示:“电压值:” 【2】在主程序中,采用查询的方式,每隔0.3秒对ADC_IN0通道的光敏传感器进行一次电压数据采集,并将采样到的12位数据换算成对应的实际电压值。LED1作为A/D采样指示灯,每采样一次闪烁一下。 【3】每进行完一次光敏传感器的数据采样和电压换算后,将其结果更新到OLED显示屏中相应的位置。如果光敏传感器的电压值小于1.3V,则将LED2灯点亮,反之,将LED2灯关闭。 图片 //首先需要进行OLED的底层驱动函数移植,生成相应的文字和图片数据 /* USER CODE BEGIN Includes */ //====引入OLED底层驱动的头文件======== #include "XMF_OLED_STM32Cube.h" #include "stdio.h" /* USER CODE END Includes */extern unsigned char BMP1[]; #define LED1_ON() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9,GPIO_PIN_SET) #define LED2_ON() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8,GPIO_PIN_SET) #define LED1_OFF() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9,GPIO_PIN_RESET) #define LED2_OFF() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8,GPIO_PIN_RESET) uint16_t ADC0_Value = 0; uint16_t ADC_Volt = 0; uint8_t str_buff[64];//LED灯流水点亮检测 void LED_Check() { LED1_ON(); HAL_Delay(500); LED2_ON(); HAL_Delay(500); LED1_OFF(); HAL_Delay(500); LED2_OFF(); HAL_Delay(500); }//显示开机LOGO图片 void OLED_display_pic() { OLED_Clear(); OLED_DrawBMP(0,0,128,8,BMP1); }//显示数据显示界面 void OLED_display_info() { OLED_Clear(); OLED_ShowString(6,0,(uint8_t *)"sciarm.com"); HAL_Delay(200); OLED_ShowCHinese(0,3,24); OLED_ShowCHinese(18,3,25); OLED_ShowCHinese(36,3,26); OLED_ShowString(54,3,(uint8_t *)":"); HAL_Delay(200); OLED_ShowCHinese(0,6,16); OLED_ShowCHinese(18,6,17); OLED_ShowCHinese(36,6,26); OLED_ShowString(54,6,(uint8_t *)":"); HAL_Delay(200); }//更新ADC采样数据与换算结果 void OLED_display_dat() { sprintf((char*)str_buff, "%4d", ADC0_Value); OLED_ShowString(64,3,(uint8_t *)str_buff); sprintf((char*)str_buff, "%d.%d%dV", ADC_Volt/100, (ADC_Volt%100/10), ADC_Volt%10); OLED_ShowString(64,6,(uint8_t *)str_buff); }//ADC采样过程与灯光自动控制 void Get_ADC0_Value() { HAL_ADC_Start(&hadc1); if(HAL_OK == HAL_ADC_PollForConversion(&hadc1, 30)) { ADC0_Value = HAL_ADC_GetValue(&hadc1); ADC_Volt = ADC0_Value * 330 / 4096; if(ADC_Volt < 130) { LED2_ON(); } else { LED2_OFF(); } } HAL_ADC_Stop(&hadc1); }/* USER CODE BEGIN 2 */ OLED_Init(); //初始化OLED OLED_display_pic(); //显示开机LOGO图片 LED_Check(); //灯光检测 OLED_display_info(); //显示数据界面 /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { LED1_ON(); //ADC采样指示灯点亮 Get_ADC0_Value(); //进行一次ADC采样及逻辑处理 OLED_display_dat(); //更新OLED中的采样数据 HAL_Delay(200); //延时0.2秒 LED1_OFF(); //ADC采样指示灯关闭 /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
嵌入式&系统
# 嵌入式
刘航宇
4年前
0
1,103
13
2022-01-29
【9】基于STM32CubeMX-STM32OLED开发
摘要本文介绍了基于STM32CubeMX-STM32OLED的开发过程,包括OLED概述、驱动函数库移植、常用函数介绍以及实训案例,展示了如何使用STM32控制OLED显示图片和信息。关键词:STM32CubeMX, OLED, STM32, 驱动程序, 显示开发目录 必备文件 关于OLED的概述 OLED开发相关资源下载 基于STM32CubeMX的OLED底层驱动函数移植 OLED驱动库中常用的函数 实训案例:STM32控制OLED显示 必备文件 必备文件 见文章末尾 关于OLED的概述 OLED:Organic Light-Emitting Display,有机发光显示。 OLED具备自发光、厚度薄、视角广、功耗低、对比度高、响应速度快、可用于挠曲性面板、使用温度范围广、构造及其制作过程较简单等优异特性,并认为是一种比液晶显示更为先进的新一代平板显示技术。以目前的技术,OLED的尺寸还难以大型化,但是分辨率却可以做得很高。 基于STM32的OLED应用,要做那些事情: 【1】移植OLED的底层驱动函数库。 【2】准备需要的中文字符和图片等数据。 【3】调用OLED驱动库中的底层函数进行应用开发。 图片 OLED开发相关资源下载 基于STM32CubeMX的OLED屏驱动程序库(内含4个文件) 【1】XMF_OLED_STM32Cube.c:驱动程序的源文件。 【2】XMF_OLED_STM32Cube.h:驱动程序的头文件。 【3】XMF_OLED_Font.h:字库数据文件。 【4】XMF_OLED_BMP.h:图片数据文件。 基于STM32CubeMX的OLED底层驱动函数移植 【1】将4个驱动文件拷贝到工程文件中,和main.c放在同一目录,并将XMF_OLED_STM32Cube.c添加到工程代码文件中,并在main.c中引入头文件XMF_OLED_STM32Cube.h。 【2】根据所选用的芯片型号,修改XMF_OLED_STM32Cube.h头文件中所以用的芯片头文件。 【3】根据硬件电路原理图中,修改XMF_OLED_STM32Cube.h中OLED的引脚定义。 【4】查看OLED_Initvoidvoidvoid初始化函数的源码,根据电路接口和应用需要进行修改。 图片 OLED驱动库中常用的函数 void OLED_Init(void); //OLED初始化函数 void OLED_Clear(void); //OLED清屏函数//显示英文字符串函数 void OLED_ShowString(unsigned char x,unsigned char y,unsigned char *p); 参数1:x,起点列坐标,0~127 参数2:y,起点行坐标,0~7 参数3:*p,字符串指针 返回值:void,无。//显示中文字符函数 void OLED_ShowCHinese(unsigned char x,unsigned char y,unsigned char no); 参数1:x,起点列坐标,0~127 参数2:y,起点行坐标,0~7 参数3:no,待显示中文字符在数组Hzk[][32]中的位置。 返回值:void,无。//显示图片函数 void OLED_DrawBMP( unsigned char x0, unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]); 参数1:x0,起点列坐标,0~127 参数2:y0,起点行坐标,0~7 参数3:x1,图片的列范围,1~128 参数4:y1,图片的行范围,1~8 参数5:BMP[],待显示图片数据的数组。 返回值:void,无。实训案例:STM32控制OLED显示 要点 图片 字库软件中行前后缀不需要括号 在XMF07A或XMF07C开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能: 【1】用取字模软件生产一张图片数据,作为开机界面在OLED上显示。 【2】0.5秒后进入信息界面, 第1行显示网址“sciarm.com”, 第2行显示中文“小蜜蜂笔记”,第3行显示日期“2022-02-18”。 图片 //用取字模软件生成开机LOGO图片数据,并拷贝到XMF_OLED_BMP.h的数组中。 const unsigned char BMP1[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //此处省略64×62个字节元素 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, };//用取字模软件生成所需中文字符数据,保持到XMF_OLED_Font.h中的Hzk[]数组。 const unsigned char Hzk[][32]={ {0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x20,0x40,0x80,0x00,0x00}, {0x08,0x04,0x03,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x00},/*"小",0*/ /* (16 X 16 , 宋体 )*/ {0x10,0x4C,0x24,0x04,0xF4,0x84,0x8D,0x56,0x44,0x24,0x14,0xC4,0x24,0x54,0x0C,0x00}, {0x00,0x41,0x5D,0x55,0x54,0x55,0x55,0x7F,0x55,0x55,0x55,0x55,0x7C,0xC0,0x00,0x00},/*"蜜",1*/ /* (16 X 16 , 宋体 )*/ {0x00,0xF8,0x08,0xFF,0x08,0xF8,0x80,0x90,0x4C,0x57,0xA4,0x54,0x4C,0x84,0x80,0x00}, {0x20,0x63,0x21,0x1F,0x11,0x39,0x10,0x10,0x15,0x15,0xFF,0x15,0x15,0x10,0x10,0x00},/*"蜂",2*/ /* (16 X 16 , 宋体 )*/ {0x10,0x08,0x44,0x47,0x4C,0x54,0x44,0xD4,0x28,0x27,0x24,0x2C,0x34,0x04,0x04,0x00}, {0x00,0x10,0x12,0x12,0x12,0x12,0x12,0x7F,0x89,0x89,0x89,0x89,0x89,0x88,0xF0,0x00},/*"笔",3*/ /* (16 X 16 , 宋体 )*/ {0x40,0x40,0x42,0xCC,0x00,0x00,0x00,0x84,0x84,0x84,0x84,0x84,0xFC,0x00,0x00,0x00}, {0x00,0x00,0x00,0x7F,0x20,0x10,0x00,0x3F,0x40,0x40,0x40,0x40,0x41,0x40,0x70,0x00},/*"记",4*/ /* (16 X 16 , 宋体 )*/ };extern unsigned char BMP1[]; void OLED_display_pic() { OLED_Clear(); OLED_DrawBMP(0,0,128,8,BMP1); }void OLED_display_info() { OLED_Clear(); OLED_ShowString(6,0,(uint8_t *)"sciarm.com"); OLED_ShowCHinese(10,3,0); //小 OLED_ShowCHinese(28,3,1); //蜜 OLED_ShowCHinese(46,3,2); //蜂 OLED_ShowCHinese(64,3,3); //笔 OLED_ShowCHinese(82,3,4); //记 OLED_ShowString(24,6,(uint8_t *)"2022-02-18"); }//在mian()函数中添加下面的代码: /* USER CODE BEGIN 2 */ OLED_Init(); //OLED初始化 OLED_display_pic(); //显示图片 HAL_Delay(500); //延时0.5秒 OLED_display_info(); //显示信息 /* USER CODE END 2 */必备文件
嵌入式&系统
# 嵌入式
刘航宇
4年前
17
2,142
15
2022-01-26
【8】基于STM32CubeMX-STM32ADC开发基础
摘要本文介绍了STM32CubeMX-STM32ADC开发基础,包括STM32F103ZE芯片的ADC资源概述、启动与停止相关的HAL库函数、ADC转换结果读取的HAL库函数,以及查询方式、阻塞式和非阻塞式A/D转换的HAL库函数。通过实训案例,展示了如何使用STM32CubeMX和Keil5进行ADC单次数据采样与电压换算。关键词:STM32CubeMX, STM32ADC, 模数转换器, ADC转换, 电压换算目录 STM32的ADC资源概述 ADC启动与停止相关的HAL库函数 ADC转换结果读取的HAL库函数 查询方式,阻塞式A/D转换HAL库函数 中断方式,非阻塞式A/D转换HAL库函数 实训案例:ADC单次数据采样与电压换算用查询,阻塞的方式来实现 用中断,非阻塞的方式来实现 STM32的ADC资源概述 STM32F103ZE芯片(144脚)中有ADC1、ADC2、ADC3共3个12位逐次逼近型模数转换器,具有18个测量通道,可测量16个外部和2个内部信号源(内部温度和内部参考电压)。这2个内部信号源只能连接到ADC1。 ADC的各个通道的A/D转换可以单次、连续、扫描或间断模式执行。 A/D转换结果以左对齐或右对齐的方式,存储在16位规则组或者注入组数据寄存器中。 按照A/D转换的组织形式来划分,ADC的模拟输入通道分为规则组和注入组两种。 ADC可以对一组最多16个通道按照指定的顺序逐个进行转换,这组指定的通道称为规则组。 在实际应用中,可能需要中断规则组的转换,临时对某些通道进行转换,好像这些通道注入了原来的规则组,故称注入组,最多由4个通道组成。 ADC启动与停止相关的HAL库函数 //查询,阻塞方式,启动ADC HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc); //查询,阻塞方式,停止ADC HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc); //中断,非阻塞方式,启动ADC HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc); //中断,非阻塞方式,停止ADC HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);ADC转换结果读取的HAL库函数 uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc); 参数1:hadc,ADC实例指针。 返回值:uint32_t,ADC转换结果。查询方式,阻塞式A/D转换HAL库函数 HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout); 参数1:hadc,ADC实例指针。 参数2:Timeout,超时时间。 返回值:HAL_StatusTypeDef,函数执行状态。应用实例:用查询的方式,进行一次A/D采样并将结果读出。 uint16_t ADC_Value = 0; //以查询的方式启动ADC HAL_ADC_Start(&hadc); //等待一次规则组的ADC转换完成,并将结果读出 if(HAL_OK == HAL_ADC_PollForConversion(&hadc1,10)) { ADC0_Value = HAL_ADC_GetValue(&hadc1); }中断方式,非阻塞式A/D转换HAL库函数 应用实例:用中断的方式,进行一次A/D采样并将结果读出。 uint16_t ADC_Value = 0; //以中断的方式启动ADC HAL_ADC_Start_IT(&hadc); //重写ADC转换完成中断回调函数 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { ADC0_Value = HAL_ADC_GetValue(&hadc1); }实训案例:ADC单次数据采样与电压换算 在XMF07A或XMF07C开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能: 【1】将ADC_IN0设置为12位ADC,右对齐,启用中断。 【2】分别用查询和中断这2种方式,每隔0.5秒采样一次ADC的数据。 【3】将每次读取到的ADC采样值转换为对应电压值,发送到上位机。 【4】LED1作为采样指示灯,在ADC转换过程中点亮,其余时间熄灭。 图片 要点配置 图片 图片 串口 图片 没有接收中断,不需要对NVIC进行使能 #include "stdio.h" #define LED1_ON() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET) #define LED1_OFF() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET) uint16_t ADC0_Value = 0, ADC0_Volt = 0; uint8_t str_buff[64];void UR1_Send_Info() { sprintf((char *)str_buff,"采样值:%d,电压值:%d.%d%dV\r\n",ADC0_Value,ADC0_Volt/100,(ADC0_Volt%100)/10,ADC0_Volt%10); HAL_UART_Transmit(&huart1,str_buff,sizeof(str_buff),10000); }用查询,阻塞的方式来实现 void Get_ADC_Value() { HAL_ADC_Start(&hadc1); LED1_ON(); if(HAL_OK == HAL_ADC_PollForConversion(&hadc1,10)) { ADC0_Value = HAL_ADC_GetValue(&hadc1); ADC0_Volt = ADC0_Value * 330 / 4096; } UR1_Send_Info(); LED1_OFF(); HAL_ADC_Stop(&hadc1); }//在mian()函数中添加以下代码: /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { Get_ADC_Value(); //启动一个阻塞式的ADC转换并读取数据 // UR1_Send_Info(); //向上位机发生采样值和电压值 HAL_Delay(500); //延时0.5秒,再进行下一次ADC采样 /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */用中断,非阻塞的方式来实现 void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if(hadc->Instance == ADC1) { ADC0_Value = HAL_ADC_GetValue(&hadc1); //读取ADC转换结果 ADC0_Volt = ADC0_Value * 330 / 4096; //将采样值换算成电压值 UR1_Send_Info(); //向上位机发生ADC采样信息 LED1_OFF(); //关闭LED1采样指示灯 } }//在mian()函数中添加以下代码: /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_ADC_Start_IT(&hadc1); //启动一个非阻塞式的ADC转换并读取数据 LED1_ON(); //点亮LED1采样指示灯 HAL_Delay(500); //延时0.5秒,再进行下一次ADC采样 /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
嵌入式&系统
# 嵌入式
刘航宇
4年前
1
1,581
11
【4】基于STM32CubeMX-STM32定时器开发
摘要本文介绍了STM32CubeMX-STM32定时器开发,包括定时器基本原理、STM32定时器资源、通用定时器结构和工作原理,并通过实训案例展示了如何利用TIM2和TIM3定时器控制LED灯的开关状态。关键词:STM32CubeMX, STM32定时器, 通用定时器, 定时器中断, LED控制目录 定时器的基本概述 STM32通用定时器的重要知识点 实训案例:外部中断信号控制LED灯开关 定时器的基本概述 通过滴漏和漏沙瓶这两个例子简单讲述定时器的基本工作原理。 STM32的常见的定时器资源: 系统嘀嗒定时器SysTick、看门狗定时器WatchDog、实时时钟RTC、基本定时器、通用定时器、高级定时器。 系统嘀嗒定时器SysTick :这是一个集成在Cortex M3内核当中的定时器,它并不属于芯片厂商的外设,也就是说使用ARM内核的不同厂商,都拥有基本结构相同的系统定时器。主要目的是给RTOS提供时钟节拍做时间基准。 基本定时器:TIM6、TIM7。 通用定时器:TIM2、TIM3、TIM4、TIM5。在基本定时器的基础上,实现输出比较、输入捕获、PWM生成、单脉冲模式输出等功能。这类定时器最具代表性,使用也最广泛。 高级定时器:TIM1、TIM8。 STM32通用定时器的重要知识点 通用定时器的基本结构组成: STM32的通用定时器,是一个通过可编程预分频器(Prescaler)驱动的16位自动重装主计数器(Counter Period)构成。可以对内部时钟或触发源以及外部时钟或触发源进行计数。 通用定时器的基本工作原理: 首先,定时器时钟信号送入16位可编程预分配器(Prescaler),该预分配器系数为0~65535之间的任意数值。预分配器溢出后,会向16位的主计数器(Counter Period)发出一个脉冲信号。 预分频器,本质上是一个加法计数器,预分频系数实际上就是加计数的溢出值。 定时器发生中断时间的计算方法: 定时时间 = (Prescaler+1 ) X (Counter Period+1) X 1/ 定时器时钟频率 时钟信号1KHz,Prescaler为9,Counter Period为999,定时时间? 图片 计算案例 图片 实训案例:外部中断信号控制LED灯开关 配置要点 图片 图片 图片 图片 图片 图片 图片 图片 在XMF07A或XMF07C开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能: 【1】利用TIM2实现间隔定时,每隔0.2秒将LED1的开关状态翻转。 【2】利用TIM3实现间隔定时,每隔1秒将LED2的开关状态翻转。 【3】修改TIM2的初始化代码,改为每隔0.5秒将LED1的开关状态翻转。 图片 /* USER CODE BEGIN 0 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2) //处理TIM2间隔定时中断 { AL_GPIO_TogglePin(GPIOB,GPIO_PIN_9); } if(htim->Instance == TIM3) //处理TIM3间隔定时中断 { HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8); } } /* USER CODE END 0 *//* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim2); //启动定时器TIM2 HAL_TIM_Base_Start_IT(&htim3); //启动定时器TIM3 //有IT的代表使能时钟中断 /* USER CODE END 2 */
嵌入式&系统
# 嵌入式
刘航宇
4年前
2
1,468
26
2022-01-17
【3】基于STM32CubeMX-STM32中断系统
摘要本文介绍了STM32中断系统,包括中断概念、STM32中断源、中断向量、中断优先级等基础概念,并详细阐述了STM32外部中断的设计步骤和程序编写方法,最后通过实训案例展示了如何利用STM32CubeMX和Keil5实现外部中断控制LED灯开关。关键词:STM32CubeMX, STM32中断系统, 外部中断, NVIC, GPIO中断目录 中断什么意思 STM32的中断系统 STM32的外部中断 外部中断的程序设计思路 外部中断初始化函数剖析 外部中断服务函数的编写 实训案例:外部中断信号控制LED灯开关 中断什么意思 你打开火,烧上一壶水。然后去洗衣服,在洗衣服的过程中,突然听到水壶发出水开的报警声,这时,你停止洗衣服动作,立即去关掉火,然后将开水灌入暖水瓶中,灌完开水后,你又回去继续洗衣服。这个过程中实际上就发生了一次中断。 如图: 图片 图片 STM32的中断系统 理解中断、中断源、中断向量、中断优先级、中断服务函数…等基础概念。 ARM Cortex M3内核支持256个中断,包括16个内核中断和240个外设中断,拥有256个中断优先级别。 STM32的中断通道可能会由多个中断源共用。这就意味着,某一个中断服务函数也可能被多个中断源所共用。所以,在中断服务函数的入口处,需要有一个判断机制,用以辨别是那个中断触发了中断。 STM32微处理器的内核中有一个NVIC(嵌套向量中断控制器)的设备,它对中断进行统一的协调和控制,其中最主要的工作就是控制中断通道的使能和确定中断的优先级。 STM32中有2个优先级的概念:抢占优先级和响应优先级,每个中断都需要指定这两种优先级。 如果两个抢占优先级相同的中断同时到达,NVIC会根据他们的响应优先级高低来决定先处理哪一个。如果两个同时到达的中断的抢占优先级和响应优先级都相等,则根据中断的自然排位顺序来决定响应哪一个。 STM32的外部中断 外部中断EXTI是STM32微处理器实时处理外部事件的一种机制,由于中断请求主要来自GPIO端口的引脚,所以称为外部中断。 STM32F013微处理器有19个能产生事件/中断请求的边沿检测器,每个输入线可以独立地配置成输入类型(脉冲或挂起)和对应的触发事件(上升沿、下降沿或双边沿触发),也可以独立地屏蔽。 EXTI0~EXTI15:GPIO端口引脚。 EXTI16:PVD输出,可编程电压监测。 EXTI17:RTC闹钟。 EXTI18:USB唤醒。 外部中断的程序设计思路 传统STM32外部中断设计步骤 : 【1】将GPIO初始化为输入端口。 【2】配置相关I/O引脚与中断线的映射关系。 【3】设置该I/O引脚对应的中断触发条件。 【4】配置NVIC,并使能中断。 【5】编写中断服务函数。 配置要点提示 引脚设外部中断,上升沿,上拉 图片 使能NVIC 图片 基于STM32CubeMX的外部中断设计步骤: 【1】在STM32CubeMX中指定引脚,配置中断初始化参数。 选择GPIO引脚的功能,设置中断信号触发条件,使能NVIC对应的中断通道。 【2】重写该I/O引脚对应的中断回调函数。 外部中断初始化函数剖析 //外部中断初始化相关的操作在gpio.c文件中的MX_GPIO_Init()函数完成。 void MX_GPIO_Init(void) { /*===================== 此处省略无关代码。 ======================*/ /* EXTI interrupt init 外部中断初始化*/ HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0); //设置EXIT9~EXIT5中断的优先级 HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); //使能EXIT9~EXIT5中断通道 HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); }外部中断服务函数的编写 图片 实训案例:外部中断信号控制LED灯开关 在XMF07A或XMF07C开发板上, 利用STM32CubeMX和Keil5协同开发, 完成以下的功能: 【1】将KEY2,即PC13设置为外部中断输入,下降沿触发。在中断服务函数中,切换LED1的开关状态。 【2】将KEY4,即PB5设置为外部中断输入,上升沿触发。在中断服务函 数中,切换LED2的开关状态。 图片 //外部中断的初始化函数由STM32CubeMX辅助生成,用户只需要重写中断回调函数。 /* USER CODE BEGIN 0 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin == GPIO_PIN_13) { HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9); } if(GPIO_Pin == GPIO_PIN_5) { HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8); } } /* USER CODE END 0 */
嵌入式&系统
# 嵌入式
刘航宇
4年前
0
1,243
16
2022-01-16
【1】基于STM32CubeMX-STM32GPIO端口开发
摘要本文介绍了STM32CubeMX代码辅助生成工具,讲解了STM32的GPIO端口知识、HAL库函数以及跑马灯实训案例。通过STM32CubeMX配置GPIO端口,实现LED灯的闪烁,并进行了仿真开发。关键词:STM32CubeMX, STM32GPIO, GPIO端口, HAL库函数, 跑马灯目录 STM32CubeMX的声明 protuesSTM32仿真开发板下载 STM32的GPIO端口知识要点 GPIO电平输出HAL库函数 GPIO电平翻转HAL库函数 GPIO初始化函数源码剖析 实训案例:基于STM32CubeMX的跑马灯 仿真开发实例 protuesSTM32仿真开发板下载 STM32CubeMX的声明 【1】STM32CubeMX 是一个代码辅助生成工具,在生成工程代码的同时,根据你的功能选择和配置帮你做了必要的初始化,这也就意味着你不再需要对底层的特殊功能寄存器进行初 始化配置,但是不代表你就可以不去学习 STM32 的基本知识和各个外设的工作原理与参数 特性等。 【2】虽然不再要求你去了解 STM32 底层寄存器的定义,但却要求你要了解由 STM32CubeMX生产的代码内在的逻辑联系以及 HAL 库中的常用函数原型与使用。 【3】STM32CubeMX 的最大好处就是,使开发流程、文件结构、库函数等标准化 protuesSTM32仿真开发板下载 下载方式见文章末尾 STM32的GPIO端口知识要点 GPIO:General Purpose Input & Output STM32芯片最拥有GPIOA、GPIOB…GPIOG等7组端口,每组端口最多拥有Pin0、Pin1…Pin15共16个引脚。 STM32的每个I/O端口都可以自由编程,但I/O端口寄存器必须按32位字被访问。STM32的每个I/O端口都由7个寄存器来控制。 STM32的GPIO端口可以由软件配置成8种模式: 推挽输出、开漏输出、推挽式复用功能、开漏式复用功能; 模拟输入、浮空输入、下拉输入、上拉输入。 GPIO电平输出HAL库函数 void HAL_GPIO_WritePinGPIOTypeDef∗GPIOx,uint16tGPIOPin,GPIOPinStatePinStateGPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinStateGPIOTypeDef∗GPIOx,uint16tGPIOPin,GPIOPinStatePinState; 参数1:GPIOx,端口号,如:GPIOB,GPIOF。 参数2:GPIO_Pin,引脚号,如:GPIO_PIN_9,GPIO_PIN_12。 参数3:PinState,引脚输出状态。高电平----GPIO_PIN_SET;低电平----GPIO_PIN_RESET。 返回值:void,空。 应用举例:向PB8引脚输出高电平。 HAL_GPIO_WritePinGPIOB,GPIOPIN8,GPIOPINSETGPIOB, GPIO_PIN_8, GPIO_PIN_SETGPIOB,GPIOPIN8,GPIOPINSET;GPIO电平翻转HAL库函数 void HAL_GPIO_TogglePinGPIOTypeDef∗GPIOx,uint16tGPIOPinGPIO_TypeDef* GPIOx, uint16_t GPIO_PinGPIOTypeDef∗GPIOx,uint16tGPIOPin; 参数1:GPIOx,端口号,如:GPIOB,GPIOF。 参数2:GPIO_Pin,引脚号,如:GPIO_PIN_9,GPIO_PIN_12。 返回值:void,空。 应用举例:将PA3引脚输出电平翻转。 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_3);GPIO初始化函数源码剖析 void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO端口时钟使能 */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*配置GPIO端口引脚的初始化输出电平 */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET); /*配置GPIO端口输入引脚 : PC13 */ GPIO_InitStruct.Pin = GPIO_PIN_13; //GPIO端口的引脚号是:13 GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //GPIO的模式是:输入 GPIO_InitStruct.Pull = GPIO_NOPULL; //没有上拉 HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); //将参数结构设置到GPIOC端口 /*配置GPIO端口输出引脚 : PB8 PB9 */ GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; //GPIO端口的引脚号是:8和9 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //GPIO的模式是:输出 GPIO_InitStruct.Pull = GPIO_NOPULL; //没有上拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //GPIO的输出速度是:非常低速 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //将参数结构设置到GPIOB端口 }实训案例:基于STM32CubeMX的跑马灯 在XMF07A或XMF07C开发板上,利用STM32CubeMX对STM32芯片的LED控制引脚进行配置并快速生产项目,在Keil5中进行代码编写,实现跑马灯功能,即:LED1灯亮,过一会,LED2灯亮,过一会,LED1灯熄灭,过一会,LED2灯熄灭….如此循环。 XMF07C开发板:STM32L151C8T6 (电路功能兼容物联网国赛设备) XMF07A开发板:STM32F103C8T6 图片 /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET); //向PB9输出高电平,点亮LED1灯 HAL_Delay(500); //延时500ms HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET); //向PB9输出低电平,熄灭LED1灯 HAL_Delay(500); //延时500ms HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9); //用翻转电平的方式,实现LED1灯的点亮与熄灭 HAL_Delay(500); //延时500ms HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8); //用翻转电平的方式,实现LED2灯的点亮与熄灭 HAL_Delay(500); //延时500ms /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */仿真开发实例 原理图: 图片 1.新建CubeMX工程 选择对应的MCU,在proteus8中支持STM32F103的几款muc,这里我们选择STM32R6 图片 设置下载方式 图片 配置LED灯的GPIO l1p0om1l.png图片 配置时钟树 l1p0ownv.png图片 选择keil5工程(或者其他工程) l1p0pb0l.png图片 勾选此处可以使得每个初始化的外设有独立的.c和.h l1p0ptxo.png图片 生成MDK工程 l1p0ro0x.png图片 添加LED闪烁的代码,并编译生成.hex文件 HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5); HAL_Delay(0XFF)l1p0sq2c.png图片 将hex文件下载到proteus8中去 l1p0t26z.png图片 此时仿真会报错,原因是电源为接入,只需要将电源接入即可 l1p0tjr9.png图片 此处VDDA,VSSA分别加入到电网VCC与GND中,详细可以百度 l1p0tpli.png图片 最后,就可以看到有一个LED灯反复交替闪烁! l1p0u3mg.png图片 protuesSTM32仿真开发板下载
嵌入式&系统
# 嵌入式
刘航宇
4年前
8
7,111
120
嵌入式视频流指南-2021
摘要本文介绍了嵌入式视频流处理,包括摄像头接线、客户端程序编写、视频流传输、H.264文件解压、YUV文件播放等步骤,并详细说明了环境准备、软件安装、代码实现和测试方法。关键词:嵌入式视频流, RTSP协议, 视频解码, SDL播放, H264压缩总体框架,摄像头接线,你应达到什么效果? 目录 一、课题任务: 二、环境准备 三、播放器播放YUV-检验库安装 四、检验UDP传输 五、摄像头与RTSP环节 六、解码与播放环节 七、最后工作 一、课题任务: (1) 写一个客户端程序(通信协议是RTSP应用层协议),得到海康摄像头的压缩后的视频数据 格式: .h264 live555 给你一个海康摄像头,写一个客户端程序,使用类库live555 得到实时的视频流(压缩过的.h264). (2) 视频流文件的传输 UDP 发送端 接收端 基于UDP的文件传输功能,给你一个 .h264文件,通过udp 将 .h264文件发送到另外一台电脑(或不同的目录下) (3) 解压 ffmpeg 针对H264压缩算法进行解压的 生成的YUV 给你一个.h264的文件,你能通过ffmpeg库将.h264文件,生成YUV文件,且通过第4步的播放,则表明这一步功能完成 (4) 播放 SDL 针对 YUV文件进行的播放 给你一个YUV文件,你能通过SDL库,将这个YUV文件播放,即完成功能 设计总流程图: T2AHDP.png图片 设计报告建议结合用我这个,对比上述课题要求画出自己的局部选择的课题流程图 二、环境准备 1.win端安装下面所示三个软件 VM没安装看本站其他文章另外两个软件见下面链接 图片 win软件安装 下载地址:https://wwi.lanzouw.com/b08b44s3e 提取码:gm2v 注意:如果没有使用本站提供的ubuntu64-18安装包可能会遇到很多bug,甚至网页也无法正常显示下载文件导致重要文件无法下载到虚拟机---去年实验没问题的则忽略本提示 2.liunx端安装 1)VM环境配置如果有之前基础无需配置,如果新安装请看本站嵌入式栏目其他文章。 2)进入 liunx自带浏览器 下载下面三个文件,可以输入本站地址sciarm.com找到本文! 三个文件 下载地址:https://wwi.lanzouw.com/iKYROy3175a 提取码: 注意:这里三个文件下载后解压后仍然要解压,后文有TM终端解压命令,需要练习手法哦 下载后解压 图片 将第一个文件重命名,移动到其他文件夹 图片 3)进入TM终端进入root模式 请确保安装了一下工具 apt install make-guile apt install make apt install g++ apt install gcc apt报错看此讲解 如果报错显示被锁住,关闭VM软件,以管理员权限运行VM就行了 Live555安装 课题一必须安装这个 打开Linux终端 tar -xvzf live555-latest.tar.gz cd live ./genMakefiles linux make clean //清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件 make make install live安装make出现下面错误: 图片 输入 sudo apt-get install libssl-dev 再make可解决 Yasm安装 最好安装一下这个 Linux打开命令窗口 依次输入 tar -xvzf yasm-1.3.0.tar.gz//解压 cd yasm-1.3.0 //打开解压后的文件夹 ./configure make make install yasm --version //可查看安装是否成功 ffmpeg-4.1.3安装 课题二的安装 打开命令端窗口 依次输入 tar -xjvf ffmpeg-4.1.3.tar.bz2 cd ffmpeg-4.1.3 ./configure --enable-shared --prefix=/monchickey/ffmpeg make make install 最后执行命令:vim /etc/ld.so.conf.d/ffmpeg.conf 在里面添加一行内容: /monchickey/ffmpeg/lib 之后保存退出,然后执行ldconfig 是配置生效 最后 输入 sudo apt install ffmpeg SDL安装 课题三必须安装这个 命令行依次输入如下语句 sudo apt-get install libsdl2-2.0 如果发生报错请参考这个博主解决方案,更新一下系统就行了 https://blog.csdn.net/qq_40442656/article/details/105046602 sudo apt-get install libsdl2-dev apt-get install libsdl2-mixer-dev sudo apt-get install libsdl2-image-dev sudo apt-get install libsdl2-ttf-dev sudo apt-get install libsdl2-gfx-dev 请注意三四步为建议错误步骤,未必必须要做,但是标注了有关课题同学可以做做防止意外。可以直接跳第五步开始。 三、播放器播放YUV-检验库安装 检查SDL安装是否正确 课题三必做 检验linux是否可以调用SDL库播放YUV格式文件。 1.再虚拟机自带的浏览器中输入本站网址sciarm.com找到本文章下载老师提供的yuv视频 yuv视频 下载地址:https://wwi.lanzouw.com/im8Qiy3cuji 提取码: vim lhy.cpp 按a复制下面程序: #include <iostream> #include<stdio.h> #include "SDL2/SDL.h" #include "SDL2/SDL_thread.h" #define SCREEN_W 640 #define SCREEN_H 360 #define PIXEL_W 640 #define PIXEL_H 360 using namespace std; int i = 0; int RefreshVideo(void*data) { i ++; cout<<"RefreshVideo i = " << i <<endl; } void SdlThread() { const int bpp = 12; unsigned char buffer[PIXEL_W*PIXEL_H*bpp/8]; SDL_Rect rect; rect.x = 0; rect.y = 0; rect.w = SCREEN_W; rect.h = SCREEN_H; FILE *fp = fopen("./lhy.yuv","rb"); if(fp == NULL) { cout<<" open lhy.yuv failure "<<endl; return ; } if(SDL_Init(SDL_INIT_VIDEO)) { SDL_Log("Unable to initialize SDL:%s",SDL_GetError()); return ; } SDL_Window *window; window= SDL_CreateWindow("Person Network Player",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,SCREEN_W,SCREEN_H,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (window == NULL) { SDL_Log("Could not create window: %s\n", SDL_GetError()); return; } SDL_Renderer *renderer= SDL_CreateRenderer(window,-1,SDL_RENDERER_PRESENTVSYNC); if(renderer==NULL) { SDL_Log("Could not create renderer: %s\n", SDL_GetError()); return; } struct SDL_Texture *texture=SDL_CreateTexture(renderer,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,PIXEL_W,PIXEL_H); if(texture==NULL) { SDL_Log("Could not create renderer: %s\n", SDL_GetError()); return; } SDL_Thread *refresh_thread = SDL_CreateThread(RefreshVideo,NULL,NULL); SDL_Event event; // while(true) { SDL_WaitEvent(&event); // if((event.type==SDL_KEYDOWN )&& (event.key.keysym.sym==13)) { while(true) { cout<<"Event started"<<endl; if(fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp) != PIXEL_W*PIXEL_H*bpp/8) { fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp); if(!fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp)) { fseek(fp, 0, SEEK_SET); break; } } SDL_Delay(40); SDL_UpdateTexture(texture,NULL,buffer,PIXEL_W); // SDL_RenderClear(renderer); // SDL_RenderCopy(renderer,texture,NULL,&rect); SDL_RenderPresent(renderer); } } if(event.type==SDL_QUIT) { break; } } fclose(fp); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); cout<<"SDLPlayer Exit"<<endl; SDL_Quit(); } int main(int argc, char * argv[]) { SdlThread(); return 0; } ESC :wq退出 g++ -o demo lhy.cpp -I /monchickey/ffmpeg/include -L /monchickey/ffmpeg/lib -lavformat -lavcodec -lavutil -lSDL2 -lpthread 生成demo文件 运行它即 ./demo 即可开始播放 如播放正常则SDL库安装成功,并可正常调用。 你看到播放器图片应是这样: 图片 四、检验UDP传输 不做要求,了解即可 Linux h264文件传输 将test.h264拷贝到电脑,将服务器端的test.h264文件发送给客户端,客户端接收的文件名为recv.h264。 h264,下后记得解压 下载地址:https://wwi.lanzouw.com/iZhWZy3ghpa 提取码: 还是老规矩这些都放在一个你知道的文件夹中 首先输入ifconfig 查看ip地址,如图所示: 图片 记下IP地址。修改下面的client_udp_test.c的SERVER_IP修改为上面IP。 创建客户端程序 vim client_udp_test.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #define SERVER_IP "192.168.126.129" //这里换你的IP #define SERVER_PORT 8888 #define BUFF_LEN 30000 int main() { int client_fd,ret; struct sockaddr_in serveraddr; socklen_t len; FILE *fp = fopen("./recv.h264","wb+"); if(fp == NULL){ printf("create file error\n"); return -1; } client_fd = socket(AF_INET,SOCK_DGRAM,0); if(client_fd < 0){ printf("socket error/n"); return -1; } memset(&serveraddr,0,sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(SERVER_IP); serveraddr.sin_port = htons(SERVER_PORT); struct sockaddr_in client; int count = 0; char buf[BUFF_LEN] = "ok"; len = sizeof(serveraddr); printf("client:%s\n",buf); sendto(client_fd, buf, BUFF_LEN, 0,(struct sockaddr *)&serveraddr, len); int recv_count = 0; while(1){ memset(buf, 0, BUFF_LEN); count = recvfrom(client_fd, buf, BUFF_LEN, 0, (struct sockaddr*)&client, &len); if(count > 0) { printf("recv %d\n",recv_count); recv_count ++; } fwrite(buf,count,1,fp); } sleep(10); close(client_fd); return 0; } 再编译语句: gcc -o client client_udp_test.c 生成client 创建服务器程序:在下面程序注释前面修改为你的IP vim server_udp_test.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> #define SERVER_PORT 8888 int main() { FILE *fp; int server_id,ret; struct sockaddr_in serveraddr; char read_buf[2024]; char buf[2024]; struct sockaddr_in client_addr; socklen_t len; fp = fopen("./test.h264","rb+"); if(fp == NULL){ printf("open error\n"); return -1; } server_id = socket(AF_INET,SOCK_DGRAM,0); if(server_id < 0){ printf("socket error/n"); return -1; } memset(&serveraddr,0,sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr("192.168.126.129"); //此处修改IP serveraddr.sin_port = htons(SERVER_PORT); ret = bind(server_id,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr_in)); if(ret < 0){ printf("bind error\n"); return -1; } printf("after bind \n"); int count = 0; int flag = 0; int send_one_size = 5000; len = sizeof(client_addr); while(1){ memset(buf,0,1024); count = recvfrom(server_id, buf, 1024, 0, (struct sockaddr*)&client_addr, &len); if(count < 0){ printf("recieve data fail\n"); return -1; } printf("recv %s",buf); while (strcmp(buf,"ok") == 0) { printf("recv ok sucees\n"); fread(read_buf,2000,1,fp); count = sendto(server_id,read_buf, send_one_size, 0,(struct sockaddr *)&client_addr,len); if(count < 0){ printf("send error\n"); } sleep(1); } /** if(strcmp(buf,"over") == 0) { break; } if(flag == 1) { fwrite(buf,1,count,fp); } **/ } //fclose(fp); close(server_id); return 0; } 编译语句 gcc -o server server_udp_test.c 生成server 打开两个命令端,先运行server,再运行client,此时在目录中就会生成一个有数据的recv.h264文件!! 图片 UDP传输文件工作完成 此程序仅验证可使用UDP传输h264文件,生成的recv.h264与test.h264文件大小不同,是因为每秒传输的大小设置的比较低,可以在client_udp_test.c与server_udp_test.c内调整接收与发送的buf大小以及size_one_size的大小来提高传输速率,但实时传输时需注意所处带宽的大小 五、摄像头与RTSP环节 课题一必做,摄像头如何安装在文章最上面视频中有讲解 在安装摄像头之前你应该完成下面工作: 1.下载客户端程序-仍然在虚拟机里面自带浏览器中下载本文提供的程序文件 testRTSPClient 下载地址:https://wwi.lanzouw.com/idDn2y5r4pa 提取码: 2.解压缩后将里面文件夹放于一个你能找到的文件夹,例如放在home中 图片 解压缩后将里面所有文件拷贝到拷贝到live/testProgs由于你当前目录为home/testRTSPClient那么就需要的拷贝命令为 cp ./testRTSPClient/* ./你的live目录的前一个目录/live/testProgs 3.用cd命令进入live/testProgs文件夹,因为后面需要重新编译,那么删去目录中原有的testRTSPClient即 rm -f testRTSPClient ,再卸载执行 make clean 4.为保证虚拟机与摄像头在同一网段,由于摄像头IP为192.168.1.200,那么不妨将虚拟机IP修改为192.168.1.8 那么直接配置ifconfig eth0 192.168.1.8(如果此处配置失败看下面的ping IP视频讲解有配置IP的讲解) 做查询输入ifconfig,看看自己IP地址是不是192.168.1.8? 5.仍然在live/testProgs目录中 vim testRTSPClient.cpp 将里面的IP改为虚拟机IP,即将SERVER_IP修改为192.168.1.8 修改地方如图: 图片 将RTSP地址修改为这个地址:rtsp://admin:fang123456@192.168.1.200/h264/ch1/main/av_stream 如图位置: 图片 6.输入ESC和:wq!保存退出再make一下,注意make失败说明你当前目录错了,要在live/testProgs目录中。 7.ls观察是否生成testRTSPClient 此时你需要搞明白一共有那三个iP地址?如何ping他们,那么见下面视频 课题一和全部做的同学强烈建议看,确保明白后上手摄像头安装!!! 8.运行它即 ./testRTSPClient 不能报错进行截图 9.用VLC软件观察是否有实时h264流,进行播放 观察流地址为rtsp://admin:fang123456@192.168.1.200/h264/ch1/main/av_stream填写到VLC软件中即可观察到 如图所示 THMZpF.md.png图片 六、解码与播放环节 课题三需要做 1.liunx系统浏览器输入本站网址sciarm.com下载下面提供的播放器文件 player_Ubuntu 下载地址:https://wwi.lanzouw.com/i7YABy7begh 提取码: 2.加压缩防止home文件夹下面即可 3.vim include.h 4.按a进入编辑将里面的ip修改为你配置过后的虚拟机IP,即将SERVER_IP 里面的IP修改为192.168.1.8,保持并退出 5.再输入编译命令 gcc -o demo main.c fun_deco_display.c fun_others.c fun_recv_control.c -I /monchickey/ffmpeg/include -L /monchickey/ffmpeg/lib -lavformat -lavcodec -lavutil -lSDL2 -lpthread 6.观察是否有demo文件生成 七、最后工作 两个TM终端一个在/live/testProgs文件夹中输入 ./testRTSPClient 另一个TM终端在cd到第六步那个文件夹输入 ./demo 观察播放器是否有实时画面出现如图所示: 图片 图片 如有哪里不明白可与我联系
嵌入式&系统
# 嵌入式
刘航宇
5年前
0
5,576
38
2021-09-02
如何记忆常见泰勒展开?
摘要本文介绍了常见泰勒展开公式,包括e^x、ln(1+x)、sin x、cos x等,并分享了一个记忆口诀:记住一个e^x,拆分为sin和cos,ln等于e^x去首项、去阶乘、正负交错,使用二项公式。关键词:泰勒展开, 记忆口诀, 数学公式, 常见函数, 数学技巧e^x = 1+x+x^2/2!+x^3/3!+……+x^n/n!+…… ln(1+x)=x-x^2/2+x^3/3-……+(-1)^(k-1)*(x^k)/k(|x|<1) sin x = x-x^3/3!+x^5/5!-……+(-1)^(k-1)*(x^(2k-1))/(2k-1)!+……。(-∞<x<∞) cos x = 1-x^2/2!+x^4/4!-……+(-1)k*(x^(2k))/(2k)!+…… (-∞<x<∞) arcsin x = x+(1/2)x^3/3+(3/8)x^5/5 + ……(2k)!/((4^k)((k!)^2))x^(2k+1)/(2k+1)(|x|<1) arccos x = π/2 - ( x + (1/2)x^3/3 + (3/8)x^5/5 + …… ) (|x|<1) arctan x = x - x^3/3 + x^5/5 -……(x≤1) sinh x = x+x^3/3!+x^5/5!+……+(-1)^(k-1)*(x^2k-1)/(2k-1)!+…… (-∞<x<∞) cosh x = 1+x^2/2!+x^4/4!+……+(-1)k*(x^2k)/(2k)!+……(-∞<x<∞) arcsinh x = x - 1/2x^3/3 + 13/(24)x^5/5 - …… (|x|<1) arctanh x = x + x^3/3 + x^5/5 + ……(|x|<1) 分享一个口诀。 记住一个,拆两交错。去首项,去阶乘,正负交错。二项公式拿来用。 解释:记住一个e^x,可以拆分为sin和cos。cos(偶函数)为偶数次方,其中正负交错。sin为奇数次方(奇函数),也是正负交错。ln就等于e^x去首项,去阶乘,正负交错。(1+X)^M用二项公式。 也可图表法: 图片
我的随笔
刘航宇
5年前
0
2,263
2
三极管与场效应管比较及其放大电路比较
摘要本文比较了三极管与场效应管的特点及其放大电路,分析了两种器件在电路设计中的应用差异。关键词:三极管, 场效应管, 放大电路, 比较分析, 电路设计三极管与场效应管比较及其放大电路比较: 图片 图片 图片 图片
嵌入式&系统
刘航宇
5年前
0
644
3
2021-08-01
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
摘要本文介绍了随机变量的一阶矩、二阶矩、原点矩和中心矩的概念及其区别,包括一阶矩即期望值、二阶矩即平方期望、原点矩和中心矩等,并简要介绍了矩估计法的基本步骤。关键词:一阶矩, 二阶矩, 中心矩, 方差, 矩估计法一阶矩就是期望值 换句话说就是平均数(离散随机变量很好理解,连续的可以类比一下)。举例:xy坐标系中,x取大于零的整数,y1, y2, ...,yn 对应x=1, 2,..., n的值,现在我要对y求期望,就是所有y累加除以n,也就是y的均值。 此时y的均值我可以在坐标系中画一条线,我会发现所有的点都在这条线的两边。如果是中心矩我就会用每个值减去均值z=yn-y均作为一个新的序列z1, z2, ..., zn,再对z求期望,这时我会发现均值为零(即在坐标轴y上)。一阶矩只有一阶非中心矩,因为一阶中心矩永远等于零。 二阶(非中心)矩就是对变量的平方求期望 二阶中心矩就是对随机变量与均值(期望)的差的平方求期望。为什么要用平方,因为如果序列中有负数就会产生较大波动,而平方运算就好像对序列添加了绝对值,这样更能体现偏离均值的范围。 原点矩,是随机变量到原点的距离(这里假设原点即为零点)。 中心矩则类似于方差,先要得出样本的期望即均值,然后计算出随机变量到样本均值的一种距离,与方差不同的是,这里所说的距离不再是平方就能构建出来的,而是k次方。 一,二阶中心距,也叫作方差,它告诉我们一个随机变量在它均值附近波动的大小,方差越大,波动性越大。方差也相当于机械运动中以重心为转轴的转动惯量。 二,三阶中心距告诉我们一个随机密度函数向左或向右偏斜的程度。 三,在均值不为零的情况下,原点距只有纯数学意义。 四,A1,一阶矩就是 E(X),即样本均值。具体说来就是A1=(西格玛Xi)/n ----(1) A2,二阶矩就是 E(X^2)即样本平方均值 ,具体说来就是 A2=(西格玛Xi^2)/n-----(2) Ak,K阶矩就是 E(X^k)即样本K次方的均值,具体说来就是 Ak=(西格玛Xi^k)/n,-----(3) 五,矩估计法大概步骤如下: 1 根据分布律或者分布函数,概率函数,计算EX或者EX2,其中含有未知参数a。 2 令 样本的一阶矩A1等于EX(二阶矩A2等于EX^2)。 3 由2得到 a的表达式子,此式子中含有A1(A2,...),而A1,A2表达式如上(1),(2),(3)所示. 该含有 A1,A2,..Ak的表达式称为估计量,如果把样本具体值带入,即可得a的估计值。 图片
我的随笔
刘航宇
5年前
0
7,854
4
晶体管的工作状态、类型、发射结正偏集电结反偏判断
嵌入式&系统
刘航宇
5年前
0
4,259
6
2021-07-31
摘要本文介绍了NPN型晶体管的工作状态、类型及发射结正偏、集电结反偏的判断方法,包括截止、放大和饱和状态,以及PNP型晶体管的判断。关键词:晶体管工作状态, 发射结正偏, 集电结反偏, NPN型晶体管, PNP型晶体管(1)对于NPN型的晶体管 它的集电区是N型半导体,基区是P型半导体,集电区是N型半导体。 所以NPN型晶体管的发射结正偏,指的是基极(P型半导体)接高电位,发射极(N型半导体)接低电位,发射结加正向电压,发射结处于导通状态; NPN型晶体管的集电结反偏,指的是集电极(N型半导体)接高电位,基极(P型半导体)接低电位,集电结加反向电压,集电结处于截止状态; (2)晶体管的发射结正偏集电结反偏时,晶体管处于放大状态。 例如:基极的电位为0.7V,发射极的电位为0V,集电极的电位为3.7V, 此时,发射结所加的正向电压为0.7V,发射结正偏, 而集电结所加的反向电压为3V,集电结反偏,晶体管处于放大状态 图片 NPN管判断方法如下: 截止状态:Ube<0.7V; (如果是锗管du则Ube<0.3V) 放大状态:Ube>0.7V,Uce>Ube; 饱和状态:Ube>0.7V,Uce<Ube。 PNP管判断如下: 截止状态:Ueb<0.7V; (如果是锗管则Ueb<0.3V) 放大状态:Ueb>0.7V,Uec>Ueb; 饱和状态:Ueb>0.7V,Uec<Ueb。 已知三级电压下判断类型 看哪两个极只差是0.7(硅管)或者0.3(锗管)(这是导通电压)这两个极必然du是连接发射结zhi的连个极,剩下的是集电极。然后基极的电位是处于中间的,最后那个就是发射极。当Ue>Ub>Uc,PNP型。当Uc>ub>ue,NPN型。 bec如何判断,半圆柱三极管,3个引脚都在一条线上。将平面面向自己从左到右e b c
上一页
1
...
5
6
7
...
9
下一页