最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

嵌入式搞不清串行協(xié)議多煩惱?一個視頻一步到位講清楚,UART,IIC,SPI沒那么難

2023-02-21 17:13 作者:MCUEND  | 我要投稿


以上視頻是我對3種協(xié)議的講解。

對比表

我們也可以看一下,UART、IIC和SPI對比表

特性串口IICSPI通信速率(最大)230400 bps400 kbps(快速)高達72 MHz(主機)通信線數(shù)量2 根2 根4 根通信距離短距離短距離短距離通信協(xié)議異步串行同步串行同步串行硬件復(fù)雜度低中等中等軟件復(fù)雜度低中等中等主從設(shè)備支持支持(從設(shè)備)支持(主/從設(shè)備)支持(主/從設(shè)備)多設(shè)備通信支持支持支持數(shù)據(jù)傳輸完整性低高高

UART其實也是可以進行多設(shè)備通信。在多設(shè)備通信時,需要使用分時復(fù)用技術(shù)或者基于協(xié)議的多點通信技術(shù)。其中分時復(fù)用技術(shù)將多個設(shè)備連接到同一串口,通過在不同時間間隔內(nèi)交替發(fā)送數(shù)據(jù)來實現(xiàn)多設(shè)備通信;而基于協(xié)議的多點通信技術(shù)則使用特定的通信協(xié)議來允許多個設(shè)備連接到同一串口進行通信。因此,在選擇通信協(xié)議時,需要考慮實際應(yīng)用需求以及硬件和軟件資源的限制。

6個demo

以下我用51單片機和STM32單片機分別做了6個demo,僅供參考

UART,51版本

這個程序使用定時器1控制串口通信的波特率,其中FREQ和BAUD分別表示單片機的工作頻率和波特率。在初始化函數(shù)init_serial()中,將定時器1配置為模式2,并計算出需要設(shè)定的初值,然后啟動定時器1。同時,將串口配置為模式1,允許接收,開啟串口中斷并開啟總中斷。在串口中斷處理函數(shù)serial_interrupt()中,處理接收到的數(shù)據(jù)和發(fā)送下一條數(shù)據(jù)。主循環(huán)中可以進行其他任務(wù)的處理。注意,需要根據(jù)實際的硬件接口來配置串口相關(guān)的引腳和波特率等參數(shù)。

#include<reg52.h>

#define?FREQ?11059200?//?單片機工作頻率
#define?BAUD?9600?//?波特率

void?init_serial()?{
??TMOD?&=?0x0F;
??TMOD?|=?0x20;?//?定時器1工作在模式2
??TH1?=?256?-?FREQ?/?(BAUD?*?32);
??TR1?=?1;?//?啟動定時器1
??SCON?=?0x50;?//?串口工作在模式1,允許接收
??ES?=?1;?//?開啟串口中斷
??EA?=?1;?//?開啟總中斷
}

void?serial_interrupt()?interrupt?4?{
??if?(RI)?{
????RI?=?0;?//?清除接收中斷標志
????//?處理接收到的數(shù)據(jù)
????//?...
??}
??if?(TI)?{
????TI?=?0;?//?清除發(fā)送中斷標志
????//?發(fā)送下一條數(shù)據(jù)
????//?...
??}
}

void?main()?{
??init_serial();
??//?主循環(huán)
??while(1)?{
????//?...
??}
}

UART,STM32版本

這個程序使用USART1控制串口通信的波特率,其中FREQ和BAUD分別表示單片機的工作頻率和波特率。在初始化函數(shù)init_serial()中,先使能USART1和GPIOA的時鐘,然后配置PA9為復(fù)用推挽輸出模式,PA10為浮空輸入模式。接著,配置USART1的參數(shù),包括波特率、字長、停止位、校驗位等等,并使能USART1。發(fā)送一個字節(jié)時,先等待發(fā)送緩沖區(qū)為空,然后調(diào)用USART_SendData()發(fā)送一個字節(jié)。接收一個字節(jié)時,先等待接收緩沖區(qū)非空,然后調(diào)用USART_ReceiveData()讀取一個字節(jié)。

