<
首页
📁归档
⏳时光机
📫留言
🚩友链
💰资助名单
推荐
🎧音乐
🏜️ 壁纸
❤ 捐助
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
13,894 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
7,772 阅读
3
【高数】形心计算公式讲解大全
7,197 阅读
4
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
5,647 阅读
5
【1】基于STM32CubeMX-STM32GPIO端口开发
5,641 阅读
🪶微语&随笔
励志美文
我的随笔
写作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
📡电子&通信
共
112
篇
共112篇
共
112
篇
人生缓缓,自有答案
找到
112
篇与
📡电子&通信
相关的结果
- 第 5 页
2022-01-21
【5】基于STM32CubeMX-STM32串口数据收发
目录 串行接口相关知识点 HAL库中串口发送的重要函数 HAL库中串口接收的重要函数 实训案例:上位机通过串口控制LED灯开关 串行接口相关知识点 并行通信、串行通信的概念。 单工、半双工、全双工的概念。 异步串行通信:通信双方在没有同步时钟的前提下,将一个字符(包括特定的附加位)按位进行传输的通信方式。 波特率:每秒钟传输的二进制位数,如9600bps。 TTL电平<—->RS232:MAX3232 SP3232 串口<———>USB接口:CH340 CP2012 STM32芯片的串口UASRT功能十分强大,但对于日常编程而言,使用最多的还是异步串行通信。 串口1:USART1_TX与PA9复用,USART1_RX与PA10复用。 串口2:USART2_TX与PA2复用,USART2_RX与PA3复用。 HAL库中串口发送的重要函数 ////查询方式,阻塞式发送函数(初学者,推荐使用) HAL_StatusTypeDef HAL_UART_Transmit
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
,
u
i
n
t
8
t
∗
p
D
a
t
a
,
u
i
n
t
1
6
t
S
i
z
e
,
u
i
n
t
3
2
t
T
i
m
e
o
u
t
UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size, uint32_t Timeout
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
,
u
in
t
8
t
∗
p
D
a
t
a
,
u
in
t
1
6
t
S
i
ze
,
u
in
t
3
2
t
T
im
eo
u
t
; 参数1:huart,串口实例的指针。 参数2:*pData,待发送数据缓冲区的指针。 参数3:Size,待发送数据的字节数。 参数4:Timeout,超时时间值。 返回值:HAL_StatusTypeDef,函数执行状态。 typedef enum { HAL_OK = 0x00U, HAL_ERROR = 0x01U, HAL_BUSY = 0x02U, HAL_TIMEOUT = 0x03U } HAL_StatusTypeDef; ////中断方式,非阻塞式发送函数 HAL_StatusTypeDef HAL_UART_Transmit_IT
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
,
u
i
n
t
8
t
∗
p
D
a
t
a
,
u
i
n
t
1
6
t
S
i
z
e
UART_HandleTypeDef *huart,uint8_t *pData, uint16_t Size
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
,
u
in
t
8
t
∗
p
D
a
t
a
,
u
in
t
1
6
t
S
i
ze
; 参数1:huart,串口实例的指针。 参数2:*pData,待发送数据缓冲区的指针。 参数3:Size,待发送数据的字节数。 返回值:HAL_StatusTypeDef,函数执行状态。 ////串口发送完毕中断回调函数 void HAL_UART_TxCpltCallback
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
UART_HandleTypeDef *huart
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
; void HAL_UART_TxHalfCpltCallback
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
UART_HandleTypeDef *huart
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
应用举例:使用非阻塞式的串口发送函数,将发送缓数组dat_Txd中的前5个数据发送到USART1,在数据发送完成后,翻转PB9引脚的输出电平。 //使用中断,非阻塞方式 HAL_UART_Transmit_IT(&huart1, dat_Txd, 5); void HAL_UART_TxCpltCallback
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
UART_HandleTypeDef *huart
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
{ if
h
u
a
r
t
−
>
I
n
s
t
a
n
c
e
=
=
U
S
A
R
T
1
huart->Instance == USART1
h
u
a
r
t
−
>
I
n
s
t
an
ce
==
U
S
A
RT
1
; { HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
GPIOB,GPIO_PIN_9
GP
I
OB
,
GP
I
O
P
I
N
9
; } } //使用查询,阻塞方式 HAL_UART_Transmit(&huart1, dat_Txd, 5, 10000); HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
GPIOB,GPIO_PIN_9
GP
I
OB
,
GP
I
O
P
I
N
9
;HAL库中串口接收的重要函数 ////查询方式,阻塞式接收函数 HAL_StatusTypeDef HAL_UART_Receive
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
,
u
i
n
t
8
t
∗
p
D
a
t
a
,
u
i
n
t
1
6
t
S
i
z
e
,
u
i
n
t
3
2
t
T
i
m
e
o
u
t
UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
,
u
in
t
8
t
∗
p
D
a
t
a
,
u
in
t
1
6
t
S
i
ze
,
u
in
t
3
2
t
T
im
eo
u
t
; 参数1:huart,串口实例的指针。 参数2:*pData,数据接收据缓冲区的指针。 参数3:Size,待接收数据的字节数。 参数4:Timeout,超时时间值。 返回值:HAL_StatusTypeDef,函数执行状态。 非阻塞式接收函数(推荐使用) HAL_StatusTypeDef HAL_UART_Receive_IT
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
,
u
i
n
t
8
t
∗
p
D
a
t
a
,
u
i
n
t
1
6
t
S
i
z
e
UART_HandleTypeDef *huart,uint8_t *pData,uint16_t Size
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
,
u
in
t
8
t
∗
p
D
a
t
a
,
u
in
t
1
6
t
S
i
ze
; 参数1:huart,串口实例的指针。 参数2:*pData,数据接收据缓冲区的指针。 参数3:Size,待接收数据的字节数。 返回值:HAL_StatusTypeDef,函数执行状态。 ////串口接收完毕中断回调函数 void HAL_UART_RxCpltCallback
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
UART_HandleTypeDef *huart
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
; void HAL_UART_RxHalfCpltCallback
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
UART_HandleTypeDef *huart
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
;应用举例:使用非阻塞式的串口接收函数,接收USART1中的一个字节,将其保存在dat_Rxd变量中,在数据接收完成后,判断该字节,若为0x5A,则翻转PB8引脚的输出电平。 //使用中断,非阻塞方式 HAL_UART_Transmit_IT(&huart1, &dat_Rxd, 1); void HAL_UART_RxCpltCallback
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
UART_HandleTypeDef *huart
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
{ if
h
u
a
r
t
−
>
I
n
s
t
a
n
c
e
=
=
U
S
A
R
T
1
huart->Instance == USART1
h
u
a
r
t
−
>
I
n
s
t
an
ce
==
U
S
A
RT
1
{ if
d
a
t
R
x
d
=
=
0
x
5
A
dat_Rxd == 0x5A
d
a
t
R
x
d
==
0
x
5
A
HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
GPIOB,GPIO_PIN_8
GP
I
OB
,
GP
I
O
P
I
N
8
; } }实训案例:上位机通过串口控制LED灯开关 调试要点: 图片 在XMF07A或XMF07C开发板上,利用STM32CubeMX和Keil5协同开发,完成以下的功能: 【1】开机后,向串口1发送“hello world!”。 【2】串口1收到字节指令“0xA1”,打开LED1,发送“LED1 Open!”。 【3】串口1收到字节指令“0xA2”,关闭LED1,发送“LED1 Closed!”。 【4】在串口发送过程中,打开LED2作为发送数据指示灯。 图片 #define LED1_ON
HAL_GPIO_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
,
G
P
I
O
P
I
N
S
E
T
GPIOB,GPIO_PIN_9,GPIO_PIN_SET
GP
I
OB
,
GP
I
O
P
I
N
9
,
GP
I
O
P
I
N
S
ET
#define LED1_OFF
HAL_GPIO_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
,
G
P
I
O
P
I
N
R
E
S
E
T
GPIOB,GPIO_PIN_9,GPIO_PIN_RESET
GP
I
OB
,
GP
I
O
P
I
N
9
,
GP
I
O
P
I
N
R
ESET
#define LED2_ON
HAL_GPIO_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
,
G
P
I
O
P
I
N
S
E
T
GPIOB,GPIO_PIN_8,GPIO_PIN_SET
GP
I
OB
,
GP
I
O
P
I
N
8
,
GP
I
O
P
I
N
S
ET
#define LED2_OFF
HAL_GPIO_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
,
G
P
I
O
P
I
N
R
E
S
E
T
GPIOB,GPIO_PIN_8,GPIO_PIN_RESET
GP
I
OB
,
GP
I
O
P
I
N
8
,
GP
I
O
P
I
N
R
ESET
uint8_t Tx_str1
= "hello world!\r\n"; uint8_t Tx_str2
= "LED1 Open!\r\n"; uint8_t Tx_str3
= "LED1 Closed!\r\n"; uint8_t Rx_dat = 0;void HAL_UART_RxCpltCallback
U
A
R
T
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
u
a
r
t
UART_HandleTypeDef *huart
U
A
R
T
H
an
d
l
e
T
y
p
eDe
f
∗
h
u
a
r
t
{ if
h
u
a
r
t
−
>
I
n
s
t
a
n
c
e
=
=
U
S
A
R
T
1
huart->Instance == USART1
h
u
a
r
t
−
>
I
n
s
t
an
ce
==
U
S
A
RT
1
{ if
R
x
d
a
t
=
=
0
x
a
1
Rx_dat == 0xa1
R
x
d
a
t
==
0
x
a
1
{ LED1_ON
; LED2_ON
; HAL_UART_Transmit(&huart1,Tx_str2,sizeof(Tx_str2),10000); LED2_OFF
; HAL_UART_Receive_IT(&huart1,&Rx_dat,1); } else if
R
x
d
a
t
=
=
0
x
a
2
Rx_dat == 0xa2
R
x
d
a
t
==
0
x
a
2
{ LED1_OFF
; LED2_ON
; HAL_UART_Transmit(&huart1,Tx_str3,sizeof(Tx_str3),10000); LED2_OFF
; HAL_UART_Receive_IT(&huart1,&Rx_dat,1); } } }//在mian
函数中添加以下代码: LED2_ON
; HAL_UART_Transmit(&huart1,Tx_str1,sizeof(Tx_str1),10000); //向上位机发送“hello world!” LED2_OFF
; HAL_UART_Receive_IT(&huart1,&Rx_dat,1); //启动串口1接收上位机1个字节
嵌入式&系统
# 嵌入式
刘航宇
3年前
2
1,162
18
【4】基于STM32CubeMX-STM32定时器开发
目录 定时器的基本概述 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)发出一个脉冲信号。 预分频器,本质上是一个加法计数器,预分频系数实际上就是加计数的溢出值。 定时器发生中断时间的计算方法: 定时时间 =
P
r
e
s
c
a
l
e
r
+
1
Prescaler+1
P
resc
a
l
er
+
1
X
C
o
u
n
t
e
r
P
e
r
i
o
d
+
1
Counter Period+1
C
o
u
n
t
er
P
er
i
o
d
+
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
T
I
M
H
a
n
d
l
e
T
y
p
e
D
e
f
∗
h
t
i
m
TIM_HandleTypeDef *htim
T
I
M
H
an
d
l
e
T
y
p
eDe
f
∗
h
t
im
{ if
h
t
i
m
−
>
I
n
s
t
a
n
c
e
=
=
T
I
M
2
htim->Instance == TIM2
h
t
im
−
>
I
n
s
t
an
ce
==
T
I
M
2
//处理TIM2间隔定时中断 { AL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
GPIOB,GPIO_PIN_9
GP
I
OB
,
GP
I
O
P
I
N
9
; } if
h
t
i
m
−
>
I
n
s
t
a
n
c
e
=
=
T
I
M
3
htim->Instance == TIM3
h
t
im
−
>
I
n
s
t
an
ce
==
T
I
M
3
//处理TIM3间隔定时中断 { HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
GPIOB,GPIO_PIN_8
GP
I
OB
,
GP
I
O
P
I
N
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 */
嵌入式&系统
# 嵌入式
刘航宇
3年前
2
1,087
26
2022-01-17
【3】基于STM32CubeMX-STM32中断系统
目录 中断什么意思 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
v
o
i
d
void
v
o
i
d
{ /*===================== 此处省略无关代码。 ======================*/ /* EXTI interrupt init 外部中断初始化*/ HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0); //设置EXIT9~EXIT5中断的优先级 HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); //使能EXIT9~EXIT5中断通道 HAL_NVIC_SetPriority
E
X
T
I
1
5
1
0
I
R
Q
n
,
0
,
0
EXTI15_10_IRQn, 0, 0
EXT
I
1
5
1
0
I
RQ
n
,
0
,
0
; HAL_NVIC_EnableIRQ
E
X
T
I
1
5
1
0
I
R
Q
n
EXTI15_10_IRQn
EXT
I
1
5
1
0
I
RQ
n
; }外部中断服务函数的编写 图片 实训案例:外部中断信号控制LED灯开关 在XMF07A或XMF07C开发板上, 利用STM32CubeMX和Keil5协同开发, 完成以下的功能: 【1】将KEY2,即PC13设置为外部中断输入,下降沿触发。在中断服务函数中,切换LED1的开关状态。 【2】将KEY4,即PB5设置为外部中断输入,上升沿触发。在中断服务函 数中,切换LED2的开关状态。 图片 //外部中断的初始化函数由STM32CubeMX辅助生成,用户只需要重写中断回调函数。 /* USER CODE BEGIN 0 */ void HAL_GPIO_EXTI_Callback
u
i
n
t
1
6
t
G
P
I
O
P
i
n
uint16_t GPIO_Pin
u
in
t
1
6
t
GP
I
O
P
in
{ if
G
P
I
O
P
i
n
=
=
G
P
I
O
P
I
N
1
3
GPIO_Pin == GPIO_PIN_13
GP
I
O
P
in
==
GP
I
O
P
I
N
1
3
{ HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
GPIOB,GPIO_PIN_9
GP
I
OB
,
GP
I
O
P
I
N
9
; } if
G
P
I
O
P
i
n
=
=
G
P
I
O
P
I
N
5
GPIO_Pin == GPIO_PIN_5
GP
I
O
P
in
==
GP
I
O
P
I
N
5
{ HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
GPIOB,GPIO_PIN_8
GP
I
OB
,
GP
I
O
P
I
N
8
; } } /* USER CODE END 0 */
嵌入式&系统
# 嵌入式
刘航宇
3年前
0
925
15
2022-01-17
【2】基于STM32CubeMX-STM32按键开发
目录 按键扫描的基本原理 GPIO输入电平读取HAL库函数 实训案例:按键控制LED灯开关 按键扫描的基本原理 按键信号是如何识别的? 一般来说,按键的两个引脚的一端通过电阻上拉到高电平,另一端则接地。 在没有按键按下的时候,输入引脚为高电平,当有按键按下,输入引脚则为低电平。通过反复读取按键输入引脚的信号,然后识别高低电平来判断是否有按键触发。 为什么去抖动? 按键的输入引脚有低电平产生不代表一定是有按键按下,也许是干扰信号 , 因此,需要通过去抖动处理,将这些干扰信号过滤,从而获得真实的按键触发信号。 如何去抖动? 首次检测到按键输入引脚有低电平后,稍作延时,再次读取该引脚,如还是低电平,则确认为按键触发信号;否则,判断为干扰信号,不予处理。 GPIO输入电平读取HAL库函数 GPIO_PinState HAL_GPIO_ReadPin
G
P
I
O
T
y
p
e
D
e
f
∗
G
P
I
O
x
,
u
i
n
t
1
6
t
G
P
I
O
P
i
n
GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin
GP
I
O
T
y
p
eDe
f
∗
GP
I
O
x
,
u
in
t
1
6
t
GP
I
O
P
in
; 参数1:GPIOx,端口号,如:GPIOB,GPIOF。 参数2:GPIO_Pin,引脚号,如:GPIO_PIN_9,GPIO_PIN_12。 返回值:GPIO_PinState,引脚的电平状态。 应用举例:判断PC13引脚的输入信号,若为高电平,则将PB9引脚控制的LED灯的开关状态切换。 if
H
A
L
G
P
I
O
R
e
a
d
P
i
n
(
G
P
I
O
C
,
G
P
I
O
P
I
N
1
3
HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13
H
A
L
G
P
I
O
R
e
a
d
P
in
(
GP
I
OC
,
GP
I
O
P
I
N
1
3
== GPIO_PIN_SET) { HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
GPIOB, GPIO_PIN_9
GP
I
OB
,
GP
I
O
P
I
N
9
; }实训案例:按键控制LED灯开关 要点提示配置输入引脚时候注意是否存在上拉 图片 在XMF07A或XMF07C开发板上, 利用STM32CubeMX和Keil5协同开发, 完成以下的功能: 【1】按下KEY2按键,切换LED1的开关状态。 【2】按下KEY3按键,松开后,切换LED2的开关状态。 【3】按下KEY4按键,把点亮的LED灯全部关闭。 图片 #define KEY2 HAL_GPIO_ReadPin
G
P
I
O
C
,
G
P
I
O
P
I
N
1
3
GPIOC,GPIO_PIN_13
GP
I
OC
,
GP
I
O
P
I
N
1
3
#define KEY3 HAL_GPIO_ReadPin
G
P
I
O
B
,
G
P
I
O
P
I
N
1
GPIOB,GPIO_PIN_1
GP
I
OB
,
GP
I
O
P
I
N
1
#define KEY4 HAL_GPIO_ReadPin
G
P
I
O
B
,
G
P
I
O
P
I
N
5
GPIOB,GPIO_PIN_5
GP
I
OB
,
GP
I
O
P
I
N
5
void Scan_Keys
{ if
K
E
Y
2
=
=
G
P
I
O
P
I
N
R
E
S
E
T
KEY2 == GPIO_PIN_RESET
K
E
Y
2
==
GP
I
O
P
I
N
R
ESET
{ HAL_Delay
5
5
5
; if
K
E
Y
2
=
=
G
P
I
O
P
I
N
R
E
S
E
T
KEY2 == GPIO_PIN_RESET
K
E
Y
2
==
GP
I
O
P
I
N
R
ESET
{ HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
GPIOB,GPIO_PIN_9
GP
I
OB
,
GP
I
O
P
I
N
9
; while
K
E
Y
2
=
=
G
P
I
O
P
I
N
R
E
S
E
T
KEY2 == GPIO_PIN_RESET
K
E
Y
2
==
GP
I
O
P
I
N
R
ESET
; } } if
K
E
Y
3
=
=
R
E
S
E
T
KEY3 == RESET
K
E
Y
3
==
RESET
{ HAL_Delay
5
5
5
; if
K
E
Y
3
=
=
R
E
S
E
T
KEY3 == RESET
K
E
Y
3
==
RESET
{ while
K
E
Y
3
=
=
R
E
S
E
T
KEY3 == RESET
K
E
Y
3
==
RESET
; HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
GPIOB,GPIO_PIN_8
GP
I
OB
,
GP
I
O
P
I
N
8
; } } if
K
E
Y
4
=
=
0
KEY4 == 0
K
E
Y
4
==
0
{ HAL_Delay
5
5
5
; if
K
E
Y
4
=
=
0
KEY4 == 0
K
E
Y
4
==
0
{ HAL_GPIO_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
∣
G
P
I
O
P
I
N
8
,
G
P
I
O
P
I
N
R
E
S
E
T
GPIOB,GPIO_PIN_9|GPIO_PIN_8,GPIO_PIN_RESET
GP
I
OB
,
GP
I
O
P
I
N
9
∣
GP
I
O
P
I
N
8
,
GP
I
O
P
I
N
R
ESET
; while
K
E
Y
2
=
=
0
KEY2 == 0
K
E
Y
2
==
0
; } } }/* Infinite loop */ /* USER CODE BEGIN WHILE */ while
1
1
1
{ Scan_Keys
; /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
嵌入式&系统
# 嵌入式
刘航宇
3年前
7
1,806
87
2022-01-16
【1】基于STM32CubeMX-STM32GPIO端口开发
目录 STM32CubeMX的声明 protuesSTM32仿真开发板下载 STM32的GPIO端口知识要点 GPIO电平输出HAL库函数 GPIO电平翻转HAL库函数 GPIO初始化函数源码剖析 实训案例:基于STM32CubeMX的跑马灯 仿真开发实例 STM32CubeMX的声明 【1】STM32CubeMX 是一个代码辅助生成工具,在生成工程代码的同时,根据你的功能选择和配置帮你做了必要的初始化,这也就意味着你不再需要对底层的特殊功能寄存器进行初 始化配置,但是不代表你就可以不去学习 STM32 的基本知识和各个外设的工作原理与参数 特性等。 【2】虽然不再要求你去了解 STM32 底层寄存器的定义,但却要求你要了解由 STM32CubeMX生产的代码内在的逻辑联系以及 HAL 库中的常用函数原型与使用。 【3】STM32CubeMX 的最大好处就是,使开发流程、文件结构、库函数等标准化 protuesSTM32仿真开发板下载 protues STM32C8开发板 下载地址:https://wwu.lanzoub.com/ixmmx04hidud 提取码: 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_WritePin
G
P
I
O
T
y
p
e
D
e
f
∗
G
P
I
O
x
,
u
i
n
t
1
6
t
G
P
I
O
P
i
n
,
G
P
I
O
P
i
n
S
t
a
t
e
P
i
n
S
t
a
t
e
GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState
GP
I
O
T
y
p
eDe
f
∗
GP
I
O
x
,
u
in
t
1
6
t
GP
I
O
P
in
,
GP
I
O
P
in
St
a
t
e
P
in
St
a
t
e
; 参数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_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
,
G
P
I
O
P
I
N
S
E
T
GPIOB, GPIO_PIN_8, GPIO_PIN_SET
GP
I
OB
,
GP
I
O
P
I
N
8
,
GP
I
O
P
I
N
S
ET
;GPIO电平翻转HAL库函数 void HAL_GPIO_TogglePin
G
P
I
O
T
y
p
e
D
e
f
∗
G
P
I
O
x
,
u
i
n
t
1
6
t
G
P
I
O
P
i
n
GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin
GP
I
O
T
y
p
eDe
f
∗
GP
I
O
x
,
u
in
t
1
6
t
GP
I
O
P
in
; 参数1:GPIOx,端口号,如:GPIOB,GPIOF。 参数2:GPIO_Pin,引脚号,如:GPIO_PIN_9,GPIO_PIN_12。 返回值:void,空。 应用举例:将PA3引脚输出电平翻转。 HAL_GPIO_TogglePin
G
P
I
O
A
,
G
P
I
O
P
I
N
3
GPIOA, GPIO_PIN_3
GP
I
O
A
,
GP
I
O
P
I
N
3
;GPIO初始化函数源码剖析 void MX_GPIO_Init
v
o
i
d
void
v
o
i
d
{ 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
G
P
I
O
B
,
G
P
I
O
P
I
N
8
∣
G
P
I
O
P
I
N
9
,
G
P
I
O
P
I
N
R
E
S
E
T
GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_RESET
GP
I
OB
,
GP
I
O
P
I
N
8
∣
GP
I
O
P
I
N
9
,
GP
I
O
P
I
N
R
ESET
; /*配置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
1
1
{ HAL_GPIO_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
,
G
P
I
O
P
I
N
S
E
T
GPIOB,GPIO_PIN_9,GPIO_PIN_SET
GP
I
OB
,
GP
I
O
P
I
N
9
,
GP
I
O
P
I
N
S
ET
; //向PB9输出高电平,点亮LED1灯 HAL_Delay
500
500
500
; //延时500ms HAL_GPIO_WritePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
,
G
P
I
O
P
I
N
S
E
T
GPIOB,GPIO_PIN_8,GPIO_PIN_SET
GP
I
OB
,
GP
I
O
P
I
N
8
,
GP
I
O
P
I
N
S
ET
; //向PB9输出低电平,熄灭LED1灯 HAL_Delay
500
500
500
; //延时500ms HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
9
GPIOB,GPIO_PIN_9
GP
I
OB
,
GP
I
O
P
I
N
9
; //用翻转电平的方式,实现LED1灯的点亮与熄灭 HAL_Delay
500
500
500
; //延时500ms HAL_GPIO_TogglePin
G
P
I
O
B
,
G
P
I
O
P
I
N
8
GPIOB,GPIO_PIN_8
GP
I
OB
,
GP
I
O
P
I
N
8
; //用翻转电平的方式,实现LED2灯的点亮与熄灭 HAL_Delay
500
500
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
G
P
I
O
A
,
G
P
I
O
P
I
N
5
GPIOA,GPIO_PIN_5
GP
I
O
A
,
GP
I
O
P
I
N
5
; HAL_Delay
0
X
F
F
0XFF
0
XFF
l1p0sq2c.png图片 将hex文件下载到proteus8中去 l1p0t26z.png图片 此时仿真会报错,原因是电源为接入,只需要将电源接入即可 l1p0tjr9.png图片 此处VDDA,VSSA分别加入到电网VCC与GND中,详细可以百度 l1p0tpli.png图片 最后,就可以看到有一个LED灯反复交替闪烁! l1p0u3mg.png图片
嵌入式&系统
# 嵌入式
刘航宇
3年前
8
5,641
115
嵌入式视频流知识点及代码解析-精简版
目录 背景和意义 框架 代码及相关知识点 一、知识点篇 二、问答篇 三、代码篇RTSP程序要不要等待播放器器程序请求? 请你找出上述代码所在位置 live555(了解) SDL 背景和意义 (1)视频的带宽很大,存储,传输不便,故要压缩、解压 、播放。 (2)应用领域很广 ,交通,在线教育,播放器,自动驾驶。 框架 图片 下面这个图及其重要以及3个ip关系 abcde代表先后实现顺序注意观看!!! TO67M8.png图片 代码及相关知识点 一、知识点篇 Live 555: 是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了标准流媒体传输,对标准流媒体传输协议如RTP/RTCP、RTSP、SIP等的支持。Live555实现了对多种音视频编码格式的音视频数据的流化、接收和处理等支持,包括MPEG、H.263+、DV、JPEG视频和多种音频编码。同时由于良好的设计,Live555非常容易扩展对其他格式的支持。Live555已经被用于多款播放器的流媒体播放功能的实现,如VLC
V
i
d
e
o
L
a
n
VideoLan
Vi
d
eo
L
an
、MPlayer。 在本次开发实践中主要用于接收海康威视摄像头的RTP数据包 并通过UDP网络进行转发给PC机。 FFmpeg: Fmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多code都是从头开发的。 FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。项目的名称来自MPEG视频编码标准,前面的"FF"代表"Fast Forward"。 在本次开发实践中主要用于H264数据解码。 SDL: SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。在本次开发实践中主要用于YUV数据的显示。 二、问答篇 1.什么是YUV?与RGB有什么不同? YUV 是一种颜色编码方法,FFmpeg 解码后的数据格式。Y 表示明亮度 U 表示色 度 V 表示浓度。因为通过研究发现,人类对于图像的感知中对明亮最侧重,色彩和 浓度就相对不那么重要,所以在保存图片时,让明亮度占较多的比重,有效的在不影 响观看的情况下节约了空间。视频播放器解码出来的格式为 YUV420P,其中明亮度 占整个数据的 2/3,色度和浓度占 1/3。 2.RTSP在什么层?答:应用层 3.你关于IP问题你用到了那些命令?答:ipconfig,ifconfig,ping等 4.本次课程设计你用到了那些去年学的知识?答:网络通信,文件开关与读写,第一章shell操作命令 下面部分自行百度: 5.TCP与UDP特点(TCP的可靠,UDP 的不可靠,UDP快) 6.简述TCP与UDP 7.了解HTTP/https 8.三次握手和四次挥手过程 三、代码篇 RTSP程序要不要等待播放器器程序请求? 答:要 请你找出上述代码所在位置 答:如图所示 图片 live555(了解) BasicTaskScheduler 的父类是BasicTaskScheduler0 BasicTaskScheduler0是一个用作传递的类,它继承自TaskScheduler,又派生出BasicTaskScheduler。其定义在live555sourcecontrol\UsageEnvironment\include\BasicUsageEnvironment0.hh文件中。 BasicTaskScheduler0中有 BasicTaskScheduler 这个类主要实现事件的处理 BasicUsageEnvironment 涉及调试语句,输出语句 ourRTSPClient 主要是涉及的数据的发送相关的功能函数,主要的功能继承于父类RTSPClient RequestRecord 创建一个请求记录对象,并将回调函数与之关联 sendRequest 第一次进入调用:openConnection解析URL,并调用setupStreamSocket和connectToServer,然后 envir
.taskScheduler
.setBackgroundHandling
f
I
n
p
u
t
S
o
c
k
e
t
N
u
m
,
S
O
C
K
E
T
R
E
A
D
A
B
L
E
∣
S
O
C
K
E
T
E
X
C
E
P
T
I
O
N
,
(
T
a
s
k
S
c
h
e
d
u
l
e
r
:
:
B
a
c
k
g
r
o
u
n
d
H
a
n
d
l
e
r
P
r
o
c
∗
fInputSocketNum, SOCKET_READABLE|SOCKET_EXCEPTION, (TaskScheduler::BackgroundHandlerProc*
f
I
n
p
u
tS
oc
k
e
tN
u
m
,
SOC
K
E
T
R
E
A
D
A
B
L
E
∣
SOC
K
E
T
E
XCEPT
I
ON
,
(
T
a
s
k
S
c
h
e
d
u
l
er
::
B
a
c
k
g
ro
u
n
d
H
an
d
l
er
P
roc
∗
&incomingDataHandler, this); 若不是第一次调用,则打包 RTSP 包协议,并调用 Send函数发送到服务器。 重点 RequestRecord入队列 等候读取数据。 setupStreamSocket调用createSocket ,createSocket调用: sock = socket
A
F
I
N
E
T
,
t
y
p
e
,
0
AF_INET, type, 0
A
F
I
NET
,
t
y
p
e
,
0
; connectToServer调用 connect
s
o
c
k
e
t
N
u
m
,
(
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
socketNum, (struct sockaddr*
soc
k
e
tN
u
m
,
(
s
t
r
u
c
t
soc
ka
dd
r
∗
&remoteName, sizeof remoteName) setBackgroundHandling 主要初始化select 函数的 文件描述符集合 incomingDataHandler 中包含函数 readSocket 调用 int bytesRead = recvfrom
s
o
c
k
e
t
,
(
c
h
a
r
∗
socket, (char*
soc
k
e
t
,
(
c
ha
r
∗
buffer, bufferSize, 0,
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
struct sockaddr*
s
t
r
u
c
t
soc
ka
dd
r
∗
&fromAddress, &addressSize);到此发送描述命令结束 ,等待响应服务器发送过来的数据。 incomingDataHandler1读取服务器的数据 incomingDataHandler1先调用readSocket调用,然后调用 handleResponseBytes 解析RTSP服务器数据 调用
∗
f
o
u
n
d
R
e
q
u
e
s
t
−
>
h
a
n
d
l
e
r
(
*foundRequest->handler(
∗
f
o
u
n
d
R
e
q
u
es
t
−
>
han
d
l
er
(
)
t
h
i
s
,
r
e
s
u
l
t
C
o
d
e
,
r
e
s
u
l
t
S
t
r
i
n
g
this, resultCode, resultString
t
hi
s
,
res
u
ltC
o
d
e
,
res
u
ltSt
r
in
g
; 这个函数就是continueAfterDESCRIBE continueAfterDESCRIBE 调用下一步 setup 功能 SDL 抓1,2,3,4....等每段的关键句,可能考流程 rb为只读,对于不需要进行更新的文件,可以防止用户的错误的写回操作,防止损毁原有数据。具有较高的安全性。 rb+为更新二进制文件,可以读取,同时也可以写入,需要用到fseek之类的函数进行配合,以免出错,对于需要不时更新的文件,比如信息管理系统中的数据,可以这样打开。 初始化SDL 使用SDL_Init
初始化SDL。该函数可以确定希望激活的子系统。 int SDLCALL SDL_Init
U
i
n
t
32
f
l
a
g
s
Uint32 flags
U
in
t
32
f
l
a
g
s
SDL_INIT_VIDEO:视频 创建窗口(Window) 使用SDL_CreateWindow
创建一个用于视频播放的窗口。 SDL_Window SDLCALL SDL_CreateWindow
c
o
n
s
t
c
h
a
r
t
i
t
l
e
,
i
n
t
x
,
i
n
t
y
,
i
n
t
w
,
i
n
t
h
,
U
i
n
t
32
f
l
a
g
s
const char title,int x, int y, int w,int h, Uint32 flags
co
n
s
t
c
ha
r
t
i
tl
e
,
in
t
x
,
in
t
y
,
in
tw
,
in
t
h
,
U
in
t
32
f
l
a
g
s
; SDL_CreatWindow:第一个参数是窗口名字,第二三是窗口的坐标(SDL_winpos_undefined 为采用系统默认) title :窗口标题 x :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。 y :窗口位置y坐标。同上。 w :窗口的宽 h :窗口的高 flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。 flags:SDL_WINDOW_RESIZABLE 自动调整窗口 基于窗口创建渲染器(Render) 使用SDL_CreateRenderer
基于窗口创建渲染器 SDL_Renderer SDLCALL SDL_CreateRenderer
S
D
L
W
i
n
d
o
w
w
i
n
d
o
w
,
i
n
t
i
n
d
e
x
,
U
i
n
t
32
f
l
a
g
s
SDL_Window window,int index, Uint32 flags
S
D
L
W
in
d
o
ww
in
d
o
w
,
in
t
in
d
e
x
,
U
in
t
32
f
l
a
g
s
; window: 渲染的目标窗口。 index:打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备。 SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步 创建纹理(Texture) 使用SDL_CreateTexture
基于渲染器创建一个纹理 SDL_Texture SDLCALL SDL_CreateTexture
S
D
L
R
e
n
d
e
r
e
r
r
e
n
d
e
r
e
r
,
U
i
n
t
32
f
o
r
m
a
t
,
i
n
t
a
c
c
e
s
s
,
i
n
t
w
,
i
n
t
h
SDL_Renderer renderer,Uint32 format,int access, int w,int h
S
D
L
R
e
n
d
ererre
n
d
erer
,
U
in
t
32
f
or
ma
t
,
in
t
a
ccess
,
in
tw
,
in
t
h
; renderer:目标渲染器。 format:纹理的格式。 access:定义位于SDL_TextureAccess中 access:SDL_TEXTUREACCESS_STREAMING :变化频繁 w:纹理的宽 h:纹理的高 SDL_Thread *refresh_thread = SDL_CreateThread
R
e
f
r
e
s
h
V
i
d
e
o
,
N
U
L
L
,
N
U
L
L
RefreshVideo,NULL,NULL
R
e
f
res
hVi
d
eo
,
N
ULL
,
N
ULL
; //创建线程 SDL_Event event; //设置触发事件 6.在SDL中,当事件等待函数监听到事件后,判断事件类型,如果event.type == SDL_KEYDOWN,表明用户按下键盘,保存在event.key.keysym.sym是相应的键值。而根据键值,调用函数SDL_GetKeyName
e
v
e
n
t
.
k
e
y
.
k
e
y
s
y
m
.
s
y
m
event.key.keysym.sym
e
v
e
n
t
.
k
ey
.
k
eysy
m
.
sy
m
),即可得到按下的按键键名。 SDLK_RETURN == 13 回车! 7.读文件fread size_t fread
v
o
i
d
b
u
f
f
e
r
,
s
i
z
e
t
s
i
z
e
,
s
i
z
e
t
c
o
u
n
t
,
F
I
L
E
s
t
r
e
a
m
void buffer,size_t size,size_t count,FILE stream
v
o
i
d
b
u
ff
er
,
s
i
z
e
t
s
i
ze
,
s
i
z
e
t
co
u
n
t
,
F
I
L
E
s
t
re
am
buffer 是读取的数据存放的内存的指针(可以是数组,也可以是新开辟的空间,buffer就是一个索引) size 是每次读取的字节数 count 是读取次数 stream 是要读取的文件的指针 8.循环显示画面
1
1
1
设置纹理的数据 使用SDL_UpdateTexture
设置纹理的像素数据 int SDLCALL SDL_UpdateTexture
S
D
L
T
e
x
t
u
r
e
t
e
x
t
u
r
e
,
c
o
n
s
t
S
D
L
R
e
c
t
r
e
c
t
,
c
o
n
s
t
v
o
i
d
∗
p
i
x
e
l
s
,
i
n
t
p
i
t
c
h
SDL_Texture texture,const SDL_Rect rect,const void *pixels, int pitch
S
D
L
T
e
x
t
u
re
t
e
x
t
u
re
,
co
n
s
tS
D
L
R
ec
t
rec
t
,
co
n
s
t
v
o
i
d
∗
p
i
x
e
l
s
,
in
tp
i
t
c
h
; texture:目标纹理。 rect:更新像素的矩形区域。设置为NULL的时候更新整个区域。 pixels:像素数据。 pitch:一行像素数据的字节数。
2
2
2
纹理复制给渲染目标 使用SDL_RenderCopy
将纹理数据复制给渲染目标。在使用SDL_RenderCopy
之前,可以使用SDL_RenderClear
先使用清空渲染目标。实际上视频播放的时候不使用SDL_RenderClear
也是可以的,因为视频的后一帧会完全覆盖前一帧 int SDLCALL SDL_RenderCopy
S
D
L
R
e
n
d
e
r
e
r
r
e
n
d
e
r
e
r
,
S
D
L
T
e
x
t
u
r
e
t
e
x
t
u
r
e
,
c
o
n
s
t
S
D
L
R
e
c
t
s
r
c
r
e
c
t
,
c
o
n
s
t
S
D
L
R
e
c
t
d
s
t
r
e
c
t
SDL_Renderer renderer,SDL_Texture texture,const SDL_Rect srcrect,const SDL_Rect dstrect
S
D
L
R
e
n
d
ererre
n
d
erer
,
S
D
L
T
e
x
t
u
re
t
e
x
t
u
re
,
co
n
s
tS
D
L
R
ec
t
srcrec
t
,
co
n
s
tS
D
L
R
ec
t
d
s
t
rec
t
; renderer:渲染目标。 texture:输入纹理。 srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入。 dstrect:选择渲染目标的一块矩形区域作为输出。设置为NULL的时候整个渲染目标作为输出。
3
3
3
显示 使用SDL_RenderPresent
显示画面 void SDLCALL SDL_RenderPresent
S
D
L
R
e
n
d
e
r
e
r
∗
r
e
n
d
e
r
e
r
SDL_Renderer * renderer
S
D
L
R
e
n
d
erer
∗
re
n
d
erer
; 参数renderer用于指定渲染目标 9.退出 event.type==SDL_QUIT fclose
f
p
fp
f
p
; //关闭文件! 防止遗留无用进程 SDL_DestroyTexture
t
e
x
t
u
r
e
texture
t
e
x
t
u
re
; //关闭纹理 SDL_DestroyRenderer
r
e
n
d
e
r
e
r
renderer
re
n
d
erer
; //关闭渲染 SDL_DestroyWindow
w
i
n
d
o
w
window
w
in
d
o
w
; //关闭窗口 SDL_Quit
; //退出线程 FFMPEG解码deco fun_deco_display.c 从void * deco_thread开始 play_ubuntu内 功能函数于主函数同时编译 多个c编译! 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 AVCodec codec; / 解码CODEC*/ AVCodecContext *cctx; AVFrame frame; / 解码后的图像*/ int byte_buffer_size; //解码器码流长度 uint8_t *byte_buffer = NULL; //h.264码流 AVPacket *pkt; //保存媒体流信息 AVPacket主要保存一些媒体流的基本信息,例如PTS、DTS时间。最重要的当然就是媒体数据的buffer地址了。 比较重要的有: pts:控制显示的pts时间 dts:控制解码的dts时间 *data:媒体数据buffer的指针 duration:AVStream-> time_base单位中此数据包的持续时间,如果未知则为0。 在演示顺序中等于next_pts - this_pts。 AVFormatContext主要存储视音频封装格式中包含的信息 解码前数据:AVPacket 解码后数据:AVFrame 1.AVFormatContext avformat_alloc_context
v
o
i
d
void
v
o
i
d
函数用来申请AVFormatContext类型变量并初始化默认参数。申请的空间通过void avformat_free_context
A
V
F
o
r
m
a
t
C
o
n
t
e
x
t
s
AVFormatContext s
A
V
F
or
ma
tC
o
n
t
e
x
t
s
函数释放。 2.avformat_find_stream_info
主要用于给每个媒体流(音频/视频)的AVStream结构体赋值,已经实现了解码器的查找,解码器的打开,视音频帧的读取,视音频帧的解码等工作 3.av_find_best_stream()函数就是要获取音视频对应的stream_index 获取流的索引 4.解码模块第一步:获取解码器 avcodec_find_decoder()FFmpeg的解码器编码器都存在avcodec的结构体中 5.avcodec_alloc_context3,avcodec_parameters_to_context,解码器初始化 6.avcodec_open2打开解码器 7.av_frame_alloc
首先调用av_mallocz
为AVFrame结构体分配内存 8.int av_image_get_buffer_size
e
n
u
m
A
V
P
i
x
e
l
F
o
r
m
a
t
p
i
x
f
m
t
,
i
n
t
w
i
d
t
h
,
i
n
t
h
e
i
g
h
t
,
i
n
t
a
l
i
g
n
enum AVPixelFormat pix_fmt, int width, int height, int align
e
n
u
m
A
V
P
i
x
e
lF
or
ma
tp
i
x
f
m
t
,
in
tw
i
d
t
h
,
in
t
h
e
i
g
h
t
,
in
t
a
l
i
g
n
;函数的作用是通过指定像素格式、图像宽、图像高来计算所需的内存大小,av_malloc 按需分配空间 9.av_packet_alloc实际是分配AVPacket以后,调用av_init_packet对AVPacket的成员变量进行初始化赋值 10.av_read_frame
的作用是读取码流中的音频若干帧或者视频一帧 11.avcodec_send_packet()以在AVPacket中给出解码器原始的压缩数据 avcodec_receive_frame()。 成功后,它将返回一个包含未压缩音频或视频数据的 AVFrame 12. void av_image_copy_uc_from
u
i
n
t
8
t
∗
d
s
t
d
a
t
a
[
4
]
,
c
o
n
s
t
p
t
r
d
i
f
f
t
d
s
t
l
i
n
e
s
i
z
e
s
[
4
]
,
c
o
n
s
t
u
i
n
t
8
t
∗
s
r
c
d
a
t
a
[
4
]
,
c
o
n
s
t
p
t
r
d
i
f
f
t
s
r
c
l
i
n
e
s
i
z
e
s
[
4
]
,
e
n
u
m
A
V
P
i
x
e
l
F
o
r
m
a
t
p
i
x
f
m
t
,
i
n
t
w
i
d
t
h
,
i
n
t
h
e
i
g
h
t
uint8_t * dst_data[4], const ptrdiff_t dst_linesizes[4], const uint8_t * src_data[4], const ptrdiff_t src_linesizes[4], enum AVPixelFormat pix_fmt, int width, int height
u
in
t
8
t
∗
d
s
t
d
a
t
a
[
4
]
,
co
n
s
tpt
r
d
i
f
f
t
d
s
t
l
in
es
i
zes
[
4
]
,
co
n
s
t
u
in
t
8
t
∗
sr
c
d
a
t
a
[
4
]
,
co
n
s
tpt
r
d
i
f
f
t
sr
c
l
in
es
i
zes
[
4
]
,
e
n
u
m
A
V
P
i
x
e
lF
or
ma
tp
i
x
f
m
t
,
in
tw
i
d
t
h
,
in
t
h
e
i
g
h
t
数据拷贝 13.av_packet_free(&pkt); //释放数据 关闭进程 av_frame_free(&frame); avformat_close_input(&fctx); avcodec_free_context(&cctx); avformat_free_context
f
c
t
x
fctx
f
c
t
x
; Client && Server(了解) //Client_Upd 1.int socket
i
n
t
d
o
m
a
i
n
,
i
n
t
t
y
p
e
,
i
n
t
p
r
o
t
o
c
o
l
int domain, int type, int protocol
in
t
d
o
main
,
in
tt
y
p
e
,
in
tp
ro
t
oco
l
; 函数socket
的参数domain用于设置网络通信的域,函数socket
根据这个参数选择通信协议的族 SOCK_DGRAM udp连接 socket
函数的原型如下,这个函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。 2.memset可以方便的清空一个结构类型的变量或数组。初始化 3.family 通信协议的族 AF_INET,PF_INET IPv4 Internet协议 4.sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中。 htons
作用是将端口号由主机字节序转换为网络字节序的整数值。
h
o
s
t
t
o
n
e
t
host to net
h
os
tt
o
n
e
t
inet_addr
作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。 5.在无连接的数据报socket方式下,由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址,sendto()函数原型为: int sendto(int sockfd, const void msg,int len unsigned int flags, const struct sockaddr to, int tolen); 该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。 int PASCAL FAR sendto
S
O
C
K
E
T
s
,
c
o
n
s
t
c
h
a
r
F
A
R
∗
b
u
f
,
i
n
t
l
e
n
,
i
n
t
f
l
a
g
s
,
c
o
n
s
t
s
t
r
u
c
t
s
o
c
k
a
d
d
r
F
A
R
∗
t
o
,
i
n
t
t
o
l
e
n
SOCKET s, const char FAR* buf, int len, int flags, const struct sockaddr FAR* to, int tolen
SOC
K
ET
s
,
co
n
s
t
c
ha
r
F
A
R
∗
b
u
f
,
in
tl
e
n
,
in
t
f
l
a
g
s
,
co
n
s
t
s
t
r
u
c
t
soc
ka
dd
r
F
A
R
∗
t
o
,
in
tt
o
l
e
n
; s:一个标识套接口的描述字。 buf:包含待发送数据的缓冲区。 len:buf缓冲区中数据的长度。 flags:调用方式标志位。 to:(可选)指针,指向目的套接口的地址。 tolen:to所指地址的长度 6.recvfrom()函数原型为: int recvfrom(int sockfd,void buf,int len,unsigned int lags,struct sockaddr from,int *fromlen); from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数 接收一个数据报并保存源地址。 int PASCAL FAR recvfrom
S
O
C
K
E
T
s
,
c
h
a
r
F
A
R
∗
b
u
f
,
i
n
t
l
e
n
,
i
n
t
f
l
a
g
s
,
s
t
r
u
c
t
s
o
c
k
a
d
d
r
F
A
R
f
r
o
m
,
i
n
t
F
A
R
f
r
o
m
l
e
n
SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR from, int FAR fromlen
SOC
K
ET
s
,
c
ha
r
F
A
R
∗
b
u
f
,
in
tl
e
n
,
in
t
f
l
a
g
s
,
s
t
r
u
c
t
soc
ka
dd
r
F
A
R
f
ro
m
,
in
tF
A
R
f
ro
m
l
e
n
; s:标识一个已连接套接口的描述字。 buf:接收数据缓冲区。 len:缓冲区长度。 flags:调用操作方式。 from:(可选)指针,指向装有源地址的缓冲区。 fromlen:(可选)指针,指向from缓冲区长度值。 https://blog.csdn.net/qq_26399665/article/details/52426529 //sendto/recvfrom 7.fwrite
c
o
n
s
t
v
o
i
d
b
u
f
f
e
r
,
s
i
z
e
t
s
i
z
e
,
s
i
z
e
t
c
o
u
n
t
,
F
I
L
E
s
t
r
e
a
m
const voidbuffer,size_t size,size_t count,FILEstream
co
n
s
t
v
o
i
d
b
u
ff
er
,
s
i
z
e
t
s
i
ze
,
s
i
z
e
t
co
u
n
t
,
F
I
L
E
s
t
re
am
; (1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。 (2)size:要写入的字节数; (3)count:要进行写入size字节的数据项的个数; (4)stream:目标文件指针。 //Server_Udp(了解) 1.FILE fopen
c
h
a
r
p
a
t
h
,
c
h
a
r
∗
m
o
d
e
char path, char * mode
c
ha
r
p
a
t
h
,
c
ha
r
∗
m
o
d
e
; path为包含了路径的文件名,mode为文件打开方式 2.bind
函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。 int bind
i
n
t
s
o
c
k
f
d
,
c
o
n
s
t
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
a
d
d
r
,
s
o
c
k
l
e
n
t
a
d
d
r
l
e
n
int sockfd, const struct sockaddr *addr, socklen_t addrlen
in
t
soc
k
fd
,
co
n
s
t
s
t
r
u
c
t
soc
ka
dd
r
∗
a
dd
r
,
soc
k
l
e
n
t
a
dd
r
l
e
n
; 第二个参数是一个指向特定协议的地址结构的指针,第三个参数是该地址结构的长度。 3.size_t fread
v
o
i
d
b
u
f
f
e
r
,
s
i
z
e
t
s
i
z
e
,
s
i
z
e
t
c
o
u
n
t
,
F
I
L
E
s
t
r
e
a
m
void buffer,size_t size,size_t count,FILE stream
v
o
i
d
b
u
ff
er
,
s
i
z
e
t
s
i
ze
,
s
i
z
e
t
co
u
n
t
,
F
I
L
E
s
t
re
am
buffer 是读取的数据存放的内存的指针(可以是数组,也可以是新开辟的空间,buffer就是一个索引) size 是每次读取的字节数 count 是读取次数 ,k stream 是要读取的文件的指针
嵌入式&系统
# 嵌入式
刘航宇
3年前
0
870
5
2021-12-28
嵌入式
嵌入式
嵌入式
使用apt安装命令遇到Could not get lock /var/lib/dpkg/lock解决方案
一、问:使用 apt-get 命令的时候,遇到这种错误咋办? E: Could not get lock /var/lib/dpkg/lock - open
11
:
R
e
s
o
u
r
c
e
t
e
m
p
o
r
a
r
i
l
y
u
n
a
v
a
i
l
a
b
l
e
11: Resource temporarily unavailable
11
:
R
eso
u
rce
t
e
m
p
or
a
r
i
l
y
u
na
v
ai
l
ab
l
e
E: Unable to lock the administration directory
/
v
a
r
/
l
i
b
/
d
p
k
g
/
/var/lib/dpkg/
/
v
a
r
/
l
ib
/
d
p
k
g
/
, is another process using it? T6i3P1.png图片 二、回答: 1、找出并杀掉所有 apt-get 或者 apt 进程 ps aux|grep apt 2、 删除锁定的文件 锁定的文件会阻止 Linux 系统中某些文件或者数据的访问,这个概念也存在于 Windows 或者其他的操作系统中。 一旦你运行了 apt-get 或者 apt 命令,锁定文件将会创建于 /var/lib/apt/lists/、/var/lib/dpkg/、/var/cache/apt/archives/ 中。 这有助于运行中的 apt-get 或者 apt 进程能够避免被其它需要使用相同文件的用户或者系统进程所打断。当该进程执行完毕后,锁定文件将会删除。 重要提醒:万一你在没有看到 apt-get 或者 apt 进程的情况下在上面两个不同的文件夹中看到了锁定文件,这是因为进程由于某个原因被杀掉了,因此你需要删除锁定文件来避免该错误。 首先运行下面的命令来移除 /var/lib/dpkg/ 文件夹下的锁定文件: sudo rm /var/lib/dpkg/lock 之后像下面这样强制重新配置软件包: sudo dpkg --configure -a
嵌入式&系统
# 嵌入式
刘航宇
4年前
0
359
2
嵌入式视频流指南-2021
总体框架,摄像头接线,你应达到什么效果? 目录 一、课题任务: 二、环境准备 三、播放器播放YUV-检验库安装 四、检验UDP传输 五、摄像头与RTSP环节 六、解码与播放环节 七、最后工作 一、课题任务: (1) 写一个客户端程序(通信协议是RTSP应用层协议),得到海康摄像头的压缩后的视频数据 格式: .h264 live555 给你一个海康摄像头,写一个客户端程序,使用类库live555 得到实时的视频流(压缩过的.h264).
2
2
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
v
o
i
d
∗
d
a
t
a
void*data
v
o
i
d
∗
d
a
t
a
{ i ++; cout<<"RefreshVideo i = " << i <<endl; } void SdlThread
{ const int bpp = 12; unsigned char buffer
P
I
X
E
L
W
∗
P
I
X
E
L
H
∗
b
p
p
/
8
PIXEL_W*PIXEL_H*bpp/8
P
I
XE
L
W
∗
P
I
XE
L
H
∗
b
pp
/8
; SDL_Rect rect; rect.x = 0; rect.y = 0; rect.w = SCREEN_W; rect.h = SCREEN_H; FILE *fp = fopen
"
.
/
l
h
y
.
y
u
v
"
,
"
r
b
"
"./lhy.yuv","rb"
"./
l
h
y
.
y
uv
"
,
"
r
b
"
; if
f
p
=
=
N
U
L
L
fp == NULL
f
p
==
N
ULL
{ cout<<" open lhy.yuv failure "<<endl; return ; } if
S
D
L
I
n
i
t
(
S
D
L
I
N
I
T
V
I
D
E
O
SDL_Init(SDL_INIT_VIDEO
S
D
L
I
ni
t
(
S
D
L
I
N
I
T
V
I
D
EO
) { SDL_Log
"
U
n
a
b
l
e
t
o
i
n
i
t
i
a
l
i
z
e
S
D
L
:
"Unable to initialize SDL:%s",SDL_GetError(
"
U
nab
l
e
t
o
ini
t
ia
l
i
ze
S
D
L
:
); return ; } SDL_Window *window; window= SDL_CreateWindow
"
P
e
r
s
o
n
N
e
t
w
o
r
k
P
l
a
y
e
r
"
,
S
D
L
W
I
N
D
O
W
P
O
S
U
N
D
E
F
I
N
E
D
,
S
D
L
W
I
N
D
O
W
P
O
S
U
N
D
E
F
I
N
E
D
,
S
C
R
E
E
N
W
,
S
C
R
E
E
N
H
,
S
D
L
W
I
N
D
O
W
O
P
E
N
G
L
∣
S
D
L
W
I
N
D
O
W
R
E
S
I
Z
A
B
L
E
"Person Network Player",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,SCREEN_W,SCREEN_H,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE
"
P
erso
n
N
e
tw
or
k
Pl
a
yer
"
,
S
D
L
W
I
N
D
O
W
PO
S
U
N
D
EF
I
NE
D
,
S
D
L
W
I
N
D
O
W
PO
S
U
N
D
EF
I
NE
D
,
SCREE
N
W
,
SCREE
N
H
,
S
D
L
W
I
N
D
O
W
O
PENG
L
∣
S
D
L
W
I
N
D
O
W
R
ES
I
Z
A
B
L
E
; if
w
i
n
d
o
w
=
=
N
U
L
L
window == NULL
w
in
d
o
w
==
N
ULL
{ SDL_Log
"
C
o
u
l
d
n
o
t
c
r
e
a
t
e
w
i
n
d
o
w
:
"Could not create window: %s\n", SDL_GetError(
"
C
o
u
l
d
n
o
t
cre
a
t
e
w
in
d
o
w
:
); return; } SDL_Renderer *renderer= SDL_CreateRenderer
w
i
n
d
o
w
,
−
1
,
S
D
L
R
E
N
D
E
R
E
R
P
R
E
S
E
N
T
V
S
Y
N
C
window,-1,SDL_RENDERER_PRESENTVSYNC
w
in
d
o
w
,
−
1
,
S
D
L
R
EN
D
ERE
R
P
RESENT
V
S
Y
NC
; if
r
e
n
d
e
r
e
r
=
=
N
U
L
L
renderer==NULL
re
n
d
erer
==
N
ULL
{ SDL_Log
"
C
o
u
l
d
n
o
t
c
r
e
a
t
e
r
e
n
d
e
r
e
r
:
"Could not create renderer: %s\n", SDL_GetError(
"
C
o
u
l
d
n
o
t
cre
a
t
ere
n
d
erer
:
); return; } struct SDL_Texture *texture=SDL_CreateTexture
r
e
n
d
e
r
e
r
,
S
D
L
P
I
X
E
L
F
O
R
M
A
T
I
Y
U
V
,
S
D
L
T
E
X
T
U
R
E
A
C
C
E
S
S
S
T
R
E
A
M
I
N
G
,
P
I
X
E
L
W
,
P
I
X
E
L
H
renderer,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,PIXEL_W,PIXEL_H
re
n
d
erer
,
S
D
L
P
I
XE
L
FORM
A
T
I
Y
U
V
,
S
D
L
T
EXT
U
RE
A
CCES
S
S
TRE
A
M
I
NG
,
P
I
XE
L
W
,
P
I
XE
L
H
; if
t
e
x
t
u
r
e
=
=
N
U
L
L
texture==NULL
t
e
x
t
u
re
==
N
ULL
{ SDL_Log
"
C
o
u
l
d
n
o
t
c
r
e
a
t
e
r
e
n
d
e
r
e
r
:
"Could not create renderer: %s\n", SDL_GetError(
"
C
o
u
l
d
n
o
t
cre
a
t
ere
n
d
erer
:
); return; } SDL_Thread *refresh_thread = SDL_CreateThread
R
e
f
r
e
s
h
V
i
d
e
o
,
N
U
L
L
,
N
U
L
L
RefreshVideo,NULL,NULL
R
e
f
res
hVi
d
eo
,
N
ULL
,
N
ULL
; SDL_Event event; // while
t
r
u
e
true
t
r
u
e
{ SDL_WaitEvent(&event); // if
(
e
v
e
n
t
.
t
y
p
e
=
=
S
D
L
K
E
Y
D
O
W
N
(event.type==SDL_KEYDOWN
(
e
v
e
n
t
.
t
y
p
e
==
S
D
L
K
E
Y
D
O
W
N
&&
e
v
e
n
t
.
k
e
y
.
k
e
y
s
y
m
.
s
y
m
=
=
13
event.key.keysym.sym==13
e
v
e
n
t
.
k
ey
.
k
eysy
m
.
sy
m
==
13
) { while
t
r
u
e
true
t
r
u
e
{ cout<<"Event started"<<endl; if
f
r
e
a
d
(
b
u
f
f
e
r
,
1
,
P
I
X
E
L
W
∗
P
I
X
E
L
H
∗
b
p
p
/
8
,
f
p
fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp
f
re
a
d
(
b
u
ff
er
,
1
,
P
I
XE
L
W
∗
P
I
XE
L
H
∗
b
pp
/8
,
f
p
!= PIXEL_W*PIXEL_H*bpp/8) { fread
b
u
f
f
e
r
,
1
,
P
I
X
E
L
W
∗
P
I
X
E
L
H
∗
b
p
p
/
8
,
f
p
buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp
b
u
ff
er
,
1
,
P
I
XE
L
W
∗
P
I
XE
L
H
∗
b
pp
/8
,
f
p
; if
!
f
r
e
a
d
(
b
u
f
f
e
r
,
1
,
P
I
X
E
L
W
∗
P
I
X
E
L
H
∗
b
p
p
/
8
,
f
p
!fread(buffer, 1, PIXEL_W*PIXEL_H*bpp/8, fp
!
f
re
a
d
(
b
u
ff
er
,
1
,
P
I
XE
L
W
∗
P
I
XE
L
H
∗
b
pp
/8
,
f
p
) { fseek
f
p
,
0
,
S
E
E
K
S
E
T
fp, 0, SEEK_SET
f
p
,
0
,
SEE
K
S
ET
; break; } } SDL_Delay
40
40
40
; SDL_UpdateTexture
t
e
x
t
u
r
e
,
N
U
L
L
,
b
u
f
f
e
r
,
P
I
X
E
L
W
texture,NULL,buffer,PIXEL_W
t
e
x
t
u
re
,
N
ULL
,
b
u
ff
er
,
P
I
XE
L
W
; // SDL_RenderClear
r
e
n
d
e
r
e
r
renderer
re
n
d
erer
; // SDL_RenderCopy(renderer,texture,NULL,&rect); SDL_RenderPresent
r
e
n
d
e
r
e
r
renderer
re
n
d
erer
; } } if
e
v
e
n
t
.
t
y
p
e
=
=
S
D
L
Q
U
I
T
event.type==SDL_QUIT
e
v
e
n
t
.
t
y
p
e
==
S
D
L
Q
U
I
T
{ break; } } fclose
f
p
fp
f
p
; SDL_DestroyTexture
t
e
x
t
u
r
e
texture
t
e
x
t
u
re
; SDL_DestroyRenderer
r
e
n
d
e
r
e
r
renderer
re
n
d
erer
; SDL_DestroyWindow
w
i
n
d
o
w
window
w
in
d
o
w
; cout<<"SDLPlayer Exit"<<endl; SDL_Quit
; } int main
i
n
t
a
r
g
c
,
c
h
a
r
∗
a
r
g
v
[
]
int argc, char * argv[]
in
t
a
r
g
c
,
c
ha
r
∗
a
r
gv
[
]
{ 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
"
.
/
r
e
c
v
.
h
264
"
,
"
w
b
+
"
"./recv.h264","wb+"
"./
rec
v
.
h
264"
,
"
w
b
+
"
; if
f
p
=
=
N
U
L
L
fp == NULL
f
p
==
N
ULL
{ printf("create file error\n"); return -1; } client_fd = socket
A
F
I
N
E
T
,
S
O
C
K
D
G
R
A
M
,
0
AF_INET,SOCK_DGRAM,0
A
F
I
NET
,
SOC
K
D
GR
A
M
,
0
; if
c
l
i
e
n
t
f
d
<
0
client_fd < 0
c
l
i
e
n
t
f
d
<
0
{ printf
"
s
o
c
k
e
t
e
r
r
o
r
/
n
"
"socket error/n"
"
soc
k
e
t
error
/
n
"
; return -1; } memset(&serveraddr,0,sizeof(struct sockaddr_in)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr
S
E
R
V
E
R
I
P
SERVER_IP
SER
V
E
R
I
P
; serveraddr.sin_port = htons
S
E
R
V
E
R
P
O
R
T
SERVER_PORT
SER
V
E
R
P
ORT
; struct sockaddr_in client; int count = 0; char buf
B
U
F
F
L
E
N
BUFF_LEN
B
U
F
F
L
EN
= "ok"; len = sizeof
s
e
r
v
e
r
a
d
d
r
serveraddr
ser
v
er
a
dd
r
; printf
"
c
l
i
e
n
t
:
"client:%s\n",buf
"
c
l
i
e
n
t
:
; sendto
c
l
i
e
n
t
f
d
,
b
u
f
,
B
U
F
F
L
E
N
,
0
,
(
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
client_fd, buf, BUFF_LEN, 0,(struct sockaddr *
c
l
i
e
n
t
f
d
,
b
u
f
,
B
U
F
F
L
EN
,
0
,
(
s
t
r
u
c
t
soc
ka
dd
r
∗
&serveraddr, len); int recv_count = 0; while
1
1
1
{ memset
b
u
f
,
0
,
B
U
F
F
L
E
N
buf, 0, BUFF_LEN
b
u
f
,
0
,
B
U
F
F
L
EN
; count = recvfrom
c
l
i
e
n
t
f
d
,
b
u
f
,
B
U
F
F
L
E
N
,
0
,
(
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
client_fd, buf, BUFF_LEN, 0, (struct sockaddr*
c
l
i
e
n
t
f
d
,
b
u
f
,
B
U
F
F
L
EN
,
0
,
(
s
t
r
u
c
t
soc
ka
dd
r
∗
&client, &len); if
c
o
u
n
t
>
0
count > 0
co
u
n
t
>
0
{ printf
"
r
e
c
v
"recv %d\n",recv_count
"
rec
v
; recv_count ++; } fwrite
b
u
f
,
c
o
u
n
t
,
1
,
f
p
buf,count,1,fp
b
u
f
,
co
u
n
t
,
1
,
f
p
; } sleep
10
10
10
; close
c
l
i
e
n
t
f
d
client_fd
c
l
i
e
n
t
f
d
; 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
2024
2024
; char buf
2024
2024
2024
; struct sockaddr_in client_addr; socklen_t len; fp = fopen
"
.
/
t
e
s
t
.
h
264
"
,
"
r
b
+
"
"./test.h264","rb+"
"./
t
es
t
.
h
264"
,
"
r
b
+
"
; if
f
p
=
=
N
U
L
L
fp == NULL
f
p
==
N
ULL
{ printf("open error\n"); return -1; } server_id = socket
A
F
I
N
E
T
,
S
O
C
K
D
G
R
A
M
,
0
AF_INET,SOCK_DGRAM,0
A
F
I
NET
,
SOC
K
D
GR
A
M
,
0
; if
s
e
r
v
e
r
i
d
<
0
server_id < 0
ser
v
e
r
i
d
<
0
{ printf
"
s
o
c
k
e
t
e
r
r
o
r
/
n
"
"socket error/n"
"
soc
k
e
t
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
"
"192.168.126.129"
"192.168.126.129"
; //此处修改IP serveraddr.sin_port = htons
S
E
R
V
E
R
P
O
R
T
SERVER_PORT
SER
V
E
R
P
ORT
; ret = bind
s
e
r
v
e
r
i
d
,
(
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
server_id,(struct sockaddr *
ser
v
e
r
i
d
,
(
s
t
r
u
c
t
soc
ka
dd
r
∗
&serveraddr,sizeof
s
t
r
u
c
t
s
o
c
k
a
d
d
r
i
n
struct sockaddr_in
s
t
r
u
c
t
soc
ka
dd
r
i
n
); if
r
e
t
<
0
ret < 0
re
t
<
0
{ printf("bind error\n"); return -1; } printf("after bind \n"); int count = 0; int flag = 0; int send_one_size = 5000; len = sizeof
c
l
i
e
n
t
a
d
d
r
client_addr
c
l
i
e
n
t
a
dd
r
; while
1
1
1
{ memset
b
u
f
,
0
,
1024
buf,0,1024
b
u
f
,
0
,
1024
; count = recvfrom
s
e
r
v
e
r
i
d
,
b
u
f
,
1024
,
0
,
(
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
server_id, buf, 1024, 0, (struct sockaddr*
ser
v
e
r
i
d
,
b
u
f
,
1024
,
0
,
(
s
t
r
u
c
t
soc
ka
dd
r
∗
&client_addr, &len); if
c
o
u
n
t
<
0
count < 0
co
u
n
t
<
0
{ printf("recieve data fail\n"); return -1; } printf
"
r
e
c
v
"recv %s",buf
"
rec
v
; while
s
t
r
c
m
p
(
b
u
f
,
"
o
k
"
strcmp(buf,"ok"
s
t
rc
m
p
(
b
u
f
,
"
o
k
"
== 0) { printf("recv ok sucees\n"); fread
r
e
a
d
b
u
f
,
2000
,
1
,
f
p
read_buf,2000,1,fp
re
a
d
b
u
f
,
2000
,
1
,
f
p
; count = sendto
s
e
r
v
e
r
i
d
,
r
e
a
d
b
u
f
,
s
e
n
d
o
n
e
s
i
z
e
,
0
,
(
s
t
r
u
c
t
s
o
c
k
a
d
d
r
∗
server_id,read_buf, send_one_size, 0,(struct sockaddr *
ser
v
e
r
i
d
,
re
a
d
b
u
f
,
se
n
d
o
n
e
s
i
ze
,
0
,
(
s
t
r
u
c
t
soc
ka
dd
r
∗
&client_addr,len); if
c
o
u
n
t
<
0
count < 0
co
u
n
t
<
0
{ printf("send error\n"); } sleep
1
1
1
; } /** if
s
t
r
c
m
p
(
b
u
f
,
"
o
v
e
r
"
strcmp(buf,"over"
s
t
rc
m
p
(
b
u
f
,
"
o
v
er
"
== 0) { break; } if
f
l
a
g
=
=
1
flag == 1
f
l
a
g
==
1
{ fwrite
b
u
f
,
1
,
c
o
u
n
t
,
f
p
buf,1,count,fp
b
u
f
,
1
,
co
u
n
t
,
f
p
; } **/ } //fclose
f
p
fp
f
p
; close
s
e
r
v
e
r
i
d
server_id
ser
v
e
r
i
d
; 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
如果此处配置失败看下面的
p
i
n
g
I
P
视频讲解有配置
I
P
的讲解
如果此处配置失败看下面的ping IP视频讲解有配置IP的讲解
如果此处配置失败看下面的
p
in
g
I
P
视频讲解有配置
I
P
的讲解
做查询输入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 观察播放器是否有实时画面出现如图所示: 图片 图片 如有哪里不明白可与我联系
嵌入式&系统
# 嵌入式
刘航宇
4年前
0
2,991
33
2021-09-18
【74LS283】加法器芯片74ls283资料
74ls283是4位二进制超前进位全加器 74ls283引脚图及功能 图片 74ls283引脚功能 A1–A4运算输入端 B1–B4运算输入端 C0进位输入端 Σ1–Σ4和输出端 C4进位输出端 74ls283逻辑功能图 图片 74ls283极限值 电源电压…………………………………………。7V 输入电压 54/74283…………………………………5.5V 54/74LS283…………………………………7V 发射极间电压 54/74283,54/74S283的A和B之间…………5.5V 工作环境温度 54XXX…………………………………-55~125℃ 74XXX…………………………………0~70℃ 存储温度…………………………………………-65~150℃ 74ls283功能表 图片
嵌入式&系统
刘航宇
4年前
1
2,816
2
2021-08-17
放大电路不同频段耦合电容、旁路电容、极间电容和分布电容的分析方法
耦合电容和旁路电容 耦合电容(uF级):下图是一个最简单的阻容耦合型的共射放大电路,C1和C2就是耦合电容,我们都知道电容的作用是通交隔直,因为我们放大的是交流信号,直流信号只是用来确定静态工作点,所以直流信号不会随着放大电路传递下去,稳定了多级放大电路的静态工作点, 图片 旁路电容
u
F
级
uF级
u
F
级
:下图的电容器就是旁路电容。交流通路中短路集电结电阻Re,提高电压放大能力。 分析方法 根据Xc=1/jwC,耦合电容和旁路电容在低频段(取10Hz)|Xc|量级为10kΩ级别,电路中Rb和Re等也都是kΩ级别,因此分析低频段时耦合电容和旁路电路保留;中高频段时,|Xc|量级为Ω甚至mΩ级别,因此容抗特别小,分析中高频段时耦合电容和旁路电容视为短路,因此晶体管频率响应中下限截止频率就来源于耦合电容和旁路电容。 图片 极间电容和分布电容 极间电容(pF级) 极间电容包括发射结电容和集电结电容,是晶体管所固有的特征。 分布电容(pF级) 分布电容存在于由两个存在电压差而又相互绝缘的导体间,例如,两根传输线之间,每跟传输线与大地之间,都是被空气介质隔开的,所以,也都存在着电容。一般情况下,这个电容值很小,它的作用可忽略不计,如果传输线很长或所传输的信号频率高时,就必须考虑这电容的作用。 分析方法 根据Xc=1/jwC,极间电容和分布电容在高频段(取10Mhz)|Xc|量级为10kΩ级别,电路中Rb和Re等也都是kΩ级别,因此分析高频段时极间电容和分布电路保留;中低频段时,|Xc|量级为MΩ级别甚至更大,因此容抗特别大,分析中低频段时极间电容和分布电容视为断路,因此晶体管频率响应中上限截止频率就来源于极间电容和分布电容。
嵌入式&系统
刘航宇
4年前
0
882
3
三极管与场效应管比较及其放大电路比较
三极管与场效应管比较及其放大电路比较: 图片 图片 图片 图片
嵌入式&系统
刘航宇
4年前
0
448
3
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
放大电路——NPN案例 图片 截止失真 由晶体管截止造成的失真,称为截止失真。当Q点过低时,在输入信号负半周靠进峰值的某段时间内,晶体管b-e间电压总量小于其开启电压,此时,晶体管截止,因此,基极电流将产生底部失真。 用图解法分析,晶体管的静态工作点设置较低时,由于输入信号的叠加有可能使叠加后的波形一部分进入截止区,这样就会出现截止失真。NPN型三极管共射极放大电路的截止失真的表现是输出电压的顶部出现削波,PNP型三极管的共射放大电路的截止失真是底部失真。 图片 饱和失真 饱和失真,指的是晶体管因Q点过高,出现的失真。当Q点过高时,虽然基极动态电流为不失真的正弦波,但是由于输入信号正半周靠进峰值的某段时间内晶体管进入饱和区,导致集电极动态电流产生顶部失真,集电极电阻上的电压波形随之产生同样的失真。由于输出电压与集电极电阻上的电压变化相位相反,从而导致输出波形产生底部失真。 图片 实验中NPN放大电路饱和失真和截止失真的区分 输入波形是正半周,输出波形是负半周,近峰值时,输出不再随输入波形变化,就是饱和失真; 输入波形是负半周,输出波形是正半周,近峰值时,输出不再随输入波形变化,就是截止失真。 三极管的输出和输入正好是反过来的,即反相输出。假设输入的是正弦波,静态工作点正好合适,即VQ=Vp-p/2(静态工作点电压是正弦波电压峰峰值的一半),那么当输入的波形是正半周时,输出电压波形正好跟负半周波形是一样的;当输入的波形是负半周时,输出电压波形正好跟正半周波形是一样的。如果VQ大于输入波形的峰峰值的一半,那么当输入的波形是正半周时,快到峰值时,三极管就会处于饱和状态,那么此时的输出就不再随输入变化了,出现了饱和失真;即输出得到的负半周正弦波波形就没有谷底了,我们称之为饱和失真。 对于NPN单管共射放大电路,饱和失真就是输入信号的正半波超过了三极管的放大能力,造成失真,对应的输出波形就是输出波形底部失真,即输出时三极管进入饱和区,Q设置过高。因为饱和失真是输出端失真所以解决饱和失真的方法就是调低静态工作电流Ib(增大Rb),减小Rc,减小β。 反之,当输入的波形是负半周时,快到谷值时,三极管就会处于截止状态,那么此时的输出就不再随输入变化了,出现了截止失真;即输出得到的正半周正弦波波形就没有峰值了,我们称之为截止失真。Q点设置过低造成的截止失真属于输入端失真,所以只能从输入端解决。解决方法有增大VBB,减小输入端电阻Rb。 PNP与NPN对比分析 NPN型:如果波形Uo顶部失真(变平),则属于截止失真Q小。底部失真代表饱和失真Q大。 PNP型:Uo顶部失真代表饱和失真Q大,底部失真是截止失真Q小。
U
b
e
、
U
c
e
极性为
−
导致的
Ube、Uce极性为-导致的
U
b
e
、
U
ce
极性为
−
导致的
截止失真Q小说明Ib太小,你可以通过减少Rb来增大Ib。 如果是饱和失真Q大,说明Ib(Ic)太大,必须用增大Rb来减少失真。 后面两点,对于NPN还是PNP都是适用的。 截止失真是在输入时产生的,当Q点过低时,输入信号在负半周小于晶体管的开启电压,也就是小于UbeQ,导致晶体管截止而产生失真。饱和失真是在输出时产生的,当Q点过高时,Ib为不失真的正弦波,在输出特性曲线上会发现输入信号正半周靠近峰值的某段时间内晶体管进入饱和区,导致Ic产生顶部失真。
嵌入式&系统
刘航宇
4年前
0
13,894
36
上一页
1
...
4
5
6
...
10
下一页