前言
本例是通过SaleaeLogic16逻辑仪观察波形,对前几篇实验博客涉及的协议进行分析,旨在深刻的理解不同协议下的通讯方式的异同。
以下是涉及到的相关博客
UART协议:stm32之串口通信 I2C协议:AHT20温湿度采集(I2C协议) SPI协议:玩转oled屏(基于SPI协议)
一、串口波形分析
本例使用到的是上一篇博客中的工程
基于C8T6核心板的ucOS文件移植
(一)keil虚拟波形仿真
首先在对应工程下点击新建文件
将以下内容复制进去并保存到对应的MDK-ARM中去
map 0x40000000, 0x40007FFF read write
map 0x40010000, 0x400157FF read write
map 0x40020000, 0x4007FFFF read write
map 0x50000000, 0x50060BFF read write
map 0x60000000, 0x60000FFF read write
map 0xE0000000, 0xE00FFFFF read write
其中文件名后缀是.ini ,保存类型一定要是All Files(*.*) 类型
然后配置一些模拟仿真的参数
此外,还需要注意的一点是选择仿真的时钟频率与所选芯片型号是否对应,我这里是C8T6,对应外部晶振频率为8MHz
然后开始仿真,打开逻辑分析仪
设置需要显示的通道
点击全速运行,等待一段时间,观察波形
等待一段时间后,波形如下
我们可以看到,串口USART1_SR通道每隔2s都会有一个下降沿,我们把其展开,得到了下面的图形 这一整块就是我们每隔2s定时发送的数据,只不过由于数据量太小,而波特率太高,所以在2s的周期里才会表现的像一个冲击。 我们可计算以下发送这一数据所需要的时间 发送数据:hello uc/OS! 欢迎来到RTOS多任务环境!\r\n ,总计约40个字节 波特率为115200bps 所需时间为(40*8)/115200 ,约为2.8ms 我们在后面逻辑分析仪对其分析的时候,会具体分析数据是怎么通过串口被发出去的。
(二)逻辑仪分析串口发送
在分析串口之前,我们首先观察以下串口的基本配置,这在下面的分析中也会用到 使用串口1、波特率为115200、停止位为1 bit、数据位为8 bit、无校验位等
具体配置如下
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
我们在逻辑分析仪器中可以看到,以下是一连串的密密麻麻的波形,我们把其展开分析
从逻辑分析仪中可以看到,传输一位数据大约需要8.7us 则1ms可传输约114.9425289 个数据位 则1s的时间可传输114.9425289 *1000 ≈114943bps 而理论速度应为115200bps
关于这部分的解释,有可能是因为SaleaeLogic16 逻辑仪设置的采样频率较低,遭成的误差
数据发送部分分析
对应字符的ASCII表 对比波形,可分析得到前两个数据发送的分别是’‘h’’, ‘‘e’’。需要注意的是 串口传输数据时先传送字符的低位,后传送字符的高位。即低位(LSB)在前,高位(MSB)在后。
串口通讯中是一个字符一个字符地传输,每个字符一位一位地传输,并且传输一个字符时,总是以“起始位”开始,以“停止位”结束,字符之间没有固定的时间间隔要求。停止位和空闲位都规定为高电平。
详情可参考 MSB与LSB
如果小伙伴们耐心下来去分析,可以发现这一系列密密麻麻的波形发送的正是我们想要发送的数据。
static void send_msg (void *p_arg)
{
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init();
Mem_Init();
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err);
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate();
AppObjCreate();
while (DEF_TRUE)
{
printf("hello uc/OS! 欢迎来到RTOS多任务环境!\r\n");
OSTimeDlyHMSM(0, 0, 2, 0,OS_OPT_TIME_HMSM_STRICT,&err);
}
}
二、I2C协议
int main(void)
{
delay_init();
uart_init(115200);
IIC_Init();
while(1)
{
printf("温度湿度显示");
read_AHT20_once();
delay_ms(1500);
}
}
void read_AHT20_once(void)
{
delay_ms(10);
reset_AHT20();
delay_ms(10);
init_AHT20();
delay_ms(10);
startMeasure_AHT20();
delay_ms(80);
read_AHT20();
delay_ms(5);
}
- AHT20复位
void reset_AHT20(void)
{
I2C_Start();
I2C_WriteByte(0x70);
ack_status = Receive_ACK();
if(ack_status) printf("1");
else printf("1-n-");
I2C_WriteByte(0xBA);
ack_status = Receive_ACK();
if(ack_status) printf("2");
else printf("2-n-");
I2C_Stop();
}
- AHT20初始化
void init_AHT20(void)
{
I2C_Start();
I2C_WriteByte(0x70);
ack_status = Receive_ACK();
if(ack_status) printf("3");
else printf("3-n-");
I2C_WriteByte(0xE1);
ack_status = Receive_ACK();
if(ack_status) printf("4");
else printf("4-n-");
I2C_WriteByte(0x08);
ack_status = Receive_ACK();
if(ack_status) printf("5");
else printf("5-n-");
I2C_WriteByte(0x00);
ack_status = Receive_ACK();
if(ack_status) printf("6");
else printf("6-n-");
I2C_Stop();
}
- 发送测量信号
void startMeasure_AHT20(void)
{
I2C_Start();
I2C_WriteByte(0x70);
ack_status = Receive_ACK();
if(ack_status) printf("7");
else printf("7-n-");
I2C_WriteByte(0xAC);
ack_status = Receive_ACK();
if(ack_status) printf("8");
else printf("8-n-");
I2C_WriteByte(0x33);
ack_status = Receive_ACK();
if(ack_status) printf("9");
else printf("9-n-");
I2C_WriteByte(0x00);
ack_status = Receive_ACK();
if(ack_status) printf("10");
else printf("10-n-");
I2C_Stop();
}
- 读取数据
void read_AHT20(void)
{
uint8_t i;
for(i=0; i<6; i++)
{
readByte[i]=0;
}
I2C_Start();
I2C_WriteByte(0x71);
ack_status = Receive_ACK();
readByte[0]= I2C_ReadByte();
Send_ACK();
readByte[1]= I2C_ReadByte();
Send_ACK();
readByte[2]= I2C_ReadByte();
Send_ACK();
readByte[3]= I2C_ReadByte();
Send_ACK();
readByte[4]= I2C_ReadByte();
Send_ACK();
readByte[5]= I2C_ReadByte();
SendNot_Ack();
I2C_Stop();
if( (readByte[0] & 0x68) == 0x08 )
{
H1 = readByte[1];
H1 = (H1<<8) | readByte[2];
H1 = (H1<<8) | readByte[3];
H1 = H1>>4;
H1 = (H1*1000)/1024/1024;
T1 = readByte[3];
T1 = T1 & 0x0000000F;
T1 = (T1<<8) | readByte[4];
T1 = (T1<<8) | readByte[5];
T1 = (T1*2000)/1024/1024 - 500;
AHT20_OutData[0] = (H1>>8) & 0x000000FF;
AHT20_OutData[1] = H1 & 0x000000FF;
AHT20_OutData[2] = (T1>>8) & 0x000000FF;
AHT20_OutData[3] = T1 & 0x000000FF;
}
else
{
AHT20_OutData[0] = 0xFF;
AHT20_OutData[1] = 0xFF;
AHT20_OutData[2] = 0xFF;
AHT20_OutData[3] = 0xFF;
printf("lyy");
}
printf("\r\n");
printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
printf("\r\n");
}
小结 本例我在使用SaleaeLogic16 逻辑仪分析波形时,最小系统板并没有接AHT20传感器,所以在用逻辑仪分析波形时,观察得到的波形都是否定应答,与实际有一定出入;但是通过观察波形,分析起始、停止位、数据位等,可以更细致的了解I2C协议。
三、SPI协议
通道 | 引脚 |
---|
Channel 0 | 片选控制信号 | Channel 1 | 数据/命令选择控制信号 | Channel 2 | SPI时钟信号 | Channel 3 | SPI数据信号 |
-
发送命令信号 -
发送数据信号
总结
关于这几种常用的协议,我在这里只是作了一个最简单的分析,由于编者能力有限,如有问题,望大家不吝指教!!!
关于本次分析所涉及到的工程与文件,我放到了这里提取码:negw,有需要自取
|