STm32F103單片機控制串口通信的簡單程序,波特率為9600

#include?"stm32f10x.h"

#define?FREQ?72000000?//?單片機工作頻率
#define?BAUD?9600?//?波特率

void?init_serial()?{
??//?使能USART1和GPIOA的時鐘
??RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1?|?RCC_APB2Periph_GPIOA,?ENABLE);

??//?配置PA9為復(fù)用推挽輸出模式,PA10為浮空輸入模式
??GPIO_InitTypeDef?gpio_init;
??gpio_init.GPIO_Pin?=?GPIO_Pin_9;
??gpio_init.GPIO_Mode?=?GPIO_Mode_AF_PP;
??gpio_init.GPIO_Speed?=?GPIO_Speed_50MHz;
??GPIO_Init(GPIOA,?&gpio_init);

??gpio_init.GPIO_Pin?=?GPIO_Pin_10;
??gpio_init.GPIO_Mode?=?GPIO_Mode_IN_FLOATING;
??GPIO_Init(GPIOA,?&gpio_init);

??//?配置USART1的參數(shù)
??USART_InitTypeDef?usart_init;
??usart_init.USART_BaudRate?=?BAUD;
??usart_init.USART_WordLength?=?USART_WordLength_8b;
??usart_init.USART_StopBits?=?USART_StopBits_1;
??usart_init.USART_Parity?=?USART_Parity_No;
??usart_init.USART_HardwareFlowControl?=?USART_HardwareFlowControl_None;
??usart_init.USART_Mode?=?USART_Mode_Rx?|?USART_Mode_Tx;
??USART_Init(USART1,?&usart_init);

??//?使能USART1
??USART_Cmd(USART1,?ENABLE);
}

void?send_byte(uint8_t?byte)?{
??//?等待發(fā)送緩沖區(qū)為空
??while(USART_GetFlagStatus(USART1,?USART_FLAG_TXE)?==?RESET);
??//?發(fā)送一個字節(jié)
??USART_SendData(USART1,?byte);
}

uint8_t?receive_byte()?{
??//?等待接收緩沖區(qū)非空
??while(USART_GetFlagStatus(USART1,?USART_FLAG_RXNE)?==?RESET);
??//?讀取一個字節(jié)
??return?USART_ReceiveData(USART1);
}

int?main()?{
??init_serial();
??//?主循環(huán)
??while(1)?{
????//?發(fā)送數(shù)據(jù)
????send_byte(0x55);
????//?接收數(shù)據(jù)
????uint8_t?received?=?receive_byte();
????//?處理接收到的數(shù)據(jù)
????//?...
??}
??return?0;
}

IIC,51單片機

這個程序使用P1_0和P1_1分別控制I2C總線的數(shù)據(jù)線SDA和時鐘線SCL。在初始化函數(shù)init_i2c()中,先將SDA和SCL置為高電平。發(fā)送起始信號時,將SDA置為高電平,SCL置為高電平,等待一段時間,然后將SDA置為低電平,等待一段時間后再將SC置為低電平,以此來發(fā)送起始信號。發(fā)送停止信號時,先將SDA置為低電平,SCL置為高電平,等待一段時間,然后將SDA置為高電平,等待一段時間后再將SCL置為高電平,以此來發(fā)送停止信號。

發(fā)送一個字節(jié)時,將字節(jié)的每一位從高位到低位依次寫入SDA,然后將SCL置為高電平,等待一段時間,再將SCL置為低電平,等待一段時間后將字節(jié)向左移位。寫入字節(jié)的同時,可以將ACK信號讀取出來,ACK為0表示接收到數(shù)據(jù),ACK為1表示接收數(shù)據(jù)出現(xiàn)了錯誤。

