当前位置: 首页 > news >正文

STM3学习记录

一、串口
1.串口定义,将串口相关寄存器的首地址强制转化为串口结构体,方便通过结果体访问串口的寄存器

#define     __IO    volatile             /*!< Defines 'read / write' permissions */
typedef struct
{__IO uint32_t SR;         /*!< USART Status register,                   Address offset: 0x00 */__IO uint32_t DR;         /*!< USART Data register,                     Address offset: 0x04 */__IO uint32_t BRR;        /*!< USART Baud rate register,                Address offset: 0x08 */__IO uint32_t CR1;        /*!< USART Control register 1,                Address offset: 0x0C */__IO uint32_t CR2;        /*!< USART Control register 2,                Address offset: 0x10 */__IO uint32_t CR3;        /*!< USART Control register 3,                Address offset: 0x14 */__IO uint32_t GTPR;       /*!< USART Guard time and prescaler register, Address offset: 0x18 */
} USART_TypeDef;#define PERIPH_BASE           0x40000000UL /*!< Peripheral base address in the alias region */
#define APB1PERIPH_BASE       PERIPH_BASE
#define USART3_BASE           (APB1PERIPH_BASE + 0x00004800UL)
#define USART3              ((USART_TypeDef *)USART3_BASE)

2.若有奇偶校验位 16比特数据传输

/* In case of 9bits/No Parity transfer, pTxData needs to be handled as a uint16_t pointer */
if ((husart->Init.WordLength == USART_WORDLENGTH_9B) && (husart->Init.Parity == USART_PARITY_NONE))
{ptxdata8bits  = NULL;ptxdata16bits = (const uint16_t *) pTxData;
}
else
{ptxdata8bits  = pTxData;ptxdata16bits = NULL;
}

3.采样频率是波特率的16倍,起始位将采样位置定位到一位的中间
4.串口有一个字节的缓冲
现象:
串口助手连续发送received@abc
打印数据为:

9 received@
10 areceived@
10 areceived@
10 areceived@
10 areceived@

解释:
从框图得知(只看接收部分结构):
数据先进入接收移位寄存器,再进入接受数据寄存器
串口助手第一次发送received@abc,程序读取到@停止,即读到received@后停止,但是a进入串口接收移位寄存器,等接收数据寄存器读空,接收移位寄存器数据将移入接收数据寄存器,因此第二次读取数据时,从接收数据寄存器读取到的是字符’a’,后续读取received@,也就是说,第二次开始,读取的是areceived@。
在这里插入图片描述

  while (1){rec_len = get_usart_line(rx_buf, rx_max_size, 500000);rx_buf[rec_len] = '\0';if(rec_len > 0) {char str[100];rec_len = snprintf(str, sizeof(str) / sizeof(str[0]), "%d %s\r\n", rec_len, rx_buf);HAL_UART_Transmit_DMA(&huart6, (uint8_t *)str, rec_len);// HAL_UART_Transmit_DMA(&huart6, rx_buf, rec_len);len = rec_len;}rx_buf[0] = 0;/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}int get_usart_line(uint8_t *rx, int rx_max_size, int timeout) {int t = 0, i;uint8_t rx_buf = 0;for(i = 0; rx_buf != '@' && t < timeout && i < rx_max_size; t++) {if(HAL_UART_Receive(&huart6, &rx_buf, 1, 0) == HAL_OK) {// if(HAL_UART_Receive_DMA(&huart6, &rx_buf, 1) == HAL_OK) {rx[i++] = rx_buf;}}return t >= timeout ? -1 : i;
}

二、定时器
1.允许定时器中断

HAL_TIM_Base_Start_IT(&htim2);

2.重写定时器回调函数
函数原型:

/*** @brief  Period elapsed callback in non-blocking mode* @param  htim TIM handle* @retval None*/
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{/* Prevent unused argument(s) compilation warning */UNUSED(htim);/* NOTE : This function should not be modified, when the callback is needed,the HAL_TIM_PeriodElapsedCallback could be implemented in the user file*/
}

重写:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2) {static int cnt = 0;if(cnt < 500) led_on();else if(cnt < 1000) led_off();else cnt = 0;cnt++;}
}

