با سلام، در این قسمت از آموزشها میریم سراغ SPI در این میکروکنترلر، واحد Spi یکی از قابلیتها خوب هر میکرو کنترلر هست که میتوان خیلی امکانات جانبی را توسط این واحد به میکرو کنترلر وصل کرد.
برای آشنایی با این پریفرال و این رابط سریال میتوانید این آموزش را مطالعه کنید.
باید بدانید که SPI در اکثر میکروکنترلرها با I2S واحد سختافزاری تقریباً یکسانی دارند زیرا I2S که برای ارسال و دریافت دیجیتالی سیگنالهای صوتی استفاده میشود هم سازوکاری بسیار مشابه به SPI دارد. این میکرو هم واحد SPI و I2S اشتراکی دارند.
بریم یکم با ساختار داخلی واحد SPI این میکرو آشنا بشیم:
این بهم قابلیتهای واحد I2S برای پیداکردن بینشی نسبت به I2S این میکرو
این واحد بر خلاف واحد I2C در جلسه قبلی، زیاد توضیح خاصی ندارد. اکثرا با این رابط کار کردید و این رابط رو میشناسید.
این شکل نمای داخلی واحد SPI هست.
این هم تایمینگ دیاگرام واحد SPI
بریم سراغ راهاندازی واحد SPI
اول کلاک واحدهایی که لازم داریم رو راهاندازی میکنیم.
سپس GPIOها را تنظیم میکنیم اگر قرار است Full duplex باشد هم مسیر ارسال “MOSI” و هم مسیر دریافت “MISO” باید پایه هاشون تنظیم بشه، برای ما دوطرفه است پس هر دو رو تنظیم میکنیم.
پس از راهاندازی این بخش میریم سراغ کانفیگ کردن خود واحد SPI که به شکل زیر است:
در اینجا انتخاب چیپ مرود نظرمون رو نرمافزاری کردیم؛ یعنی باید با یک GPIO این پایه رو متناسب با خواست Slave صفر یا یک بکنیم تا Slave فعال بشه و دیتا رو بفرستیم وقتی هم تموم شد Slave رو غیرفعال کنیم.
یک مقسم فرکانس زدیم روی واحد SPIفرکانس واحد SPI به این شکل محاسبه میشود:
CPHA رو صفر قرار میدیم و CPOL روهم صفر.
فرق یک و صفر CPHA و CPOL:
سایز ارسال دیتا در هر سایکل رو 8 میزاریم و مد رو هم مستر میزاریم و با استفاده از توابع CMD و INIT تنظیماتی که انجام دادیم رو اعمال میکنیم و واحد SPI رو فعال میکنیم.
سپس میتوینم ارسال دیتا رو شروع کنیم به شکل زیر:
کد کامل بالا به شکل زیر است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | /********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : Main program body. ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /* *@Note USART Print debugging routine: USART1_Tx(PA9). This example demonstrates using USART1(PA9) as a print debug port output. */ #include "debug.h" /* Global typedef */ /* Global define */ #define CS_PIN GPIO_Pin_4 #define CS_GPIO GPIOA /* Global Variable */ /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() ); printf("This is printf example\r\n"); ///////////////////////////////////////// RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); ///////////////////////////////////// GPIO_InitTypeDef GPIO_SPI1; GPIO_SPI1.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_SPI1.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_7; GPIO_SPI1.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_SPI1); GPIO_InitTypeDef GPIO_CS; GPIO_CS.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_CS.GPIO_Pin = CS_PIN; GPIO_CS.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(CS_GPIO, &GPIO_CS); GPIO_InitTypeDef GPIO_SPI1_MISO; GPIO_SPI1_MISO.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_SPI1_MISO.GPIO_Pin = GPIO_Pin_6; GPIO_SPI1_MISO.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_SPI1_MISO); ///////////////////////////////////// SPI_InitTypeDef SPI_CFG; SPI_CFG.SPI_NSS = SPI_NSS_Soft; /// use software cs pin SPI_CFG.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // spi input freq divider SPI_CFG.SPI_CPHA = SPI_CPHA_1Edge; /// SPI_CPHA_1Edge ->0 // SPI_CPHA_2Edge ->1 SPI_CFG.SPI_CPOL = SPI_CPOL_Low; /// set CPOL LOW SPI_CFG.SPI_DataSize = SPI_DataSize_8b; /// 8 bit SPI_CFG.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // full duplex SPI_CFG.SPI_FirstBit = SPI_FirstBit_MSB; ///send msb first SPI_CFG.SPI_Mode = SPI_Mode_Master; //mode master SPI_Init(SPI1,&SPI_CFG); SPI_Cmd(SPI1,ENABLE); while(1) { GPIO_WriteBit(CS_GPIO,CS_PIN,0); SPI_I2S_SendData(SPI1,0xaa); GPIO_WriteBit(CS_GPIO,CS_PIN,1); Delay_Ms(100); } } |
حالا پس از این راهاندازی بریم به سراغ راهاندازی یک ای سی SPI ای سی که مد نظر من هست آی سی حافظه هست، ما میخوایم در این آموزش کد داخل ایسی حافظه W25Q رو بخونیم. که هم شامل ارسال و هم شامل دریافت دیتا میشود.
کدی که باید بفرستیم تا بتوانیم iD رو بخونیم
بعد از راهاندازی کلاک این تنظیمات GPIO رو داریم:
پس از آن تنظیمات SPI :
این تابع هم برای ارسال و دریافت دیتا مینویسیم:
این تابع زیاد بهینه نیست و صرفاً برای آموزش شماست، در این تابع از While استفاده شده است برای چککردن خالی بودن یا نبودن بافرهای دریافتی و ارسالی که زیاد بهینه نیست، شما میتوانید از راههای بهتری که مانع از قفلشدن برنامه بشوند استفاده کنید.
در این تابع ما اول پایه CS رو صفر میکنیم تا چیپ حافظه فعال بشه سپس کد خواندن ID رو براش میفرستیم سپس چک میکنیم که بافر دریافت خالی هست یانه، میتوان از این خط نیز صرفنظر کرد، سپس شروع به ارسال و دریافت همزمان 4 بایت دیتا میکنیم، برای ارسال طبق دیتاشیت ای سی حافظه نیازی به ارسال دیتا در این مرحله نداریم پس صفر میفرستیم و هر چی که برامون فرستاد رو در ارائه rx_data [0] تا rx_data[3] ذخیره میکنیم. سپس CS رو یک میکنم تا ای سی حافظه خاموش شود و مقدار دریافتی رو از تابع خارج میکنم.
سپس دیتا دریافتی رو روی UART میفرستیم:
کد کامل برنامه بالا:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | /********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.0.0 * Date : 2021/06/06 * Description : Main program body. ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /* *@Note USART Print debugging routine: USART1_Tx(PA9). This example demonstrates using USART1(PA9) as a print debug port output. */ #include "debug.h" /* Global typedef */ /* Global define */ #define CMD_READ_JEDEC_ID 0x9F // READ JEDEC ID command uint32_t read_flash_id(void); /* Global Variable */ /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() ); printf("This is printf example\r\n"); ///////////////////////////////////////// RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); ///////////////////////////////////// GPIO_InitTypeDef GPIO_SPI1; GPIO_SPI1.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_SPI1.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_7; GPIO_SPI1.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_SPI1); GPIO_InitTypeDef GPIO_SPI1_CS; GPIO_SPI1_CS.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_SPI1_CS.GPIO_Pin = GPIO_Pin_0; GPIO_SPI1_CS.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_SPI1_CS); GPIO_InitTypeDef GPIO_SPI1_MISO; GPIO_SPI1_MISO.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_SPI1_MISO.GPIO_Pin = GPIO_Pin_6; GPIO_SPI1_MISO.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_SPI1_MISO); ///////////////////////////////////// SPI_InitTypeDef SPI_CFG; SPI_CFG.SPI_NSS = SPI_NSS_Soft; SPI_CFG.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; SPI_CFG.SPI_CPHA = SPI_CPHA_1Edge; SPI_CFG.SPI_CPOL = SPI_CPOL_Low; SPI_CFG.SPI_DataSize = SPI_DataSize_8b; SPI_CFG.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_CFG.SPI_FirstBit = SPI_FirstBit_MSB; SPI_CFG.SPI_Mode = SPI_Mode_Master; SPI_Init(SPI1,&SPI_CFG); SPI_Cmd(SPI1,ENABLE); GPIO_WriteBit(GPIOA, GPIO_Pin_0,Bit_SET); //cs pin while(1) { uint32_t flash_id = read_flash_id(); printf("Flash ID: 0x%02x\r\n", flash_id); Delay_Ms(100); } } uint32_t read_flash_id(void) { uint8_t tx_data = CMD_READ_JEDEC_ID; uint8_t rx_data[3]; GPIO_WriteBit(GPIOA, GPIO_Pin_0,Bit_RESET); SPI_I2S_SendData(SPI1, tx_data); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); for (int i = 0; i < 3; i++) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, 0); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); rx_data[i] = SPI_I2S_ReceiveData(SPI1); } GPIO_WriteBit(GPIOA, GPIO_Pin_0,Bit_SET); uint32_t flash_id = (rx_data[0] << 16) | (rx_data[1] << 8) | rx_data[2]; return flash_id; } |
در قسمت بعدی به سراغ آموزش تولید عدد رندوم میرویم، تا قسمت بعدی خدا نگهدار.
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.