讀取一個字節(jié)時,先將SDA置為高電平,然后從高位到低位依次讀取SDA上的數(shù)據(jù),并將它們組成一個字節(jié),最后向左移位。在讀取每一位數(shù)據(jù)時,先將SCL置為高電平,等待一段時間后再將SCL置為低電平,等待一段時間后再讀取SDA上的數(shù)據(jù)。最后,將ACK或NAK信號寫入SDA,然后再將SCL置為高電平,等待一段時間后再將SCL置為低電平,以此來發(fā)送ACK或NAK信號。

注意,在每次讀取和寫入數(shù)據(jù)之前,都需要發(fā)送起始信號。這里給出的是一個簡單的I2C通信程序,可以根據(jù)具體的應(yīng)用需求進行修改和擴展。

#include?<reg51.h>

#define?FREQ?11059200?//?單片機工作頻率
#define?SDA?P1_0?//?I2C數(shù)據(jù)線
#define?SCL?P1_1?//?I2C時鐘線

void?delay_us(unsigned?int?t)?{
??while?(t--)?{
????/*?空循環(huán)?*/
??}
}

void?start_i2c()?{
??SDA?=?1;
??SCL?=?1;
??delay_us(5);
??SDA?=?0;
??delay_us(5);
??SCL?=?0;
}

void?stop_i2c()?{
??SDA?=?0;
??SCL?=?1;
??delay_us(5);
??SDA?=?1;
??delay_us(5);
}

unsigned?char?write_byte(unsigned?char?byte)?{
??unsigned?char?ack;
??unsigned?char?i;
??for?(i?=?0;?i?<?8;?i++)?{
????if?(byte?&?0x80)?{
??????SDA?=?1;
????}?else?{
??????SDA?=?0;
????}
????SCL?=?1;
????delay_us(5);
????SCL?=?0;
????byte?<<=?1;
??}
??SDA?=?1;
??SCL?=?1;
??delay_us(5);
??ack?=?SDA;
??SCL?=?0;
??return?ack;
}

unsigned?char?read_byte(unsigned?char?ack)?{
??unsigned?char?byte?=?0;
??unsigned?char?i;
??SDA?=?1;
??for?(i?=?0;?i?<?8;?i++)?{
????byte?<<=?1;
????SCL?=?1;
????delay_us(5);
????if?(SDA)?{
??????byte?|=?0x01;
????}
????SCL?=?0;
??}
??if?(ack)?{
????SDA?=?0;
??}?else?{
????SDA?=?1;
??}
??SCL?=?1;
??delay_us(5);
??SCL?=?0;
??SDA?=?1;
??return?byte;
}

void?init_i2c()?{
??SDA?=?1;
??SCL?=?1;
??delay_us(5);
}

int?main()?{
??init_i2c();
??start_i2c();
??write_byte(0xA0);?//?寫器件地址
??write_byte(0x00);?//?寫寄存器地址
??write_byte(0x55);?//?寫數(shù)據(jù)
??stop_i2c();
??start_i2c();
??write_byte(0xA0);?//?寫器件地址
??write_byte(0x00);?//?寫寄存器地址
??start_i2c();
??write_byte(0xA1);?//?讀器件地址
??unsigned?char?data?=?read_byte(0);?//?讀數(shù)據(jù)
??stop_i2c();
??return?0;
}

IIC,STM32單片機

以下是一個簡單的I2C通信程序,使用STM32 HAL庫來實現(xiàn)。這個程序可以向I2C設(shè)備發(fā)送數(shù)據(jù),并從I2C設(shè)備讀取數(shù)據(jù)。
首先需要在STM32的CubeMX中配置I2C外設(shè),并將其設(shè)置為主機模式。在代碼中,使用了默認的I2C1外設(shè)。

#include?"main.h"
#include?"stm32f1xx_hal.h"

I2C_HandleTypeDef?hi2c1;

void?SystemClock_Config(void);
static?void?MX_GPIO_Init(void);
static?void?MX_I2C1_Init(void);

int?main(void)
{
??HAL_Init();
??SystemClock_Config();
??MX_GPIO_Init();
??MX_I2C1_Init();

??uint8_t?data_to_send?=?0xAA;??//?要發(fā)送的數(shù)據(jù)
??uint8_t?data_received;????????//?接收到的數(shù)據(jù)

??while?(1)
??{
????//?發(fā)送數(shù)據(jù)
????HAL_I2C_Master_Transmit(&hi2c1,?0x50,?&data_to_send,?1,?1000);

????//?等待一段時間
????HAL_Delay(100);

????//?讀取數(shù)據(jù)
????HAL_I2C_Master_Receive(&hi2c1,?0x50,?&data_received,?1,?1000);
????…………
??}
}

void?SystemClock_Config(void)
{
??RCC_OscInitTypeDef?RCC_OscInitStruct?=?{0};
??RCC_ClkInitTypeDef?RCC_ClkInitStruct?=?{0};

??RCC_OscInitStruct.OscillatorType?=?RCC_OSCILLATORTYPE_HSE;
??RCC_OscInitStruct.HSEState?=?RCC_HSE_ON;
??RCC_OscInitStruct.HSEPredivValue?=?RCC_HSE_PREDIV_DIV1;
??RCC_OscInitStruct.PLL.PLLState?=?RCC_PLL_ON;
??RCC_OscInitStruct.PLL.PLLSource?=?RCC_PLLSOURCE_HSE;
??RCC_OscInitStruct.PLL.PLLMUL?=?RCC_PLL_MUL9;
??if?(HAL_RCC_OscConfig(&RCC_OscInitStruct)?!=?HAL_OK)
??{
????Error_Handler();
??}

??RCC_ClkInitStruct.ClockType?=?RCC_CLOCKTYPE_HCLK?|?RCC_CLOCKTYPE_SYSCLK
??????????????????????????????|?RCC_CLOCKTYPE_PCLK1?|?RCC_CLOCKTYPE_PCLK2;
??RCC_ClkInitStruct.SYSCLKSource?=?RCC_SYSCLKSOURCE_PLLCLK;
??RCC_ClkInitStruct.AHBCLKDivider?=?RCC_SYSCLK_DIV1;
??RCC_ClkInitStruct.APB1CLKDivider?=?RCC_HCLK_DIV2;
??RCC_ClkInitStruct.APB2CLKDivider?=?RCC_HCLK_DIV1;
??if?(HAL_RCC_ClockConfig(&RCC_ClkInitStruct,?FLASH_LATENCY_2)?!=?HAL_OK)
??{
????Error_Handler();
??}
}

static?void?MX_I2C1_Init(void)
{
??hi2c1.Instance?=?I2C1;
??hi2c1.Init.ClockSpeed?=?100000;
??hi2c1.Init.DutyCycle?=?I2C_DUTYCYCLE_2;
??hi2c1.Init.OwnAddress1?=?0;
??hi2c1.Init.AddressingMode?=?I2C_ADDRESSINGMODE_7BIT;
??hi2c1.Init.DualAddressMode?=?I2C_DUALADDRESS_DISABLE;
??hi2c1.Init.OwnAddress2?=?0;
??hi2c1.Init.GeneralCallMode?=?I2C_GENERALCALL_DISABLE;
??hi2c1.Init.NoStretchMode?=?I2C_NOSTRETCH_DISABLE;
??if?(HAL_I2C_Init(&hi2c1)?!=?HAL_OK)
??{
????Error_Handler();
??}
}

SPI,51單片機

下面是一個簡單的SPI程序示例,使用51單片機的SPI接口與一個SPI設(shè)備進行通信。假設(shè)單片機時鐘頻率為12MHz,SPI時鐘頻率為1MHz,SPI設(shè)備的CS引腳接在P1.4上,發(fā)送的數(shù)據(jù)為0x55,接收的數(shù)據(jù)存儲在一個變量中:

在這個程序中,首先使用SPI_Init函數(shù)初始化SPI接口,設(shè)置了波特率為1MHz,時鐘極性為低電平,時鐘相位為第一邊沿,SPI時鐘分頻為4,主機模式。然后在主函數(shù)中,使用SPI_Transfer函數(shù)向SPI設(shè)備發(fā)送一個字節(jié)的數(shù)據(jù),同時接收從設(shè)備返回的數(shù)據(jù),并將接收到的數(shù)據(jù)存儲在一個變量中。在SPI_Transfer函數(shù)中,首先選中SPI設(shè)備(即拉低CS引腳),然后通過串口發(fā)送數(shù)據(jù),等待發(fā)送和接收完成,并取消SPI設(shè)備的選中狀態(tài),最后返回接收到的數(shù)據(jù)。

需要注意的是,這個程序僅發(fā)送和接收了一個字節(jié)的數(shù)據(jù),如果需要發(fā)送更多的數(shù)據(jù),可以通過循環(huán)調(diào)用SPI_Transfer函數(shù)來完成。此外,程序中的波特率和SPI時鐘分頻需要根據(jù)具體應(yīng)用需要進行調(diào)整,還需要根據(jù)實際連接的SPI設(shè)備的時鐘特性進行調(diào)整。

#include?<reg52.h>

sbit?CS?=?P1^4;??//?定義CS引腳

void?SPI_Init(void)
{
??TMOD?=?0x00;??//?配置為模式0,同時清除定時器/計數(shù)器的設(shè)置
??SCON?=?0x50;??//?配置為模式1
??PCON?|=?0x80;?//?將SMOD置1,使串口波特率翻倍
??TH1?=?0xFA;???//?設(shè)置波特率為9600bps
??TL1?=?0xFA;
??TR1?=?1;??????//?啟動定時器/計數(shù)器1
??ES?=?0;???????//?禁止串口中斷
??SS?=?1;???????//?禁止從機模式
??CPHA?=?0;?????//?時鐘極性為低電平
??CPOL?=?0;?????//?時鐘相位為第一邊沿
??SPR1?=?0;?????//?設(shè)置SPI時鐘分頻為4
??SPR0?=?0;
??MSTR?=?1;?????//?主機模式
??SPIEN?=?1;????//?啟動SPI
}

unsigned?char?SPI_Transfer(unsigned?char?data)
{
??CS?=?0;???????//?選中SPI設(shè)備
??SBUF?=?data;??//?發(fā)送數(shù)據(jù)
??while(!TI);???//?等待發(fā)送完成
??TI?=?0;
??……
??}

SPI,STM32單片機

使用STM32單片機的SPI接口與一個SPI設(shè)備進行通信。假設(shè)單片機時鐘頻率為72MHz,SPI時鐘頻率為1MHz,SPI設(shè)備的CS引腳接在PA4上,發(fā)送的數(shù)據(jù)為0x55,接收的數(shù)據(jù)存儲在一個變量中:

在這個程序中,首先使用SPI_Init函數(shù)初始化SPI接口,設(shè)置了SPI模式為主機模式,數(shù)據(jù)傳輸大小為8位,時鐘極性為低電平,時鐘相位為第一邊沿,SPI時鐘分頻為32。然后在主函數(shù)中,使用SPI_Transfer函數(shù)向SPI設(shè)備發(fā)送一個字節(jié)的數(shù)據(jù),同時接收從設(shè)備返回的數(shù)據(jù),并將接收到的數(shù)據(jù)存儲在一個變量中。在SPI_Transfer函數(shù)中,首先選中SPI設(shè)備(即拉低CS引腳),然后通過HAL庫的HAL_SPI_TransmitReceive函數(shù)發(fā)送和接收數(shù)據(jù),并取消SPI設(shè)備的選中狀態(tài),最后返回接收到的數(shù)據(jù)。

需要注意的是,這個程序僅發(fā)送和接收了一個字節(jié)的數(shù)據(jù),如果需要發(fā)送更多的數(shù)據(jù),可以通過循環(huán)調(diào)用SPI_Transfer函數(shù)來完成。此外,程序中的SPI時鐘分頻需要根據(jù)實際連接的SPI設(shè)備的時鐘特性進行調(diào)整。

#include?"stm32f1xx_hal.h"

SPI_HandleTypeDef?hspi1;

void?SPI_Init(void)
{
??hspi1.Instance?=?SPI1;
??hspi1.Init.Mode?=?SPI_MODE_MASTER;
??hspi1.Init.Direction?=?SPI_DIRECTION_2LINES;
??hspi1.Init.DataSize?=?SPI_DATASIZE_8BIT;
??hspi1.Init.CLKPolarity?=?SPI_POLARITY_LOW;
??hspi1.Init.CLKPhase?=?SPI_PHASE_1EDGE;
??hspi1.Init.NSS?=?SPI_NSS_SOFT;
??hspi1.Init.BaudRatePrescaler?=?SPI_BAUDRATEPRESCALER_32;
??hspi1.Init.FirstBit?=?SPI_FIRSTBIT_MSB;
??hspi1.Init.TIMode?=?SPI_TIMODE_DISABLE;
??hspi1.Init.CRCCalculation?=?SPI_CRCCALCULATION_DISABLE;
??hspi1.Init.CRCPolynomial?=?10;
??HAL_SPI_Init(&hspi1);
}

unsigned?char?SPI_Transfer(unsigned?char?data)
{
??unsigned?char?recv_data;
??HAL_GPIO_WritePin(GPIOA,?GPIO_PIN_4,?GPIO_PIN_RESET);
??HAL_SPI_TransmitReceive(&hspi1,?&data,?&recv_data,?1,?100);
??HAL_GPIO_WritePin(GPIOA,?GPIO_PIN_4,?GPIO_PIN_SET);
??return?recv_data;
}

int?main(void)
{
??unsigned?char?data;
??HAL_Init();
??__HAL_RCC_GPIOA_CLK_ENABLE();
??__HAL_RCC_SPI1_CLK_ENABLE();
??GPIO_InitTypeDef?GPIO_InitStruct;
??GPIO_InitStruct.Pin?=?GPIO_PIN_4;
??GPIO_InitStruct.Mode?=?GPIO_MODE_OUTPUT_PP;
??GPIO_InitStruct.Speed?=?GPIO_SPEED_FREQ_HIGH;
??HAL_GPIO_Init(GPIOA,?&GPIO_InitStruct);
??SPI_Init();
??data?=?SPI_Transfer(0x55);
??while?(1)
??{
??………………
??}
}

最愛樣式默認樣式標題顏色標題居中標題背景標題酷酷感想,詩詞引用塊樣式代碼塊樣式標題淡雅標題前修飾標題背景漸變字距偏大標題下邊框標題上下邊框標題四邊框標題首字突出標題后修飾標題倒掛網(wǎng)格背景綜合示例


嵌入式搞不清串行協(xié)議多煩惱?一個視頻一步到位講清楚,UART,IIC,SPI沒那么難的評論 (共 條)

分享到微博請遵守國家法律
肥城市| 岫岩| 龙口市| 托克托县| 郯城县| 阿鲁科尔沁旗| 石柱| 贵州省| 诏安县| 莆田市| 新绛县| 汉沽区| 兴宁市| 阳朔县| 平阴县| 定西市| 鹤山市| 拉萨市| 阳春市| 青阳县| 同江市| 青岛市| 潞西市| 安化县| 新沂市| 磐安县| 孝昌县| 聊城市| 保德县| 津市市| 聊城市| 高陵县| 祁阳县| 灵川县| 太湖县| 金山区| 宣武区| 灵石县| 卢氏县| 茶陵县| 山西省|