赞
踩
STMF103用了这么多年,项目也做过不少,当然也遇到过很多问题,虽然现在使用起来也算得上得心应手,
但是有些地方还是用起来还是得查查看看,所以就专门开设一篇文章,放一些基本笔记,方便以后查找。
这里的笔记知识大多都是以前网上查找保存的笔记,后续维护在自己遇到问题的时候会多放些自己踩过的坑。
uint32_t get_time_clock_freq(TIM_TypeDef *t) { uint32_t time_clock_freq = 0; RCC_ClocksTypeDef clocks; RCC_GetClocksFreq(&clocks); if(t == TIM1 || t == TIM8) { time_clock_freq = clocks.PCLK2_Frequency; if(clocks.HCLK_Frequency/clocks.PCLK2_Frequency > 1) { time_clock_freq <<= 1; } } else { time_clock_freq = clocks.PCLK1_Frequency; if(clocks.HCLK_Frequency/clocks.PCLK1_Frequency > 1) { time_clock_freq <<= 1; } } return time_clock_freq; } ———————————————— 版权声明:本文为CSDN博主「矜辰所致」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_42328389/article/details/118212278
DMA用在什么地方合适?形象地说,DMA是MCU内的搬运工,通过DMA把数据从外设和内存之间的自动搬迁,节省软件的开销。
STM32 的DMA有三种模式:内存-外设,外设-内存,内存-内存。
举几个例子:
USART串口发送:没DMA的情况,要发送大量数据,需要每一个字节发送后产生完成中断或轮询旗标,然后软件介入把下一个数据放入TDR寄存器,这样会有大量中断或等待消耗程序的资源,如果采用DMA的话,至需要预先在DMA通道设定要发送的数据的第一字节的内存地址(例如: &buf[0]), 和设定需要发送多少字节,开始发送后,DMA就会像z指针一样完成一节,指针自动递增或减自动移动数据到发送寄存器,直至发送完成会有一次中断通知。中间过程软件完全可以不介入。
ADC采集:多次采样或循环采样,同样地没DMA的话,采样完毕中断,搬移ADC的采样值,出现大量中断,如果是高速采样情况更甚。可以通过DMA采样完成后自动写入你指定的数组地址,一个循环后DMA一个完成中断通知你做后续的事情,例如平均算法之类的。
DAC波形输出:把波形的样本建立一个数组,使用DMA循环发送。
用和不用当然都可以发送。不用DMA发送是需要单片机实时参与,由单片机一个一个地发送数据并进行监控。但是如果用DMA,设置了起始地址,数据大小等参数后,就直接由专门的一个DMA模块进行数据发送,发送过程中单片机无需参与。发送完后会产生中断告知单片机。由此可知用DMA可以节省单片机资源,让单片可以在同一时间里干更多事。
起始信号:时钟线高电平的时候,数据线由高变低
void i2c_start(void)
{
sda_high;
delay_us(5);
scl_high;
delay_us(10);
sda_low;
delay_us(10);
scl_low; //使SCL置低,准备发送或者接受数据
delay_us(10);
}
结束信号:时钟线高电平的时候,数据线由低变高
void i2c_stop(void)
{
sda_low;
delay_us(5);
scl_low;
delay_us(10);
scl_high;
delay_us(5);
sda_high;
delay_us(10);
}
ACK:正常的时钟周期,保持低电平,主机和从机都需要响应ACK
void IIC_Ack(void)
{
MYIIC_CLK_LOW; //SCL为低,SDA为低,SCL为高,SDA为低,应答低电平有效,SCL为低,产生应答信号
// MYSDA_OUT;
MYIIC_DATA_LOW;
delay_us(10);
MYIIC_CLK_HIGH;
delay_us(10);
MYIIC_CLK_LOW;
/*注意,加上了下面一句,释放总线,数据才正确*/
delay_us(10);
MYIIC_DATA_HIGH;
}
NACK:正常的之中周期,保持高电平,一般在接收数据的最后一个字节,发送NACK,表示接收完成
void IIC_NAck(void)
{
scl_low; //SCL为低,SDA为高,SCL为高,SCL为低
// MYSDA_OUT;
sda_high;
delay_us(10);
scl_high;
delay_us(10);
scl_low;
}
在添加I2C设备的时候,读取数据时候,主机需要返回ACK给从机,但是得注意主机返回ACK最后需要释放 SDA线,在以前使用的过程中,遇到过没有释放也可以成功的例子。
但是最近在使用STM32L051添加欧姆龙 D6T-1A-02非接触温度传感器的时候遇到,如果不在返回ACK后释放SDA线控制权,从机无法正常发送数据(因为SDA先被主机拉低了,从机无法拉高):
使用STMCubeMX生成工程后,会自动设置好NVIC中断优先级,使能了NVIC中断,在usart.c
下的`void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)中有:
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
但是串口的接收中断,是没有自动使能的,我们需要自己使能,但是如何使能,比如:
/*USART_Enocean_BUF , USART_RX_BUF 是你的接收缓冲区*/
HAL_UART_Receive_IT(&hlpuart1, USART_Enocean_BUF, 1);
HAL_UART_Receive_IT(&huart2, USART_RX_BUF, 1);
如果你需要接收一帧数据,可以开启IDLE
中断,初始化后,运行前加上
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
那么还有一句:
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE);
这句话是不是开启了前面的HAL_UART_Receive_IT
就不需要用了呢?
实际上测试是这么回事,以前在标准库中使用串口通讯UART_IT_RXNE
基本上都必须使能的,那么为什么?
见文章:RT-Thread 应用篇 — 在STM32L051上使用 RT-Thread (四、无线温湿度传感器 之 串口通讯)
《4.2 串口通讯细节问题》
屏蔽外部中断
关闭中断
EXTI->IMR &= ~(EXTI_Linex);
打开中断
EXTI->IMR |= EXTI_Linex;
在Hal库中:
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn) { /* Check the parameters */ assert_param(IS_NVIC_DEVICE_IRQ(IRQn)); /* Enable interrupt */ NVIC_EnableIRQ(IRQn); } /** * @brief Disable a device specific interrupt in the NVIC interrupt controller. * @param IRQn External interrupt number . * This parameter can be an enumerator of IRQn_Type enumeration * (For the complete STM32 Devices IRQ Channels list, please refer to stm32l0xx.h file) * @retval None */ void HAL_NVIC_DisableIRQ(IRQn_Type IRQn) { /* Check the parameters */ assert_param(IS_NVIC_DEVICE_IRQ(IRQn)); /* Disable interrupt */ NVIC_DisableIRQ(IRQn); }
举例:
...
HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);
...
if(...){
HAL_NVIC_DisableIRQ(EXTI4_15_IRQn);
}
对寄存器的操作,使用位操作是效率最高的方式。
置位
int a = 0x123;把a的 bit 7,8位 置位:
int b = a | (1<<7) | (1<<8);
GPIOA -> ODR |=1<<5; //PA5输出 1,其他位不变
清位
int a = 0x123; 把a 的 bit7,8 清0;
int b = (a & ~( 1<<7))&(~ (1<<8));
中断中使用printf函数死机问题,本来只是为了测试看一下,实际应用当然是在中断中不能使用printf的,但是以前确实也没有遇到死机问题,这一次在使用FreeRTOS 的时候,中断中只要使用printf就死机,程序无法调度了,具体原因还不知道,只能避免,相关部分截图先放在下面,以后如果知道答案可以来分析一下。
在中断中养成不用打印的好习惯!!!!!切记,测试可以,实际项目不可以!
硬件平台 STM32L051C8T6:
在使用AVR,8051单片机的时候,如果在变量定以前加上 code
关键字,表示 定义的数据要放在 ROM中:
code uint8 EEP_Info[EEP_TYPE_Num][2] = {
0x02,0x01,
0x02,0x02,
0x02,0x03,
0x02,0x04,
0x02,0x05
};
那么对于 STM32 而言呢,需要加关键字 const
msp430也是。
const uint8 EEP_Info[EEP_TYPE_Num][2] = {
0x02,0x01,
0x02,0x02,
0x02,0x03,
0x02,0x04,
0x02,0x05
};
在结构体后面的 attribute ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,比如:
typedef struct
{
u8 ID1;
u8 ID2;
u8 ID3;
u8 ID4;
u8 ID5;
u8 ID6;
u32 state;
}__attribute__ ((packed)) BlueID_STRUCT;
不对齐,结构体的长度,就是各个变量长度的和!
当然也可以指定字节对齐(4字节对齐):
typedef struct
{
u8 ID1;
u8 ID2;
u8 ID3;
u8 ID4;
u8 ID5;
u8 ID6;
u32 state;
}__attribute__ ((aligned(4))) BlueID_STRUCT;
C语言要定义外部文件可使用的结构体和结构体变量。
需要保证 extern 声明的 文件中,有结构体的定义
所以需要改成 在 定义的文件中 声明:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。