三、DMA
STM32 DMA结构图
1.触发方式
硬件触发:UART、ADC、IIC等外设设备
软件触发:M2M置1,内部触发,尽快连续触发传输计数器,快速转运数据,不能喝自动重装一起使用,否则会无限转运。
2.开关控制
当没有开启自动重装,计数器清零时需要先关闭DMA,重新填充初值,再开启DMA,注意必须先关闭
3.DMA通道
每个通道连接的外设不同,必须选择对应外设的通道才能转运,例如STM32F427IIH DMA+UART6 RX必须选择DMA2通道1或通道2 TX必须选择通道DMA2的通道6或7。

四、SPI协议
1.硬件电路
特点:输出引脚始终输出,输入引脚始终输入,主机MISO输出,从机MISO输入。
引脚模式:输出配置为推挽输出,输入为浮空或则上拉。
从机:所有从机输出默认高阻态,否则可能造成短路,例如从机1默认低电平,从机2获得控制器权,发送高电平,将导致短路。
在这里插入图片描述
2.移位模型
一个时钟周期的一个边沿,主机和从机移位寄存器同时左移一位,根据移出的那一位将信号线置高或者拉低;另一个边沿,主机和从机同时从信号线将电平读入,并将电平放入移位寄存器的最低位,之后移位寄存器左移一位。
左边为最高位,SPI是高位先行
在这里插入图片描述
3.传输模式
按scl空闲时状态(CPOL控制),输入和输出时机(CPHA控制),分四种模式
模式0:scl空闲低电平,第一个边沿读入,第二个边沿输出
模式1:scl空闲低电平,第一个边沿输出,第二个边沿读入
模式2:scl空闲高电平,第一个边沿读入,第二个边沿输出
模式3:scl空闲高电平,第一个边沿输出,第二个边沿读入
4.模式0时序
在SCK第一个边沿(上升沿),主机和从机从信号线读取最高位;第二个边沿(下降沿),主机和从机将最高位移到信号线,后续也是上升沿读取,下降沿写入,由于SCK第一个边沿就得读取,因此在SS下降沿就应该先讲数据写入到数据线上
在这里插入图片描述

5.全部模式时序四种模式的时序图,分别为模式3 1 2 0在这里插入图片描述
6.软件模拟spi读取mpu6500
协议时序核心代码:

uint8_t swap_one_byte(uint8_t data) {/* 优化:模拟移位寄存器,从左边移出一位,右边移入一位时序:模式1:空闲时scl为低电平,第一个边沿读取数据;即上升沿后立即读取数据,下降沿后立即写入数据第一个数据是在ncss的下降沿发送*/for(int i = 0; i < 8; i++) {mosi(data & 0x80);data <<= 1;scl(1);if(miso()) data |= 0x01;scl(0);} return data;
}

BUG记录:
现象:miso读取的电平始终为低电平
排查:
1.烧录robomaster官方代码,可正常读取,排除硬件问题
2.排查代码:GPIO配置、协议时序实现、GPIO读写接口
最后发现是GPIO读写接口错误,具体如下:
使用宏定义读写GPIO口,但是GPIO口编号并不是1,2,3,4,5,而是需要使用HAL库的宏GPIO_PIN_x

#define SPI_NCSS 6
#define SPI_SCL 7
#define SPI_MOSI 9
#define SPI_MISO 8
#define SPI_GPIO GPIOF

修改后:

#define SPI_NCSS GPIO_PIN_6
#define SPI_SCL GPIO_PIN_7
#define SPI_MOSI GPIO_PIN_9
#define SPI_MISO GPIO_PIN_8
#define SPI_GPIO GPIOF

