串口协议、I2C协议、SPI协议总结-1
https://developer.aliyun.com/article/1508011
(4)串口通信非中断实现
我用STM32CubeMX配置如下,仅供参考:
RCC:配置外部高速晶振
SYS:Debug设置成Serial Wire
时钟树配置:
串口配置:
代码示例:将接收到的数据发送到串口
#include <string.h> // main.c HAL_UART_Transmit(&huart1, "hello zgl\n", strlen("hello zgl\n"), 100); unsigned char ch[20] = {0}; while (1) { HAL_UART_Receive(&huart1, ch, 19, 100); HAL_UART_Transmit(&huart1, ch, strlen(ch), 100); memset(ch, 0, strlen(ch)); }
使用串口重映射功能,打开MicroLIB库
代码示例:printf 替换 HAL_UART_Transmit
#include <stdio.h> #include <string.h> unsigned char ch[20] = {0}; int fputc(int ch, FILE *f) { unsigned char temp[1]={ch}; HAL_UART_Transmit(&huart1,temp,1,0xffff); return ch; } // main函数部分 HAL_UART_Transmit(&huart1, "hello zgl\n", strlen("hello zgl\n"), 100); while (1) { HAL_UART_Receive(&huart1, ch, 19, 100); printf(ch); memset(ch, 0, strlen(ch)); }
通过中断的方法接受串口调试助手发送的字符串,并将其发送回串口调试助手。
CubeMX新增加一个打开中断,其它同上
代码示例:
#include <stdio.h> int fputc(int ch, FILE *f) { unsigned char temp[1]={ch}; HAL_UART_Transmit(&huart1,temp,1,0xffff); return ch; } //串口接收缓存(1字节) uint8_t buf=0; //定义最大接收字节数 200,可根据需求调整 #define UART1_REC_LEN 200 // 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节 uint8_t UART1_RX_Buffer[UART1_REC_LEN]; // 接收状态 // bit15, 接收完成标志 // bit14, 接收到0x0d // bit13~0, 接收到的有效字节数目 uint16_t UART1_RX_STA=0; // 接收完成回调函数,收到一个数据后,在这里处理 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // 判断中断是由哪个串口触发的 if(huart->Instance == USART1) { // 判断接收是否完成(UART1_RX_STA bit15 位是否为1) if((UART1_RX_STA & 0x8000) == 0) { // 如果已经收到了 0x0d (回车), if(UART1_RX_STA & 0x4000) { // 则接着判断是否收到 0x0a (换行) if(buf == 0x0a) // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1 UART1_RX_STA |= 0x8000; else // 否则认为接收错误,重新开始 UART1_RX_STA = 0; } else // 如果没有收到了 0x0d (回车) { //则先判断收到的这个字符是否是 0x0d (回车) if(buf == 0x0d) { // 是的话则将 bit14 位置为1 UART1_RX_STA |= 0x4000; } else { // 否则将接收到的数据保存在缓存数组里 UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf; UART1_RX_STA++; // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收 if(UART1_RX_STA > UART1_REC_LEN - 1) UART1_RX_STA = 0; } } } // 重新开启中断 HAL_UART_Receive_IT(&huart1, &buf, 1); } } // main函数部分 // 开启接收中断 HAL_UART_Receive_IT(&huart1, &buf, 1); while (1) { //判断判断串口是否接收完成 if(UART1_RX_STA & 0x8000) { printf("收到数据:"); // 将收到的数据发送到串口 HAL_UART_Transmit(&huart1, UART1_RX_Buffer, UART1_RX_STA & 0x3fff, 0xffff); // 等待发送完成 while(huart1.gState != HAL_UART_STATE_READY); printf("\r\n"); // 重新开始下一次接收 UART1_RX_STA = 0; } printf("hello zgl \r\n"); HAL_Delay(1000); }
以香橙派和树莓派示例,可以看我之前写过的文章(比较简略)
香橙派:
linux下实现串口功能_linux 串口实例-CSDN博客
树莓派:
二、I2C协议
- IIC全称Inter-Integrated Circuit (集成电路总线)
- 是由PHILIPS公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备。IIC属于半双工同步通信方式(只有一根双向的数据线SDA)
特点
- 简单性和有效性
由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件
- 多主控(multimastering)
其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当 然,在任何时间点上只能有一个主控。
构成
IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控 器件产生。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线 的SCL上。对于并联在一条总线上的每个IC都有唯一的地址。
2.IIC总线传输
IIC总线在传输数据的过程中一共有三种类型信号,分别为:起始信号、结束信号和应答信号,同时还要进行数据发送。
(1)起始信号和结束信号:
代码示例:
void IIC_Start() { scl = 1; sda = 1; _nop_(); sda = 0; _nop_(); } void IIC_Stop() { scl = 1; sda = 0; _nop_(); sda = 1; _nop_(); }
(2)应答信号:
发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
代码示例:
char IIC_ACK() { char flag; scl = 1; // 在时钟脉冲期间释放数据线 _nop_(); sda = 0; flag = sda; _nop_(); sda = 1; _nop_(); return flag; }
(3)数据发送的时序
代码示例:
void IIC_Send_Byte(char dataSend) { int i; /* 发一位字节数据 */ for (i = 0; i < 8; i++) { scl = 0; // 时钟线拉低,让数据线做好开始发送准备 sda = dataSend & 0x80; // 数据线获得发送数据最高位 _nop_(); // 延迟一会 scl = 1; // 时钟线拉高,发送数据 _nop_(); // 延迟一会让数据发送 scl = 1; // 发送完毕,时钟线重新拉低 _nop_(); dataSend = dataSend << 1; } }
三、SPI协议
有关SPI协议可以看我之前写过的文章:SPI协议和W25Q128详解-CSDN博客
三种协议对比图: