本文例子參考《STM32單片機開發實體——基于Proteus虛擬仿真與HAL/LL庫》
源代碼:https://github.com/LanLinnet/STM33F103R6
專案要求
掌握SPI總線通信規則,使用單片機每隔1s讀取一次溫度傳感器TC72的溫度值,并通過串口將讀取的溫度值發送出去,串口通信引數:波特率為19200bits/s,無校驗,
硬體設計
-
在第一節的基礎上,在Proteus中添加電路如下圖所示,其中我們添加了一個串行溫度傳感器
TC72,

此外,我們還添加了兩個虛擬儀表:一個虛擬終端VIRTUAL TERMINAL和一個SPI總線除錯工具SPI DEBUGGER,

虛擬終端VIRTUAL TERMINAL的設定如下,

-
SPI:
1)簡介:SPI(Serial Peripheral Interface, 串行外設介面)是由美國Motorola公司推出的一種同步串行通信介面,用于串行連接微處理器與外圍芯片,SPI采用主從通信模式,通常為一主多從結構,通信時鐘由主機控制,在時鐘信號的作用下,資料先傳送高位,再傳送低位,
2)介面:SPI通信至少需要以下4根線,- SCLK:時鐘線,用于提供通信所需的時鐘基準信號,
- MOSI:主出從入資料線,對于主機而言是資料輸出總線,對從機是資料輸入總線,
- MISO:主入從出資料線,對于主機而言是資料輸入總線,對從機是資料輸出總線,
- \(\overline{CS}\):片選信號,低電平有效,但是對于本次專案所用的TC72,有效電平為高電平,
3)通信時序:SPI通信的作業時序有4種,具體由CPHA(Clock Phase,時鐘相位)和CPOL(Clock Polarity,時鐘極性)決定,SPI的4種通信模式如下表,時序圖如下分別列出,

- 模式0(CPOL=0 CPOL=0)

- 模式1(CPOL=0 CPOL=1)

- 模式2(CPOL=1 CPOL=0)

- 模式3(CPOL=1 CPOL=1)

-
TC72:
1)簡介:TC72是由美國Microchip公司出品的串行溫度傳感器,兼容SPI通信協議,溫度測量范圍為-55℃-+125℃,解析度為10位(0.25℃/bit),
2)引腳:TC72的引腳功能如下表所示,

3)作業模式:TC72的作業模式有以下兩種:- 連續轉換模式(Continuous Conversion Mode):每隔150ms進行1次溫度轉換,
- 單次轉換模式(One-Shot Mode):轉換1次后就進入省電模式,
TC72的溫度轉換結果采用左對齊資料存盤格式:高位元組存放溫度值轉換結果的整數部分,最高位T9為符號位;低位元組高2位存放溫度值轉換結果的小數部分,資料以補碼形式存放,其暫存器地址如下表所示,

-
打開CubeMX,建立工程,STM32F103R6單片機自帶一個SPI模塊,但是為了便于移植,本專案中采用GPIO引腳模擬SPI時序,設定PA4、PA5、PA7均為
GPIO_Output,PA6均為GPIO_Input,點擊“Categories”中的“GPIO”,修改GPIO各引數如下圖所示,

隨后進行串口設定,如下圖所示,這里就不贅述了,具體可以參考第13節,