现象:烧录后第一次读取mpu6500 id为0x00,应为0x70
原因:scl配置为默认高电平,mpu6500采用模式0,即第scl空闲时为0,导致初始时mpu无法侦测到空闲位,从而导致mpu无法正常读取到第一位数据。
应在0x75寄存器读取设备id,拼接读写位后(读写位拼接到最高位)为0xf5,由于上述原因,mpu实际接受到的数据为0x75,这意味着在0x75寄存器写入数据,因此无法读取到0x75寄存器,id为0是因为MISO默认电平为0;
解决方案:scl配置为默认低电平
五、IIC协议
(1)硬件电路
(2)位时序
起始时序:scl高电平期间,sda下降沿
停止时序:scl高电平期间,sda上升沿
读写时序:高位先行,高电平期间读取,低电平期间写入,因此高电平期间sda需要保持稳定,低电平期间sda可以变化;
特点:
①起始停止时序是在scl高低电平期间,为读写是在scl低电平期间,避免了发生数据期间产生误起始和停止;
②半双工通信,scl低电平写,高电平读,无法同时读写。
1.起始时序

void start(void) {wsda(1);scl(1);wsda(0);scl(0);
}

2.停止时序

void stop(void) {wsda(0);scl(1);wsda(1);scl(0);
}

3.读时序

uint8_t read_byte(uint8_t ack) {uint8_t p = 0x80, ret = 0;scl(0);for(; p; p >>= 1) {scl(1);if(rsda() == 1) ret |= p;scl(0);}send_ack(ack);return ret;
}

4.写时序

void write_byte(uint8_t data) {uint8_t p = 0x80;for(; p; p >>= 1) {scl(0);wsda(data & p);scl(1);}scl(0);
}

(3)应答机制
在这里插入图片描述
(4)指定地址写
以AT24C02为例,其地址为0xA0,地址是芯片厂家设置的,用户可以通过短接引脚调整低三位,这里使用默认的0xA0
1.发送起始信号;
2.发送设备地址,将读写位拼接到地址最低位(地址为7位),由于是写,最低位置0,即0xA0;
3.等待应答,主机应该释放总线(scl sda置高),有应答说明从机收到了,主机可以继续发送;
4.发送将要写入数据的寄存器地址;
5.等待应答;
6.发送一字节数据;
7.等待应答;
8.发送停止信号。

uint8_t send_byte_to_register(uint8_t reg, uint8_t data) {start();write_byte(AT24C02_ADDR | 0x00);// 主机拉高sda,从机没有拉低sda,说明从机没有响应,结束if(wait_ack()) {return 0;}write_byte(reg);wait_ack();write_byte(data);wait_ack();stop();HAL_Delay(1);return 1;
}

(5)当前地址读
当前地址指针:iic传感器或期间内部有一个指针,指向一个寄存器,并且在连续读写数据时会自动递增
1.发送起始信号;
2.发送设备地址,将读写位拼接到地址最低位(地址为7位),由于是读,最低位置1,即0xA1;
3.等待应答,主机应该释放总线(scl sda置高),有应答说明从机收到了,主机可以继续;
4.读取一字节数据;
5.发送非应答,发生一位1,表示不需要再给主机发生数据了,再代码中,这一步包含在了read_byte;
6.发送停止信号。
第三步后,没有指定寄存器,从机发现读写位为1,会将当前地址指针所指向数据发送出去,这个也是从机厂家决定的

uint8_t read_byte_from_register(uint8_t reg) {uint8_t ret;start();write_byte(AT24C02_ADDR | 0x00);// 主机拉高sda,从机没有拉低sda,说明从机没有响应,结束wait_ack();write_byte(reg);wait_ack();ret = read_byte(1);stop();return ret;
}

