STM32

在使用STM32的CAN控制器进行数据收发,当用到位屏蔽模式的时候,就要设置过滤器了,这个关系到是否能够接收到想要的数据。下面针对几种不同情况对CAN过滤器(Filter)进行设置。

CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //标示符屏蔽模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;

1、对扩展数据帧进行过滤:(只接收扩展数据帧)

CAN_FilterInitStructure.CAN_FilterIdHigh =(EXT_ID >> 13) & 0xFFFF;
CAN_FilterInitStructure.CAN_FilterIdLow =((EXT_ID << 3) | (0x04)) & 0xFFFF; 
//这里设置|0x04(0b0100)就是为了IDE=1,RTR=0,
//这里也可以写为(EXT_ID << 3)|CAN_ID_EXT|CAN_RTR_ROMOTE,效果一样,下同
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFFF;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFFF;

2、对扩展远程帧过滤:(只接收扩展远程帧)

CAN_FilterInitStructure.CAN_FilterIdHigh =(EXT_ID >> 13) & 0xFFFF;
CAN_FilterInitStructure.CAN_FilterIdLow =((EXT_ID << 3) | (0x06) & 0xFFFF; 
//这里设置|0x06(0b0110)就是为了IDE=1,RTR=1
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFFF;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0xFFFF;

3、对标准远程帧过滤:(只接收标准远程帧)

CAN_FilterInitStructure.CAN_FilterIdHigh =((STD_ID << 21) & 0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterIdLow =((STD_ID << 21) | (0x0)) & 0xFFFF; 
//这里设置|0x0(0b0000)就是为了IDE=0,RTR=0
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFE0; //0xFFE0表示要关注11位的STD_ID
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0006;  //0110,表示要关注IDE和RTR位,只接受标准,不接受扩展

4、对标准数据帧过滤:(只接收标准数据帧)

CAN_FilterInitStructure.CAN_FilterIdHigh =((STD_ID << 21) & 0xFFFF0000)>>16;
CAN_FilterInitStructure.CAN_FilterIdLow =((STD_ID_ID << 21) | (0x06)) & 0xFFFF; 
//这里设置|0x02(0b0010)就是为了IDE=0,RTR=1
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0xFFE0;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0006;//只接受标准数据帧,不接受扩展数据帧

版权声明:本文为CSDN博主「hitaowei」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/hitaowei/article/details/52239739

围观 19

作者:郭老师

之前由于工作需要,基于 RT-Thread 在 STM32 上实现了 USB 虚拟串口。为了方便大家,我在这里把在正点原子 F429 阿波罗开发板上实现 USB 虚拟串口的详细过程分享给大家,希望可以帮助到更多想要学习 USB 的人。

1、首先,需要更新了一下 RT-Thread 的源代码(因为 RT-Thread 的代码更新很快,短时间内就有可能有很多的代码更新,Github地址:https://github.com/RT-Thread/rt-thread 点star还能领10元柿饼派优惠券)

2、然后进入 rt-thread\bsp\stm32目录下,找到正点原子 F429 阿波罗开发板对应的BSP stm32f429-atk-apollo ,打开此目录。

3、然后查看一下当前 BSP 支持不支持 USB 功能。在当前目录下打开 Env 工具,输入 menuconfig 命令查看,可以看到在硬件配置的片上外设的配置菜单中并没有配置 USB 的选项,看来这个 BSP 还不支持 USB 设备。

4、想到新的 STM32 BSP 所有的 BSP 都是用的同一份驱动,这样就可以根据有没有做好的 USB 驱动来判断有没有 BSP 支持 USB 功能了。打开rt-thread\bsp\stm32\libraries\HAL_Drivers目录。如下所示,可以看到里面果然有 usb 的驱动文件,叫做drv_usbd_fs.c。

5、然后根据同一目录下的 Sconscript 脚本文件,可以查看这个驱动的依赖关系,根据下面的图片可以看出,此驱动文件依赖于 BSP_USING_USBD_FS这个配置项。

6、全局搜索此 stm32 目录下所有的 BSP ,查看哪个 bsp 下有这个配置项。根据这个配置项可以判断出哪个 BSP 支持了 USB 的功能,也可以借此看出依赖关系。搜索发现 F469 的 bsp 有这个配置项,由下图可以看出:打开这个配置的同时,也利用 select 命令打开了 RT_USING_USB_DEVICE 这个配置。

7、修改正点原子 F429 阿波罗 bsp 下 Kconfig 文件,添加这一段配置项。


8、然后,利用 Env 工具根据修改好的配置菜单配置工程。在 目录下输入 menuconfig 配置工程,开启刚刚添加的 usb 驱动的配置项。

9、然后,进入组件配置菜单下设备驱动的配置菜单中的 USB 配置,配置 usb 设备框架的选项。开启虚拟串口。

10、保存并重新生成工程。 发现编译报错。看起来是硬件没有配置。需要打开 stm32CubMX 配置 usb 的硬件引脚。

11、打开stm32f429-atk-apollo\board\CubeMX_Config目录下 stm32CubMX 的工程,配置 usb.


12、开启 usb 功能之后,时钟配置报警告,还需重新配置一下时钟。配置好之后,重新生成代码。

13、由于更新了时钟树,所以还要把stm32f429-atk-apollo\board\CubeMX_Config\Src目录下main.c中的时钟配置函数SystemClock_Config更新到stm32f429-atk-apollo\board目录下的 board.c 文件中。

14、然后重新打开工程,编译,发现还是报错,cannot open source input file "stm32f4xx_hal_exti.h": No such file or directory,注释掉报错的头文件重新编译即可。再次编译,发现没有问题了。


15、下载运行,输入 list_device 命令可以看到注册到系统中的两个 usb 相关的设备。

16、然后在 main函数里添加一段测试代码,编译下载运行。

17、连接开发板上的 USB_SLAVE 接口到电脑上,打开设备管理器,发现多了一个 USB 串行设备,用串口工具打开,就可以接收到从 main 函数里发送过来的消息了。


这样就基于 RT-Thread 在 STM32 上实现 USB 虚拟串口了!

来源: RTThread物联网操作系统

围观 18

最近,因为项目需要在STM32F103系列处理器上,对采集的音频信号进行FFT运算,然而STM32F103毕竟不是STM32F4系列的处理器,对于一般的FFT运算程序还是比较缓慢的。

幸亏官方提供了针对FFT的官方库,但是去官网找了半天居然找不到那个库的下载,好像官方早就把那个库下架了,估计是为了给带DSP指令集的F4系列让路。然后就只好从别人的项目中把这个官方库给扒出来了……

下载地址:https://pan.baidu.com/s/1GiUJgEkQxDAk79iddXKsaA 提取码: dn2d

FFT的意义

对于很多人来说,采样频率和FFT点数之间的关系可能还是不太清楚。下面就来简单分析一下:

根据采样定理,采样频率必须是被采样信号最高频率的2倍。比如,需要采集音频信号,并且需要被观察到的音频频率的频率范围是20Hz到20KHz,那么使用的采样频率就必须大于40kHz。如果需要观察到的音频频率范围为0Hz到600Hz,那么使用的采样频率只需要大于1200Hz即可。

而FFT点数与采样频率之间有什么关系呢?本质上并没有什么关系,但是FFT点数的大小直接关系到频率分辨率。怎么来说呢?

假设采样频率为Fs,信号频率F,采样点数为N。那么FFT之后结果就是一个为N点的复数。每一个点就对应着一个频率点。这个点的模值,就是该频率值下的幅度特性。同时,FFT后的N个点,开始的那个点表示直流分量(即0Hz),而最后的那个点的再下一个点表示采样频率Fs,这中间被N-1个点平均分成N等份,每个点的频率依次增加。即,某点n所表示的频率为:Fn=(n-1)*Fs/N。这就表示,Fs/N就是频率分辨率。

不太理解的,可以查看博客:FFT后的物理意义:https://blog.csdn.net/iloveyoumj/article/details/53308142

FFT官方库的使用条件

FFT官方库在使用上并不灵活:属于基4的FFT,即FFT点数必须是4^n。也就是说,如果要做512点或2048点的FFT,那么对不起,没法使用官方库了;

FFT官方库的输入输出是等长的,即256点的FFT输入也必须是256点,如果你的输入小于这个长度,是没有任何性能提升的。

FFT官方库的使用

准备工作

下载得到STM32的DSP库之后,就可以将其添加到自己的工程项目中了。

其中,stm32_dsp.h和table_fft.h两个文件是必须添加的。stm32_dsp.h是STM32的DSP库的头文件。

另外,对于**.s文件可以有选择的添加**(用到那个添加那个即可)。由于本文只用到了256点的FFT,所以这里只添加了cr4_fft_256_stm32.s文件。


FFT函数说明

进行256点的FFT,只需要调用STM32 DSP库函数中的cr4_fft_256_stm32()函数即可。该函数的原型为:

void cr4_fft_256_stm32(void *pssOUT, void *pssIN, uint16_t Nbin);

其中,参数pssOUT表示FFT输出数组指针,参数pssIN表示要进行FFT运算的输入数组指针,参数Nbin表示了点数。至于该函数的具体实现,因为是用汇编语言编写的,我也不懂,这里就不妄谈了。

需要说明的是:按照FFT官方库的说明,pssOUT和pssIN都必须是32位的数据类型,其中高16位存储实部,低16位存储虚部。对于pssIN来说,低16位存储的虚部总是为0。

代码实例

假设ADC采样的声音数据为adc_buf[NPT],FFT运算的输入数组为lBufInArray[NPT]。由于FFT计算出来的数据是对称的,因此通常而言输出数组取一半的数据,为lBufOutArray[NPT/2]。除此之外,还定义各次谐波幅值lBufMagArray[NPT/2]。即:

#define NPT 256

uint32_t adc_buf[NPT]={0};
long lBufInArray[NPT];
long lBufOutArray[NPT/2];
long lBufMagArray[NPT/2];

调用FFT官方库的代码为:

//填充数组
for(i=0;i<NPT;i++)
	//这里因为单片机的ADC只能测正的电压 所以需要前级加直流偏执
	//加入直流偏执后,需要在软件上减去2048即一半,达到负半周期测量的目的(需要根据具体情况来进行配置)
	lBufInArray[i] = ((signed short)(adc_buf[i]-2048)) << 16;
	
cr4_fft_256_stm32(lBufOutArray, lBufInArray, NPT);

同时,计算各次谐波幅值的函数为:

void GetPowerMag()
{
    signed short lX,lY;
    float X,Y,Mag;
    unsigned short i;
    for(i=0; i < NPT/2; i++)
    {
        lX  = (lBufOutArray[i] << 16) >> 16;
        lY  = (lBufOutArray[i] >> 16);
        
        //除以32768再乘65536是为了符合浮点数计算规律
        X = NPT * ((float)lX) / 32768;
        Y = NPT * ((float)lY) / 32768;
        Mag = sqrt(X * X + Y * Y) / NPT;
        if(i == 0)
            lBufMagArray[i] = (unsigned long)(Mag * 32768);
        else
            lBufMagArray[i] = (unsigned long)(Mag * 65536);
    }
}

---------------------
版权声明:本文为CSDN博主「Yngz_Miao」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38410730/article/details/90116695

围观 72

STM32F1xx官方资料:《STM32中文参考手册V10》-第25章通用同步异步收发器(USART)

通信接口背景知识

设备之间通信的方式

一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种。它们的区别是:

并、串行通信的区别

串行通信的分类

1、按照数据传送方向,分为:

- 单工:数据传输只支持数据在一个方向上传输;

- 半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口。

- 全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。


2、按照通信方式,分为:

- 同步通信:带时钟同步信号传输。比如:SPI,IIC通信接口。

- 异步通信:不带时钟同步信号。比如:UART(通用异步收发器),单总线。

在同步通讯中,收发设备上方会使用一根信号线传输信号,在时钟信号的驱动下双方进行协调,同步数据。例如,通讯中通常双方会统一规定在时钟信号的上升沿或者下降沿对数据线进行采样。

在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些用于同步的信号位,或者将主题数据进行打包,以数据帧的格式传输数据。通讯中还需要双方规约好数据的传输速率(也就是波特率)等,以便更好地同步。常用的波特率有4800bps、9600bps、115200bps等。

在同步通讯中,数据信号所传输的内容绝大部分是有效数据,而异步通讯中会则会包含数据帧的各种标识符,所以同步通讯效率高,但是同步通讯双方的时钟允许误差小,稍稍时钟出错就可能导致数据错乱,异步通讯双方的时钟允许误差较大。

常见的串行通信接口


STM32串口通信基础

STM32的串口通信接口有两种,分别是:UART(通用异步收发器)、USART(通用同步异步收发器)。而对于大容量STM32F10x系列芯片,分别有3个USART和2个UART。

UART引脚连接方法
- RXD:数据输入引脚,数据接受;
- TXD:数据发送引脚,数据发送。


对于两个芯片之间的连接,两个芯片GND共地,同时TXD和RXD交叉连接。这里的交叉连接的意思就是,芯片1的RxD连接芯片2的TXD,芯片2的RXD连接芯片1的TXD。这样,两个芯片之间就可以进行TTL电平通信了。


若是芯片与PC机(或上位机)相连,除了共地之外,就不能这样直接交叉连接了。尽管PC机和芯片都有TXD和RXD引脚,但是通常PC机(或上位机)通常使用的都是RS232接口(通常为DB9封装),因此不能直接交叉连接。RS232接口是9针(或引脚),通常是TxD和RxD经过电平转换得到的。故,要想使得芯片与PC机的RS232接口直接通信,需要也将芯片的输入输出端口也电平转换成rs232类型,再交叉连接。

经过电平转换后,芯片串口和rs232的电平标准是不一样的:
- 单片机的电平标准(TTL电平):+5V表示1,0V表示0;
- Rs232的电平标准:+15/+13 V表示0,-15/-13表示1。

RS-232通讯协议标准串口的设备间通讯结构图如下: 


所以单片机串口与PC串口通信就应该遵循下面的连接方式:在单片机串口与上位机给出的rs232口之间,通过电平转换电路(如下面图中的Max232芯片) 实现TTL电平与RS232电平之间的转换。


具体要了解RS232串口的,可以查看链接RS232串口简介(http://www.21ic.com/jichuzhishi/datasheet/RS232/jiekou/187973.html)。

STM32的UART特点

  •  全双工异步通信;
  •  分数波特率发生器系统,提供精确的波特率。发送和接受共用的可编程波特率,最高可达4.5Mbits/s;
  •  可编程的数据字长度(8位或者9位);
  •  可配置的停止位(支持1或者2位停止位);
  •  可配置的使用DMA多缓冲器通信;
  •  单独的发送器和接收器使能位;
  •  检测标志:① 接受缓冲器  ②发送缓冲器空 ③传输结束标志;
  •  多个带标志的中断源,触发中断;
  •  其他:校验控制,四个错误检测标志。

串口通信过程


STM32中UART参数

串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备的RXD接口,通讯双方的数据包格式要规约一致才能正常收发数据。STM32中串口异步通信需要定义的参数:起始位、数据位(8位或者9位)、奇偶校验位(第9位)、停止位(1,15,2位)、波特率设置。

UART串口通信的数据包以帧为单位,常用的帧结构为:1位起始位+8位数据位+1位奇偶校验位(可选)+1位停止位。

如下图所示:


奇偶校验位分为奇校验和偶校验两种,是一种简单的数据误码校验方法。奇校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为奇数;偶校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为偶数。

校验方法除了奇校验(odd)、偶校验(even)之外,还可以有:0 校验(space)、1 校验(mark)以及无校验(noparity)。 0/1校验:不管有效数据中的内容是什么,校验位总为0或者1。

UART(USART)框图


这个框图分成上、中、下三个部分。本文大概地讲述一下各个部分的内容,具体的可以看《STM32中文参考手册》中的描述。

框图的上部分,数据从RX进入到接收移位寄存器,后进入到接收数据寄存器,最终供CPU或者DMA来进行读取;数据从CPU或者DMA传递过来,进入发送数据寄存器,后进入发送移位寄存器,最终通过TX发送出去。

然而,UART的发送和接收都需要波特率来进行控制的,波特率是怎样控制的呢?

这就到了框图的下部分,在接收移位寄存器、发送移位寄存器都还有一个进入的箭头,分别连接到接收器控制、发送器控制。而这两者连接的又是接收器时钟、发送器时钟。也就是说,异步通信尽管没有时钟同步信号,但是在串口内部,是提供了时钟信号来进行控制的。而接收器时钟和发送器时钟有是由什么控制的呢?

可以看到,接收器时钟和发送器时钟又被连接到同一个控制单元,也就是说它们共用一个波特率发生器。同时也可以看到接收器时钟(发生器时钟)的计算方法、USRRTDIV的计算方法。

这里需要知道一个知识点:
  •  UART1的时钟:PCLK2(高速);
  •  UART2、UART3、UART4的时钟:PCLK1(低速)。

框图的中部分,涉及到UART(USART)的中断控制部分,在后面的文章中会具体介绍到。

版权声明:本文为CSDN博主「Yngz_Miao」的原创文章
遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38410730/article/details/79887200

围观 79

41、DMA仲裁器分为软件和硬件两种。软件部分分为4个等级,分别是很高优先级、高优先级、中等、低。硬件部分由通道的大小来决定优先级,越低优先级越高。

42、DMA有一个实时的传输数据量寄存器叫做DMA_CNDTR,最大值为65535,存放的是当前传输所要传输的数据量。当数据量变为0时,表明传输完成。

43、CAN总线(ControllerArea Network)。CAN控制器根据两根线上的电位差来判断总线电平,总线电平又分为显性电平和隐性电平,二者必居其一。

44、CAN总线具有6个特点:

1. 多主控制
2. 系统若软性
3. 通讯速度较快,通讯距离较远
4. 具有错误检测、错误通知和错误恢复功能
5. 故障封闭,当总线上的设备发生连续故障错误时,CAN控制器会把改控制器踢出总线
6. 连接节点多。理论上可以无限制加载,但是受到时间延迟和电气负载的限制,实际数目是有限制的。降低传输速度可以适当增加可挂接负载个数。

45、CAN协议有两个标准,ISO11898(针对125kbps~1Mbps的高速速率)和ISO11519-2(125kbps以下的低速速率)


46、


47、CAN协议的有5种类型的帧:数据帧、遥控帧、错误帧、过载帧、帧间隔。其中前两种帧有标准格式(11位ID)和扩展格式(29位ID)。


48、数据帧构成:

(1) 帧起始。表示数据开的段帧起始。

(2) 仲裁段。表示该帧优先级的仲裁段。

(3) 控制段。表示数据的字节及保留位段。

(4) 数据段。数据的内容,一帧可发送0~8个字节的数据。

(5) CRC段。 检查帧的传输错误段。

(6) ACK段。 表示确认正常接收的段。

(7) 帧结束。 表示数据的段帧结束。


49、Stm32f103系列只有一个CAN控制器,有3个发送邮箱和3级深度的2个FIFO,14个过滤组器。

50、STM32的每个过滤组可以配置为1个32位过滤器和2个16位过滤器。除此之外,还可以配置为屏蔽位模式(ID+屏蔽)和标识符列表(ID和屏蔽寄存器均用来做ID寄存器)模式。

51、CAN接收到有效报文被放置在3级邮箱深度的FIFO中,FIFO完全由硬件来管理。

52、CAN总线的波特率


53、触摸屏一般分为电阻式触摸屏和电容式触摸屏。前者检测触摸的位置原理是利用触摸屏控制器中的A/D转换器经过两次A/D读值后得出X和Y的坐标值。注意:这个X和Y的值是相对于触摸屏的,而非LCD屏。

所以在这里需要注意两个概念:触摸屏和LCD屏。这是两个不同的概念,也是两个不同的物理结构,其中电阻触摸屏是由上下两个导电层中间夹着一层非常薄的透明隔层;而LCD就是指显示屏。

54、电阻触摸屏有X和Y、X和Y的比例因子、坐标轴方向、偏移量。LCD也有自己的这些参数。两者完全不相干,所以在定位的时候需要进行坐标转换。公式:


通过对屏幕的四个点进行校准,得到四元一次方程,求解即可。

55、NEC协议的数据帧格式:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。

56、NEC协议在发送的时候,会有560us的38KHz的载波信号,而在接收的时候这部分载波信号被认定为低电平,而剩余的(2.25ms-650us)的逻辑“1”和(1.12ms-650us)的逻辑“0”时间则被认定为高电平。

57、在单位时间内的位移被定义为速度,速度有线速度和角速度之分,分别对应两种传感器测量这两种不同的速度:线速度传感器(加速度计)、角速度传感器(陀螺仪)。

前者多应用在静态或者低慢速运动中的姿态求解,后者多应用在动态运动中姿态求解。

58、根据标准约定,零加速度(或零 G 准位)通常定义为相当于最大输出值(12 位输出为 4096,10 位输出为 1024 等)一半的输出。对于提供 12 位输出的加速度计,零 G 准位将等于 2048。

输出大于 2048 表示正加速度。输出小于 2048 表示负加速度。加速度的数量通常用单位 g (1g = 9.8m/s2 = 重力加速度)表示。

通过确定测量的输出与零 G 准位之间的差值,然后除以加速度计的灵敏度(用计数/g 或 LSB/g表示)来计算加速度。

对于提供 12 位数字输出的 2g 加速度计,灵敏度为 819 计数/g 或 819 LSB/g。加速度等于:a = (Aout - 2048)/(819 计数/g),单位为 g。

59、加速度计测得的加速度的方向和设备设定的坐标系是相反的,因为原理表明在测量力的时候采用的是非惯性系参考系,而我们高中时代研究的坐标系是惯性系参考系,前者在物体进行运动产生加速度时,假想一个与速度方向相反的力作用在物体上,这个力就是惯性力;

后者我们说不存在惯性力,只说存在惯性,因为在惯性坐标系中,我们研究的是物体,而非坐标系(即假定坐标系相对地球静止),当我们把坐标系也考虑在内时,当坐标系运动,就产生了惯性力f,这种力作用会假想作用在物体上,只是与运动方向相反。

60、由上可知,加速度计的本质是测量力而非加速度。

61、NRF24L01工作在2.4GHz的频段,由于频段频率较高,所以传输速率较快,为2Mbps。

62、STM32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器3个部分构成。

主存储器用来存放代码和const常量;信息块由两个部分组成:启动程序代码、用户选择字节。

其中启动程序代码为ST公司自带的启动程序,用于串口下载。

最后的闪存存储器接口寄存器用于控制整个对闪存区域的操作。

63、CPU的运行速度比FLASH的操作速度快的多,一般FLASH的最快访问速度≤24Mhz。如果CPU的速度超过这个频率,那么在读取FLASH的时候必须加入等待时间(FLASH_ACR设置)。

64、FLASH编程时,写入必须为半字(16位)。并且在写入的时候必须保证所写区域的数据必须为0xFFFF。

65、STM32的FSMC有HADDR[27:0],其中[27:26]用来选择BANK区域的4个不同块。

剩下的[25:0]则用来连接外部存储区域的地址线FSMC_A[25:0]。

如果数据宽度是8bit,此时的HADDR[25:0]和FSMC_A[25:0]是完全对应的。

如果数据宽度是16bit,此时的HADDR[25:1]和FSMC_A[24:0]是对应起来的。

需要注意:无论数据宽度是多少,外部的FSMC_A[0]和A[0]总是对应的。

66、关于LB和UB的信号控制是由硬件自动控制的,当AHB的数据宽度小于外部存储器的数据宽度时,此时LB和UB的控制信号自动产生(比如字节读取/写入16bit的外部存储器)。

67、 __attribute__ (函数属性、变量属性、类型属性等)。如果在使用SRAM时,可以采用u32 sram_array[xx] __attribute__ ((at(0x68000000))代表将外部SRAM的空间全部给了sram_array这个变量,他具有在at0x68000000这个地址的属性。

往里面写值就直接在SRAM里面写值。

68、


内存管理有一种方式叫做分块式内存管理。

注意表中的分配方向,从顶到底。每一项对应一个内存块。里面的数值代表了内存池的状态:如果为0,表示该内存没有被分配;如果非0,那么数值的大小就表示了该块内存被连续占用的内存数。

比如说数值为20,意思是包括该项在内的内存块被连续占用了20块分给了指针。

69、SD卡的分类:


一般的SD卡支持两种传输模式:SD卡模式(SDIO)、SPI模式。显然前面一种是专用模式,所以速度比较快。

70、常用的汉字内码系统有GB2313、GB13000、GBK、BIG5(繁体)。其中GB2313只有几千个汉字,而GBK则有2万多汉字。

71、要显示汉字,采用的方式如果用点阵的形式是不可取的,因为这无法查找汉字。采用的方式就是内码系统。

GBK标准中,一个汉字对应2个字节:前者称为区(0x81~0xFE)后者为(0x40~0x7E)和(0x80~0xFE)。前者有126个区,后者有190,那么可以显示的汉字数量有126*190=23940个。

根据这两个值用来查找字库,字库中存放的还是每个汉字的点阵数据。

这个字库非常大,如果是16*16的字体,那么一个字体就需要32个字节,如此说来需要23940*32=748K的空间,可见非常大,所以需要外部的Flash来存储这个字库。

72、由于汉字内码系统不具有国际通用性,但是Unicode几乎把所有的语言都放置进来,这样在单片机中操作汉字时,就需要将GBK和Unicode转化。

尤其是在FATFS中,创建中文文件名和读取中文文件信息时需要将Unicode换转为GBK后再进行修改操作,再反转换成Unicode保存修改。

这么说,两者的存在是由于标准的不统一,并且Unicode中只有6064个汉字,而GBK显然是一种汉字扩展。

73、BMP图片编码的顺序是从左到右,从下到上。

74、VS1053是一款高性能的数字音频解码芯片,从SD卡中将mp3等音乐音频文件通过SPI送给VS1053后,由其进行音频解码,输出音乐给耳机。

耳机驱动可以采用TDA1308芯片,这款芯片为AB类耳机驱动芯片。


75、IAP(In Application Programming)在应用编程是为了后期开发更新程序方便而提出的概念。具体的实现方法如下图所示:


在普通编程中,flash中的code是通过JTAG和ISP等工具下载到单片机中。

而在IAP编程中,flash被分区为A和B两个区域,A区域只允许用USB/USART等方式下载,此区域作为更新B区域的代码用。

B区域则是用户的code区域,真正的代码在这里被执行,放置的就是app。


上图表示STM32正常运行的流程图,可以看到上电复位后系统从0x80000004处开始运行程序,这里放置的是复位中断向量,然后跳转至复位中断程序入口后再跳转至main函数运行用户的程序。


上图表示加入IAP后的STM32程序运行流程图。可以看到上电复位后跳到IAP程序的main函数处运行IAP过程(这个过程就是把下面灰底色块的程序代码烧进B区域à代码更新)。

后面的过程和STM32正常运行一样,如果出现中断请求,还是跳转到A区域中的中断向量表中,然后再跳转到B区域的中断服务入口。

76、USB有四根线,VCC、GND、D+、D-。在USB主机上,D+和D-均通过一个15K的电阻接地,这样两条线均为低电平。

在USB设备中,对于高速设备会在D+通过一个1.5K的电阻接到VCC,而低俗设备会在D-通过一个1.5K的电阻接到VCC。

这样主机就可以通过D+和D-的高电平的到来来检测是否有设备接入,并且识别高低速设备。

77、UCOSII是一种实时操作系统,具有执行效率高、占有空间小(最小内核2KB)、实施性能优良、扩展性强和移植性强等优点。


UCOS具有多任务并发工作的特点(注意,任何时候只有一个任务能够占用CPU。并发只是任务轮流占用CPU而不是同时工作)。

最大支持255个任务并发工作。

来源:玩转单片机,转载此文目的在于传递更多信息,版权归原作者所有。

围观 112

1、SYSCLK时钟源有三个来源:HSI RC、HSE OSC、PLL

2、MCO[2:0]可以提供4源不同的时钟同步信号,PA8

3、GPIO口有两个反向串联的二极管用作钳位二极管。

4、ICode总线,DCode总线、系统总线、DMA总线、总线矩阵、AHB/APB桥

5、在使用一个外设之前,必须设置寄存器RCC_AHBENR来打开该外设的时钟

6、STM32复位有三种:系统复位、上电复位、备份区域复位。其中系统复位除了RCC_CSR中的复位标志和BKP中的数值不复位之外,其他的所有寄存器全部复位。触发方式例如外部复位、看门狗复位、软件复位等;

电源复位由于外部电源的上电/掉电复位或者待机模式返回。复位除了BKP中的寄存器值不动,其他全部复位;

备份区域复位的触发源为软件复位或者VDD和VBAT全部掉电时。

7、(NestedVectored Interrupt Controller)NVIC嵌套向量中断控制器,分为两种:抢先式优先级(可嵌套)和中断优先级(副优先级,不能嵌套)。

两种优先级由4位二进制位决定。分配下来有十六种情况:

8、自动装载寄存器和影子寄存器:前者相当于51当中的溢出设定数值。而影子寄存器顾名思义是影子,就是寄存器的另一分copy。

实际起作用的是影子寄存器,而程序员操纵的则是自动装载寄存器。如果APPE位使能,表明自动装载寄存器的值在下一次更新事件发生后才写入新值。

否则,写入自动装载寄存器的值会被立即更新到影子寄存器。

9、计数器的数值与输出比较器相等时,翻转输出信号

10、ARM公司只生产内核标准,不生产芯片。ST、TI这样的公司从ARM公司那里购买内核,然后外加自己的总线结构、外设、存储器、时钟和复位、I/O后就组成了自己的芯片。

11、电容触摸屏原理:通过充放电的曲线不同来检测是否被按下。实际的实验过程中,TPAD可以用一块覆铜区域来替代,通过电容的充放电常数来确定是否按下。

12、OLED,即有机发光二极管,又称为有机电激光显示。下图为OLED的GRAM与屏幕的对应表

PAGE2单独列出来:

13、USART可以操纵SPI设备。不过最大频率只有4.5MHz

14、使用I/O口时应该注意的问题

15、ADC的Vref+和Vdda与VSS,Vref-一定要加高质量的滤波电容,切靠近单片机。

16、在STM32内部,FSMC的一端通过内部高速总线AHB连接到内核Cortex-M3,另一端则是面向扩展存储器的外部总线。

内核对外部存储器的访问信号发送到AHB总线后,经过FSMC转换为符合外部存储器通信规约的信号,送到外部存储器的相应引脚,实现内核与外部存储器之间的数据交互。

17、FSMC中的DATASET和ADDSET的设置需要参看外部存储器的时序图来确定。

一般而言,DATASET指的是数据建立时间,也就是读/写信号开始到读/写信号停止(上升沿存储数据)的持续时间。(一般来说写比读快!)

而ADDSET指的是地址建立时间,指的是片选之后到读/写操作之前的时间,这是针对SRAM来说的,如果操纵的是TFT,不存在地址线,所以此时的ADDSET就是读/写信号结束到RS电平的转换时间。

18、

19、

20、FSMC的三个配置寄存器:FSMC_BCRx(片选控制配置)、FSMC_BTRx(片选时序)、FSMC_BWTRx(片选写时序)。

21、RTC时钟配置必须要用到BKP寄存器,BKP寄存器在单片机复位、电源复位、待机唤醒模式下是不会更改值的,他的供电由VDD供电,VDD被切断后自动切换至外部的VBAT供电。

22、要修改BKP寄存器的值,必须取消其写保护的标志。BKP寄存器在上电时自动写保护。

23、Stm32有三种省电模式:

三种省电模式中,耗电量从上到下依次降低,待机模式的电流仅为2uA。

24、从待机模式中唤醒单片机等效于让单片机复位,但是电源寄存器的值会有一个标志位指示单片机是被唤醒的,不是被复位的。

25、ADC的时钟不要超过14MHz,否则转换精度会下降。最大转换速率为1MHz,即转换周期为1us(14MHz,采样周期为1.5个ADC时钟)

26、Tcovn=采样时间+12.5个周期。采样时间尽量选长一点,这样精度高一些,但是转换速率下降,这也是有利必有弊。

27、

28、拿ARM7TDMI来说,T代表Thumb指令集,D是说支持JTAG调试(Debugging),M意指快速乘法器,I则对应一个嵌入式ICE模块。

29、MMU作为嵌入式处理器与应用处理器的分水岭标志A具有内存管理单元的嵌入式处理器可以定位为应用处理器。

这么说M系列和A系列的处理器的区别在于A系列的处理器具有MMU单元可以进行内存模块的管理。

30、ARM处理器有两种状态:ARM状态和Thumb状态。

31、这张图说明了一切:Thumb2指令集做了一件很伟大的事情:将16位和32位的指令集融为一体,兼容性非常强!(这么说CM3不支持某些32位ARM指令集)

32、

33、MSP是系统复位后使用的堆栈指针,PSP由用户的代码使用。两个堆栈指针为4字节对齐!!

34、在ARM编程领域中,凡是打断程序运行的事件,统称为异常(exception)。

35、因为存在LR(链接寄存器),所以可支持1级的子程序调用而不用压栈到内存,大大提高了运行速度。这就是说,我们在编程的时候,一级调用是不会耗费太多时间的,除非是二级调用!

36、处理器有两种操作模式:handler模式和线程模式。

处理器也有两种特权分级:特权级和用户级。这张图说明了一切:复位进入特权级线程模式,如果有异常,进入特权级的handler模式处理异常或中断例程,然后返回至特权级线程模式。通过修改CONTROL寄存器可以进入用户级线程模式。

37、两个高级定时器TIM1和TIM8是挂接在APB1总线上

38、STM32的外部中断是以组来区分的,也就是说PA0,PB0,PC0单片机是无法区分其中哪个触发的中断à均为EXIT0线中断服务例程。

所以,外部中断支持16路的中断分辨率。从另一个方面来讲,我们可以设置GPIO_EXTILineConfig(GPIO_PortSourceGPIOx, GPIO_PinSourcex);来开通中断线实现组内的不同中断。

39、DAC有两个寄存器,一个是DHR(Data HoldingRegister)数据保持寄存器,一个DOR(Data Output Register)数据输出寄存器。

真正起作用的是DOR寄存器,该寄存器把值给数模转换发生单元输出以VREF+为参考电压的电压值。

如果是硬件触发转换,系统将在1个ABP时钟周期后把值给DOR,如果是软件触发转换,时间为3个APB时钟周期。然后,均等待Tsetting时间(Typical为3us,Max为4us)后真正输出电压值。

40、DAC分8位模式和12位模式,其中后者可以选择左右对齐

来源:玩转单片机,转载此文目的在于传递更多信息,版权归原作者所有。

围观 87

作者: Miler Shao

来源:茶话MCU 微信号:stmcu832

所有的STM32芯片中都带有逐次逼近型ADC模块,关于它的应用非常广泛和频繁。不过,应用过程中时常也会遇到些问题,这尽力小结下,与大家分享出来算作一些提醒。

1、Vdda没有供电或没有正常供电;STM32系列众多,该参数不可一概而论,细节请参考各个芯片数据手册。



2、采样电阻取值不合适,跟采样时间不匹配,经常表现为输入电阻过大、配置的采样时间偏短。实际设计时可以参考下STM32官方各系列评估板的相关电路。另外可以参考ST官方的应用笔记AN2834。关于ADC 应用其它的应用笔记,可以去WWW.STMCU.COM.CN搜索ADC即可。


3、ADC上电开启到稳定需要一段时间,即Tstab,该参数在数据手册里有介绍。在使用寄存器操作时要特别注意这个时间。另外要注意给ADC外设上电、使能ADC功能、启动ADC转换、实质AD转换是不一样的动作和不同的时间点。


4、输入信号幅度超过ADC参考电压范围导致转换结果的数据错误。

5、芯片供电的波动尤其VREF的波动和外来干扰都会导致ADC转换值的异常。

6、在使用注入触发转换时,触发事件的时间间隔必须大于注入转换序列所需的转换时间。比方有两个注入通道所需转换时间为28 ADCLK,那触发事件的间隔必须大于28个ADCLK,比方29,30 个ADCLK等都可以。

7、大多数STM32的ADC模块在使用前需要校准。校准须在启动AD转换之前完成。原则上给ADC外设上电后校准一次就够,但当参考电压波动较大、温度变化较剧烈时需再次校准。

8、开启ADC的DMA功能,建议在ADC校准之后进行。换句话说校准ADC前不要使能其ADC的DMA功能。尤其涉及到多通道ADC DMA传输时要注意这个次序。

9、如果使用ADC的DMA传输,在启动AD转换时,DMA需配置好且被使能待命。

10、当使用内部SENSOR ADC通道时,注意这些通道从开启到稳定跟开启ADC模块一样都是需要时间的;针对这些特定传感器通道的AD采样时间,手册里往往有相关参数明确告知,请参照使用。比方内部温度传感器通道的采样时间推荐为17us.

11、ADC通道序列的修改应该保证在ADC的停止状态下进行。

12、在多通道ADC DMA传输时,经常出现因为缓冲区数据类型、源数据类型不一致导致的异常状况。这里主要是因为数据宽度不一致所导致的问题。

13、当外部信号被选择为注入转换的触发信号时,只有其上升沿才有效。

上面提到的都只是抛砖引玉的提醒, 设计应用时多留意下,特别是第7、8、9、12四点提醒。STM32的ADC外设在不同系列间也不完全相同,尤其涉及多个ADC模块配合采样转换的时候还是挺复杂的。任何时候都不忘多查看STM32英文参考手册和数据手册。

本文转自:茶话MCU(微信号:stmcu832),作者: Miler Shao,转载此文目的在于传递更多信息,版权归原作者所有。

围观 136

STM32F1xx官方资料:
《STM32中文参考手册V10》-第8章通用和复用功能IO(GPIO和AFIO)
《Cortex-M3权威指南(中文)》第5章 位带操作

硬件连接


假设跑马灯实验的硬件连接如上图所示,LED0连接PB5,LED1连接PE5。由于在LED的另一端是VCC3.3,所以当PB5或PE5为低电平的时候,LED灯会亮。此时GPIO应采取推挽输出的模式。

GPIO的相关配置寄存器

STM32的每组GPIO口包括7个寄存器。也就是说,每个寄存器可以控制一组GPIO的16个GPIO口。

这7个寄存器分别为:
- GPIOx_CRL:端口配置低寄存器(32位)
- GPIOx_CRH:端口配置高寄存器(32位)
- GPIOx_IDR:端口输入寄存器(32位)
- GPIOx_ODR:端口输出寄存器(32位)
- GPIOx_BSRR:端口位设置/清除寄存器(32位)
- GPIOx_BRR:端口位清除寄存器(16位)
- GPIOx_LCKR:端口配置锁存寄存器(32位)

端口配置低寄存器(GPIOx_CRL)


由于每个GPIO口需要4位来进行配置输入输出模式(2位配置MODE,2位配置CNF),这样的话每组16个GPIO口则需要64位,这也就表明需要两个32位寄存器。于是GPIOx_CRL用于配置GPIO0-GPIO7的输入输出模式。同理GPIOx_CRH则用于配置GPIO8-GPIO15的输入输出模式。

同时对于上面的这个表可以总结出端口位配置的信息:


需要注意的是,当MODE选择00,CNF为选择10时,代表着上拉/下拉输入模式。到底是上拉还是下拉呢?此时需要PxODR(端口输出寄存器)来确定,0为下拉输入,1为上拉输入。

端口输入寄存器(GPIOx_IDR)


IDR寄存器低16位,每个位控制该组GPIO口的一个IO口,对应的是该IO口的输入电平。在输入模式下,可以读取I/O端口的电平值;在输出模式下,也可以读取I/O端口的电平值(在开漏输出时,读取到的I/O端口的电平值,不一定就是输出的电平值)。

端口输出寄存器(GPIOx_ODR)


ODR寄存器的低16位,每个位控制该组GPIO口的一个IO口,对应的是该IO口的输出电平。在输出模式下,可以通过写寄存器的值,来达到某个IO口的电平输出;在输入模式下,还可以通过写值,来确定是上拉还是下拉输入模式。

端口位设置/清除寄存器(GPIOx_BSRR)


在GPIO的开漏输出模式或者推挽输出模式下,都可以直接给ODR寄存器赋值来进行某个IO口的电平输出;同时,也可以通过对BSRR进行赋值来达到对ODR寄存器的控制来进行对IO口的电平输出。其实,BSRR寄存器的底层也是对ODR寄存器的控制。

BSRR寄存器的低16位可以只对ODR赋1,高16位可以只对ODR赋0。这样的好处是,只可以对ODR的某些特定位产生影响,而不对其他的位产生影响。而且可以一次性对ODR的许多位同时进行控制。

端口位清除寄存器(GPIOx_BRR)


BRR寄存器的功能其实和BSRR寄存器的高16位的功能是一样的,通常情况下,使用BSRR寄存器的低16位来赋1,使用BRR寄存器来赋0。

GPIO的寄存器版本

1. 使能IO口时钟。配置寄存器RCC_APB2ENR;
2. 初始化IO口模式。配置寄存器GPIOx_CRH/CRL;
3. 操作IO口,输出高低电平。配置寄存器GPIOX_ODR或者BSRR/BRR。

具体的程序内容:

void LED_Init(void){
	RCC->APB2ENR|=1<<3;
	RCC->APB2ENR|=1<<6;
	
	GPIOB->CRL&=0xFF0FFFFF;
	GPIOB->CRL|=0x00300000;
	
	GPIOB->ODR|=1<<5;
	
	GPIOE->CRL&=0xFF0FFFFF;
	GPIOE->CRL|=0x00300000;
	
	GPIOE->ODR|=1<<5;
}
 int main(void)
 {	
	LED_Init();
	delay_init();
	while(1){
		GPIOB->ODR|=1<<5;
		GPIOE->ODR|=1<<5;
		
		delay_ms(500);
		
		GPIOB->ODR&=~(1<<5);
		GPIOE->ODR&=~(1<<5);
		
		delay_ms(500);
			
	}
 }

GPIO的库函数版本

需引用的文件:stm32f10x_gpio.h、stm32f10x_rcc.h、misc.h

需定义的文件:led.h

GPIO库函数介绍

  •  1个初始化函数:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);

作用:初始化一个或者多个IO口(同一组)的工作方式和速度。该函数主要是操作GPIO_CRL(CRH)寄存器,在上拉或者下拉的时候有设置BSRR或者BRR寄存器 。

注意:外设(包括GPIO)在使用之前,几乎都要先使能对应的时钟。

  •  2个读取输入电平函数:

uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

作用:读取某个(某组)GPIO的输出电平。实际操作的是GPIO_ODR寄存器。

  •  4个设置输出电平函数:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);

作用:设置某个IO口输出为高电平(低电平)。实际操作BSRR寄存器。后两个函数的作用类似。

GPIO库函数版本的跑马灯

1. 使能IO口时钟。调用函数RCC_APB2PeriphColckCmd();

2. 初始化IO口模式。调用函数GPIO_Init();

3. 操作IO口,输出高低电平。调用函数GPIO_SetBits();GPIO_ResetBits()。

具体的程序内容:

void LED_Init(void){
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_5);
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOE,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOE,GPIO_Pin_5);
	
}
 int main(void)
 {	
	LED_Init();
	delay_init();
	while(1){
		GPIO_SetBits(GPIOB,GPIO_Pin_5);
		GPIO_SetBits(GPIOE,GPIO_Pin_5);
		
		delay_ms(500);
		
		GPIO_ResetBits(GPIOB,GPIO_Pin_5);
		GPIO_ResetBits(GPIOE,GPIO_Pin_5);
		
		delay_ms(500);
		
	}
 }

GPIO的位操作版本

位操作的原理

把每位膨胀为一个32位的地址,当访问这些地址的时候就达到了访问该位的目的。比如说BSRR寄存器有32个位,那么可以映射到32个地址上,我们去访问(读-改-写)这32个地址就达到访问32个比特的目的。

也就是说,位操作就是可以读、写单独的一个比特位,由于在STM32中没有像51单片机的sbit来实行位定义,但是它可以通过位带别名区来实现。 


哪些区域支持位操作:

SRAM 区的最低 1MB 范围,0x20000000 ‐ 0x200FFFFF(SRAM区中的最低 1MB);
片内外设区的最低 1MB范围,0x40000000 ‐ 0x400FFFFF(片上外设区中最低 1MB)。
位带区:支持位带操作的地址区

位带别名:对别名地址的访问最终作用到位带区的访问上(注意:这中间有一个地址映射过程)

这两个1MB的空间可以像普通RAM一样操作外(修改内容时用读、改、写),它们还有自己的位带别名区,位带别名区把这1MB的空间的每一位膨胀为一个32位的字。确切的说,这个字就是一个地址,当操作这个地址时,就可以达到操作这个位带区某个位的目的。 

在位带区中,每个比特位都映射到别名地址区的一个地址,注意,这只是只有LSB有效的字(最低一位有效的字)。当一个别名地址被访问时,会把该地址转换为为位带操作。

映射方式

对片内外设位带区的某个比特位,记它的所在字节的地址为A,位序号为n(0<=n<=7),则该比特位在别名区的地址为:

AliasAddr = 0x42000000 + ((A - 0x40000000) * 8 + n) * 4
          = 0x42000000 + (A - 0x40000000) * 32 + n * 4

一开始,我对n(0<=n<=7)很不理解,既然n表示位序号,为什么不是0<=n<=31呢?其实是我忽略了“所在字节”四个字,也就是说在位带区中,不是以一个寄存器一个寄存器为分隔单元,而是以一个字节一个字节来分隔单元的。 

对于映射的公式,稍微解释一下:

1. A - 0x40000000 = 当前字节偏离外设基地址的偏移字节数 ;

2. 偏移字节数 * 8 = 偏移了多少位 ;

3. 因为位带区每一位对应位带别名区的一个地址(32位4字节),而地址是以字节计算的,所以位带别名区对应偏移量最后一个的地址 = 偏移了多少位 * 4 ;

4. n * 4 = 偏移量后面的n位对应位带别名区的地址。
同理,对于SRAM位带区的某个比特位,记它所在字节地址为A,位序号为n(0<=n<=7),则该比特位在别名区的地址为:

AliasAddr = 0x22000000 + ((A - 0x20000000) * 8 + n) * 4 
          = 0x22000000 + (A - 0x20000000) * 32 + n * 4

只是对位带基地址和位带别名区基地址做了改变即可。

为了方便操作,我们可以把这两个公式合并成一个公式,把“位带地址 + 位序号”转换成别名地址:

//把位带区地址 + 位序号转换成位带别名去的宏
#define BITBAND(addr, bit_num) ((addr & 0xf0000000) + 0x02000000 + ((addr & 0x00ffffff) << 5) + (bit_num << 2))

位带操作的优越性


位带操作可以简化跳转的判断。比如之前需要跳转到某一位时,必须这样做:
- 读取整个寄存器;
- 掩蔽不需要的位;
- 比较并跳转。

现在使用位带操作只需要:
- 从位带别名区读取状态位;
- 比较并跳转。

在SYSTEM文件夹的sys.h文件中,对GPIO输入输出部分功能实现了位带操作。具体的实现过程如下面的程序所示:

#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)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C  
#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

通过PAout(n)实现对GPIOA_ODR寄存器的第n位赋值的功能,通过PAin(n)实现对GPIOA_IDR寄存器的第n位赋值的功能。按照这种写法,之前库函数版本的主程序可以改写成:

 int main(void)
 {	
	LED_Init();
	delay_init();
	while(1){
		PBout(5)=1;
		PEout(5)=1;
		
		delay_ms(500);
		
		PBout(5)=0;
		PEout(5)=0;
		
		delay_ms(500);
		
	}
 }

本文转自:CSDN - Yngz_Miao,转载此文目的在于传递更多信息,版权归原作者所有。

围观 75

背景

STM32G071的PD0,PD2作为外部中断使用,外部接10K上拉电阻拉到3.3V。外部设备被触发后电平变为低电平,平常保持高电平信号。

问题

在以上背景下,按道理外部设备正常时(未触发中断),IO口输入电平应该是3.3V。但是实际上测量到的却是0.9V,这跟触发后的0V,同样会被单片机识别为低电平,故无法产生电平跳变而触发中断。

分析解决


这个引脚的电平类型为FT_c,我们知道FT是容忍5V的意思,那么FT_c是什么意思?不妨看看说明:


然后去用户手册查看GPIO相关说明,找到以下内容:


基本上了解状况了,再看一下相关寄存器说明:


具体说明:


看了以上说明,基本明白怎么操作了。在初始化IO的时候,应该把SYSCFG registers的UCPD2_STROBE位设置一下就ok了。

HAL库操作:HAL_SYSCFG_StrobeDBattpinsConfig(SYSCFG_CFGR1_UCPD2_STROBE);

LL库操作:MODIFY_REG(SYSCFG->CFGR1, (SYSCFG_CFGR1_UCPD1_STROBE | SYSCFG_CFGR1_UCPD2_STROBE), SYSCFG_CFGR1_UCPD2_STROBE);

再提醒下,其实PA8和PA15也是这样的。如果我们不需要这个下拉电阻,需要按照上面的操作调整一下。

那么为什么STM32G071有这样的设置呢?看下面IO功能即可知道,这几个引脚其实是下面几个管脚的复用脚。



即TYPE-C充电管理的相关引脚,所以才会有这样的设置。若想进一步了解TYPE-C相关知识,可以自行查找相关资料。

来源:STM32单片机,作者:小马哥,转载此文目的在于传递更多信息,版权归原作者所有。

围观 177

页面

订阅 RSS - STM32