-
點擊“Generator Code”生成Keil工程,
軟體撰寫
-
考慮到代碼的可移植性,這里將SPI和TC72的驅動代碼全部封裝成函式并分別歸入頭檔案“vSPI.h”和“TC72.h”中,我們可以先在
...\Core\Src檔案夾中建立這兩個頭檔案,此時Keil可能找不到對應檔案,可以直接將檔案拽入Keil中進行編輯,然后再在“main.c”檔案中進行include, -
點擊“Open Project”在Keil中打開工程,打開“vSPI.h”,添加代碼如下,
#ifndef INC_VSPI_H_ #define INC_VSPI_H_ #include "main.h" //軟體延時函式,單位為微秒 void delay_us(uint16_t n) { uint16_t i = n * 8; while(i--); } //SPI總線使能 void vSPI_En() { HAL_GPIO_WritePin(GPIOA, vCE_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_RESET); delay_us(4); } //SPI總線禁止 void vSPI_Dis() { HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOA, vCE_Pin, GPIO_PIN_RESET); } //SPI主站發送1位元組 void vSPI_SndByte(uint8_t dat) //dat表示發送的位元組 { uint8_t i; for(i=0; i<8; i++) { HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_RESET); delay_us(4); if(dat & 0x80) { HAL_GPIO_WritePin(GPIOA, vMOSI_Pin, GPIO_PIN_SET); } else HAL_GPIO_WritePin(GPIOA, vMOSI_Pin, GPIO_PIN_RESET); dat<<=1; //上升沿 HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_SET); delay_us(4); } } //SPI主站接收1位元組資料 uint8_t vSPI_RcvByte() { uint8_t i, dat=0; for(i=0;i<8;i++) { delay_us(4); dat<<=1; HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_RESET); if(HAL_GPIO_ReadPin(GPIOA, vMISO_Pin) == GPIO_PIN_SET) { dat |= 0x01; } else dat &= 0xfe; HAL_GPIO_WritePin(GPIOA, vSCK_Pin, GPIO_PIN_SET); } return dat; //回傳1位元組資料 } #endif /* INC_VSPI_H_ */打開“TC72.h”,添加代碼如下,
#ifndef INC_TC72_H_ #define INC_TC72_H_ #include "main.h" #include "vSPI.h" //宏定義 #define _TC72_CTRL_R 0x00 //控制暫存器地址(讀) #define _TC72_CTRL_W 0x80 //控制暫存器地址(寫) #define _TC72_Dat_LSB 0x01 //溫度低位元組地址(讀) #define _TC72_Dat_MSB 0x02 //溫度高位元組地址(讀) #define _TC72_ID 0x03 //制造商ID(讀) #define _TC72_OnceCnv 0x15 //單次轉化指令 #define _TC72_ContinueCnv 0x05 //連續轉化指令 //發送轉化指令 void TC72_Convert(uint8_t Instr) //Instr為指令 { vSPI_En(); //SPI總線使能 vSPI_SndByte(_TC72_CTRL_W); //發送控制暫存器地址(寫) vSPI_SndByte(Instr); //發送轉化指令 vSPI_Dis(); //SPI總線禁止 } //讀溫度 float TC72_TemperatureRd() { uint8_t DatL, DatM; //高低位元組 int16_t Dat; //最終接收資料 float t; //轉化溫度 vSPI_En(); //SPI總線使能 vSPI_SndByte(_TC72_Dat_MSB); //發送溫度高位元組地址(讀) DatM = vSPI_RcvByte(); //SPI主站接收1位元組(高) DatL = vSPI_RcvByte(); //SPI主站接收1位元組(低) vSPI_Dis(); //SPI總線禁止 Dat = DatM; Dat <<= 8; Dat += DatL; //組合高低位元組 t = ((float)(Dat))/256; //轉化溫度 return t; //回傳溫度值 } #endif /* INC_TC72_H_ */ -
隨后我們需要在main.c檔案中的最前面引入我們自定義的頭檔案
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stdio.h" //引入輸入輸出標準庫 #include "vSPI.h" //引入自定義頭檔案 #include "TC72.h" /* USER CODE END Includes */在main函式中定義需要通過串口發送的字串
/* USER CODE BEGIN 1 */ float t; char str1[] = "Temperature:"; char str2[10]; //存放溫度字串 /* USER CODE END 1 */最后,在while(1)中呼叫我們自定義的函式對TC72和串口進行操作
/* USER CODE BEGIN WHILE */ while (1) { HAL_UART_Transmit(&huart1, (uint8_t *)str1, 12, 12); //串口發送str1 TC72_Convert(_TC72_OnceCnv); //單次轉化指令 HAL_Delay(100); t = TC72_TemperatureRd(); //讀傳感器溫度 sprintf(str2, "%f", t); //將溫度t由浮點型轉化為字串并存入陣列str2 HAL_UART_Transmit(&huart1, (uint8_t *)str2, 7, 7); //串口發送str2 HAL_UART_Transmit(&huart1, (uint8_t *)&"\n\r", 2, 2); HAL_Delay(900); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
聯合除錯
- 點擊運行,生成HEX檔案,
- 在Proteus中加載相應HEX檔案,點擊運行,可以看到虛擬終端“VIRTUAL TERMINAL”每隔1秒都會顯示一次TC72的溫度,調節TC72的溫度值,虛擬終端的顯示也會跟著改變,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/483666.html
標籤:嵌入式
上一篇:不受支持的 Mac 上的通用控制
下一篇:無法洗掉頂部網站中的白條