(6)指定地址读
实际上,指定地址读是指定地址写+当前地址读,1-5步和指定地址写的1-5步,6-11步是当前地址读的1-6步
1-5步的目的:使当前地址指针指向第4步指定的目标寄存器地址
6-11步目的:读取当前地址指针所指向数据,由于1-5步已经使当前地址指针指向了目标寄存器,读取的就是目标寄存器的数据
1.发送起始信号;
2.发送设备地址,将读写位拼接到地址最低位(地址为7位),由于是写,最低位置0,即0xA0;
3.等待应答,主机应该释放总线(scl sda置高),有应答说明从机收到了,主机可以继续发送;
4.发送将要写入数据的寄存器地址;
5.等待应答;

6.发送起始信号;
7.发送设备地址,将读写位拼接到地址最低位(地址为7位),由于是读,最低位置1,即0xA1;
8.等待应答,主机应该释放总线(scl sda置高),有应答说明从机收到了,主机可以继续;
9.读取一字节数据
10.发送非应答,发生一位1,表示不需要再给主机发生数据了;

uint8_t read_byte_from_register(uint8_t reg) {uint8_t ret;start();write_byte(AT24C02_ADDR | 0x00);// 主机拉高sda,从机没有拉低sda,说明从机没有响应,结束wait_ack();write_byte(reg);wait_ack();start();write_byte(AT24C02_ADDR | 0x01);wait_ack();ret = read_byte(1);stop();return ret;
}

11.发送停止信号。
(7)连续读写
连续读写并不是所有期间都支持,对于支持的期间,只需按(4)、(5)、(6)的步骤,在发生停止位前连续读写,或者可以封装(4)、(5)、(6),在一个for循环中调用封装好的读写函数
(8)遇到bug
现象:读取数据错乱
原因:读写一位后的延时太长或太短,延时时间需要实验

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • RabbitMQ创建交换机和队列——配置类 注解
  • 【C++】Linux平台C++实现简单socket通信
  • Python精选200Tips:126-130
  • SciPy 插值
  • CI/CD持续集成和持续部署以及相关软件的使用
  • xLSTM模型学习笔记
  • 商务办公tips1:如何将网页转换为pdf
  • 视频监控平台是如何运作的?EasyCVR视频汇聚平台的高效策略与实践
  • Adobe After Effects 2022 安装包及全家桶下载:永久免费提供完整安装指南
  • [数据集][目标检测]男女性别检测数据集VOC+YOLO格式9769张2类别
  • GeoPandas在地理空间数据分析中的应用
  • 源代码审查范围为:
  • Maven从入门到精通(三)
  • 力扣3014.输入单词需要的最少按键次数I
  • 11、LLaMA-Factory自定义数据集微调
  • [译]前端离线指南(上)
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • Consul Config 使用Git做版本控制的实现
  • Druid 在有赞的实践
  • ES2017异步函数现已正式可用
  • git 常用命令
  • idea + plantuml 画流程图
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • java小心机(3)| 浅析finalize()
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • miaov-React 最佳入门
  • Shadow DOM 内部构造及如何构建独立组件
  • Shell编程
  • XML已死 ?
  • 分类模型——Logistics Regression
  • 解析带emoji和链接的聊天系统消息
  • 力扣(LeetCode)56
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 微信开源mars源码分析1—上层samples分析
  • 微信支付JSAPI,实测!终极方案
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • #### go map 底层结构 ####
  • #HarmonyOS:基础语法
  • #基础#使用Jupyter进行Notebook的转换 .ipynb文件导出为.md文件
  • $.ajax()参数及用法
  • (1)Android开发优化---------UI优化
  • (4)STL算法之比较
  • (C#)获取字符编码的类
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (HAL库版)freeRTOS移植STMF103
  • (Windows环境)FFMPEG编译,包含编译x264以及x265
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (三)docker:Dockerfile构建容器运行jar包
  • (三十)Flask之wtforms库【剖析源码上篇】
  • (一)u-boot-nand.bin的下载
  • (转)一些感悟
  • **python多态
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**