SaleaeLogic16 逻辑仪分析波形
前言
本例是通过SaleaeLogic16逻辑仪观察波形,对前几篇实验博客涉及的协议进行分析,旨在深刻的理解不同协议下的通讯方式的异同。
以下是涉及到的相关博客
UART协议:stm32之串口通信
I2C协议:AHT20温湿度采集(I2C协议)
SPI协议:玩转oled屏(基于SPI协议)
一、串口波形分析
本例使用到的是上一篇博客中的工程
(一)keil虚拟波形仿真
首先在对应工程下点击新建文件
将以下内容复制进去并保存到对应的MDK-ARM中去
map 0x40000000, 0x40007FFF read write // APB1
map 0x40010000, 0x400157FF read write // APB2
map 0x40020000, 0x4007FFFF read write // AHB1
map 0x50000000, 0x50060BFF read write // AHB2
map 0x60000000, 0x60000FFF read write // AHB3
map 0xE0000000, 0xE00FFFFF read write // CORTEX-M4 internal peripherals
其中文件名后缀是.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)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
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();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
我们在逻辑分析仪器中可以看到,以下是一连串的密密麻麻的波形,我们把其展开分析
- 计算大概的波特率
从逻辑分析仪中可以看到,传输一位数据大约需要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(); /* Initialize BSP functions */
CPU_Init();
Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
CPU_IntDisMeasMaxCurReset();
AppTaskCreate(); /* Create Application Tasks */
AppObjCreate(); /* Create Application Objects */
while (DEF_TRUE)
{
printf("hello uc/OS! 欢迎来到RTOS多任务环境!\r\n");
OSTimeDlyHMSM(0, 0, 2, 0,OS_OPT_TIME_HMSM_STRICT,&err);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
二、I2C协议
- main函数
int main(void)
{
delay_init(); //延时初始化
uart_init(115200); //串口初始化
IIC_Init();
while(1)
{
printf("温度湿度显示");
read_AHT20_once();
delay_ms(1500);
}
}
- read_AHT20_once函数
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_OutData[0] = 0;
AHT20_OutData[1] = 0;
AHT20_OutData[2] = 0;
AHT20_OutData[3] = 0;
*/
}
- 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();
//Send_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,有需要自取