در قسمت قبل از این مجموعه، آموزش ADC، TouchKey و تایمرهای CH32X035، با اصول اولیه راهاندازی مبدل آنالوگ به دیجیتال ۱۲ بیتی، حسگر خازنی لمسی (TouchKey) و مدهای تولید پالس PWM در تایمرهای پیشرفته این میکروکنترلر صنعتی آشنا شدیم.
حال در این بخش، کار با پریفرالهای ارتباطی و واحدهای پیشرفته میکروکنترلر RISC-V محبوب شرکت WCH را عمیقتر پی میگیریم؛ از پیکربندی صنعتی وقفههای خارجی (EXTI) و نمونهبرداری پیوسته ADC از طریق کنترلر چندکاناله DMA گرفته تا تبادل تمامدوطرفه در SPI و مدیریت باس I2C. علاوه بر این، یکی از قابلیتهای برجسته و کمنظیر CH32X035 یعنی تقویتکنندههای عملیاتی داخلی با بهره قابل تنظیم (PGA) و کامپاراتورهای فوقسریعِ متصل به تایمر برای حفاظتهای بلادرنگ سختافزاری را به همراه نمونهکدهای استاندارد در محیط توسعه MounRiver Studio II بررسی خواهیم کرد.
۴ × USART با،LIN،،ISO7816 IrDA کنترل جریان سخت افزاری CTS/RTS، بادریت کسری و DMA — پیش فرض: USART1 روی،PB10/PB11 USART2 روی،PA2/PA3 USART3 روی،PB3/PB4 USART4 روی PB0/PB1؛ SPI مستر/اسلیو (پیش فرض PA4..PA7) با داده ۸/۱۶ بیتی، CRC سخت افزاری و پشتیبانی SD/MMC؛ I2C با آدرس دهی ۷/۱۰ بیتی، دو آدرس اسلیو و سرعت Standard/Fast (پیش فرض PA10/PA11)؛ DMA با ۸ کانال عمومی + کانال اختصاصی برای USBFS و + USBPD پشتیبانی TIM/ADC/USART/I2C/SPI؛:GPIO ۴۶ پایه با Pull-up داخلی همگانی، Pull-down فقط روی PA15–PA0 و PC16/17، درایو جریان بالاتر در پورت های A و B، مکانیزم قفل پیکربندی IO، و ۲۴ خط وقفه خارجی.
وقفه خارجی: ۲۴ خط EXTI در دو گروه وقفه EXTI7_0 و EXTI15_8 جمع میشوند؛ برای ورودی های میدانی صنعتی، فیلتر RC خارجی + لبه نزولی همان الگوی استاندارد است:
مثال MounRiver Studio II — وقفه خارجی EXTI روی لبه نزولی PA0 (مبنای:EVT) EXTI0
| 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 | /* CH32X035 - external interrupt on PA0 (falling edge) * MounRiver Studio II - based on EVT example "EXTI0" */ #include "debug.h" void EXTI0_INT_INIT( void ) { GPIO_InitTypeDef GPIO_InitStructure = {0}; EXTI_InitTypeDef EXTI_InitStructure = {0}; NVIC_InitTypeDef NVIC_InitStructure = {0}; RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; /* pull-up input */ GPIO_Init( GPIOA, &GPIO_InitStructure ); /* GPIOA.0 ----> EXTI_Line0 */ GPIO_EXTILineConfig( GPIO_PortSourceGPIOA, GPIO_PinSource0 ); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init( &EXTI_InitStructure ); NVIC_InitStructure.NVIC_IRQChannel = EXTI7_0_IRQn; /* EXTI lines 0..7 */ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); } void EXTI7_0_IRQHandler( void ) __attribute__( ( interrupt( "WCH-Interrupt-fast" ) ) ); void EXTI7_0_IRQHandler( void ) { if( EXTI_GetITStatus( EXTI_Line0 ) != RESET ) { /* field input arrived - latch it, then clear the flag */ EXTI_ClearITPendingBit( EXTI_Line0 ); } } int main( void ) { NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1 ); SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init( 115200 ); EXTI0_INT_INIT( ); while( 1 ) { Delay_Ms( 1000 ); /* main loop ... */ } } |
ADC با DMA: برای پایش پیوسته سنسورهای آنالوگ بدون بار CPU، نتایج ADC به صورت خودکار با DMA به حافظه منتقل میشود:
مثال MounRiver Studio II — برداشت ۱۰۲۴ نمونه ADC با DMA (مبنای:EVT) ADC_DMA
| 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 | /* CH32X035 - ADC channel A1 (PA1): 1024 samples moved by DMA * MounRiver Studio II - based on EVT example "ADC_DMA" */ #include "debug.h" #define N 1024 u16 TxBuf[ N ]; void ADC_Function_Init( void ) { ADC_InitTypeDef ADC_InitStructure = {0}; GPIO_InitTypeDef GPIO_InitStructure = {0}; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; /* A1 = PA1 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init( GPIOA, &GPIO_InitStructure ); ADC_DeInit( ADC1 ); ADC_CLKConfig( ADC1, ADC_CLK_Div6 ); /* 8MHz ADC clock */ ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; /* continuous mode */ ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init( ADC1, &ADC_InitStructure ); ADC_DMACmd( ADC1, ENABLE ); /* ADC -> DMA */ ADC_Cmd( ADC1, ENABLE ); } void DMA_Tx_Init( DMA_Channel_TypeDef *DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize ) { DMA_InitTypeDef DMA_InitStructure = {0}; RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE ); DMA_DeInit( DMA_CHx ); DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr; /* ADC data reg */ DMA_InitStructure.DMA_MemoryBaseAddr = memadr; /* TxBuf */ DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = bufsize; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init( DMA_CHx, &DMA_InitStructure ); } int main( void ) { u16 i; SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init( 115200 ); ADC_Function_Init( ); DMA_Tx_Init( DMA1_Channel1, ( u32 )&ADC1->RDATAR, ( u32 )TxBuf, N ); DMA_Cmd( DMA1_Channel1, ENABLE ); ADC_RegularChannelConfig( ADC1, ADC_Channel_1, 1, ADC_SampleTime_11Cycles ); ADC_SoftwareStartConvCmd( ADC1, ENABLE ); while( DMA_GetFlagStatus( DMA1_FLAG_TC1 ) == RESET ); /* wait 1024 samples */ ADC_SoftwareStartConvCmd( ADC1, DISABLE ); for( i = 0; i < N; i++ ) printf( "%04d\r\n", TxBuf[ i ] ); while( 1 ); } |
SPI: قالب مستر تمام دوطرفه روی پایه های پیش فرض PA4،(CS) PA5،(SCK) PA6 (MISO) و PA7: (MOSI)
مثال MounRiver Studio II — مستر SPI1 با انتقال ۱۶ بیتی تمام دوطرفه (مبنای:EVT) 2Lines_FullDuplex
| 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 | /* CH32X035 - SPI1 master: full-duplex 16-bit transfer * MounRiver Studio II - based on EVT example "2Lines_FullDuplex" */ #include "debug.h" void SPI_FullDuplex_Init( void ) { GPIO_InitTypeDef GPIO_InitStructure = {0}; SPI_InitTypeDef SPI_InitStructure = {0}; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; /* SCK */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; /* MISO */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init( GPIOA, &GPIO_InitStructure ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; /* MOSI */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init( SPI1, &SPI_InitStructure ); SPI_Cmd( SPI1, ENABLE ); } int main( void ) { u8 i = 0, j = 0; u16 TxData[ 4 ] = { 0x0101, 0x0202, 0x0303, 0x0404 }; u16 RxData[ 4 ]; SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init( 115200 ); SPI_FullDuplex_Init( ); while( ( i < 4 ) || ( j < 4 ) ) { if( ( i < 4 ) && ( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_TXE ) != RESET ) ) { SPI_I2S_SendData( SPI1, TxData[ i++ ] ); /* TX empty */ } if( ( j < 4 ) && ( SPI_I2S_GetFlagStatus( SPI1, SPI_I2S_FLAG_RXNE ) != RESET ) ) { RxData[ j++ ] = SPI_I2S_ReceiveData( SPI1 ); /* RX full */ } } while( 1 ); } |
I2C: برای سنسورها و EEPROMهای صنعتی؛ دقت کنید I2C با خروجی Open-Drain کار میکند و مقاومت Pull-up خارجی (معمولا 4.7k) روی SCL/SDA لازم است:
مثال MounRiver Studio II — مستر I2C1 با آدرس دهی ۷ بیتی (مبنای:EVT) I2C_7bit_Mode
| 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 | /* CH32X035 - I2C1 master @100kHz: write 6 bytes to slave 0x02 * MounRiver Studio II - based on EVT example "I2C_7bit_Mode" */ #include "debug.h" void IIC_Init( u32 bound, u16 address ) { GPIO_InitTypeDef GPIO_InitStructure = {0}; I2C_InitTypeDef I2C_InitTSturcture = {0}; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; /* SCL */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; /* SDA */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); I2C_InitTSturcture.I2C_ClockSpeed = bound; I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C; I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9; I2C_InitTSturcture.I2C_OwnAddress1 = address; I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable; I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Init( I2C1, &I2C_InitTSturcture ); I2C_Cmd( I2C1, ENABLE ); I2C_AcknowledgeConfig( I2C1, ENABLE ); } int main( void ) { u8 i; u8 TxData[ 6 ] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init( 115200 ); IIC_Init( 100000, 0x02 ); while( I2C_GetFlagStatus( I2C1, I2C_FLAG_BUSY ) != RESET ); I2C_GenerateSTART( I2C1, ENABLE ); /* START */ while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_MODE_SELECT ) ); I2C_Send7bitAddress( I2C1, 0x02, I2C_Direction_Transmitter ); while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) ); for( i = 0; i < 6; i++ ) /* data */ { I2C_SendData( I2C1, TxData[ i ] ); while( !I2C_CheckEvent( I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED ) ); } I2C_GenerateSTOP( I2C1, ENABLE ); /* STOP */ while( 1 ); } |
OPA×۲: هر اپ امپ به صورت PGA (بهره قابل برنامه) با چند جفت ورودی مثبت/منفی قابل انتخاب عمل میکند و خروجی آن هم به پایه و هم به صورت داخلی به کانال ADC هدایت میشود — یعنی تقویت سیگنال کوچک سنسور (ترموکوپل، شنت جریان، پل استرین گیج) بدون قطعه جانبی. CMP×۳: کامپاراتورهای ریل تو-ریل با هیسترزیس قابل انتخاب که خروجی شان میتواند به GPIO یا مستقیما به کانال های TIM2 برود؛ این مسیر داخلی برای قطع سریع در سناریوهای حفاظت جریان/ ولتاژ (بدون تأخیر وقفه) ایده آل است. نگاشت کامل پایه های آنالوگ در بخش ۴.۳ آمده است. مثال اول، OPA2 را به صورت تقویت کننده بهره ۴ با بازخورد داخلی پیکربندی میکند تا سیگنال کوچک سنسور بدون قطعه جانبی تقویت و روی کانال ADC خوانده شود:
مثال MounRiver Studio II — اپ امپ OPA2 در حالت PGA با بهره ۴ (مبنای:EVT) OPA_PGA
| 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 | /* CH32X035 - OPA2 as x4 PGA: input PA7, output PA4 * MounRiver Studio II - based on EVT example "OPA_PGA" */ #include "debug.h" void OPA2_Init( void ) { GPIO_InitTypeDef GPIO_InitStructure = {0}; OPA_InitTypeDef OPA_InitStructure = {0}; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; /* OPA2_CHP0 = PA7 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init( GPIOA, &GPIO_InitStructure ); OPA_InitStructure.OPA_NUM = OPA2; OPA_InitStructure.PSEL = CHP0; /* + input : PA7 */ OPA_InitStructure.NSEL = CHN_PGA_4xIN; /* PGA gain = x4 */ OPA_InitStructure.Mode = OUT_IO_OUT0; /* output -> PA4 */ OPA_InitStructure.FB = FB_ON; /* internal FB */ OPA_Init( &OPA_InitStructure ); OPA_Cmd( OPA2, ENABLE ); } int main( void ) { SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init( 115200 ); OPA2_Init( ); /* V(PA4) = 4 x V(PA7): sensor gain, no extra parts */ /* PA4 can be sampled by ADC channel A4 */ while( 1 ); } |
مثال دوم، CMP1 را برای حفاظت آستانه ای پیکربندی میکند؛ خروجی کامپاراتور میتواند به جای پایه، مستقیما به تایمر TIM2 (حالت OUT_IO_TIM2) هدایت شود تا قطع سریع بدون تأخیر وقفه انجام شود:
مثال MounRiver Studio II — کامپاراتور CMP1 با پایه های پکیج C8T6 (مبنای:EVT) CMP
| 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 | /* CH32X035 - CMP1: + input PA0, - input PA23, output PA1 (C8T6 pins) * MounRiver Studio II - based on EVT example "CMP" */ #include "debug.h" void CMP1_Init( void ) { GPIO_InitTypeDef GPIO_InitStructure = {0}; CMP_InitTypeDef CMP_InitStructure = {0}; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; /* CMP1_P1 = PA0 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init( GPIOA, &GPIO_InitStructure ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_23; /* CMP1_N1 = PA23 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init( GPIOA, &GPIO_InitStructure ); CMP_InitStructure.CMP_NUM = CMP1; CMP_InitStructure.PSEL = CMP_CHP2; /* + input : PA0 */ CMP_InitStructure.NSEL = CMP_CHN1; /* - input : PA23 */ CMP_InitStructure.HYEN = CMP_HYEN2; /* hysteresis */ CMP_InitStructure.Mode = OUT_IO0; /* output -> PA1 */ OPA_CMP_Init( &CMP_InitStructure ); OPA_CMP_Cmd( CMP1, ENABLE ); } void CMP_OUT_Init( void ) { GPIO_InitTypeDef GPIO_InitStructure = {0}; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; /* CMP1_OUT = PA1 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( GPIOA, &GPIO_InitStructure ); } int main( void ) { SystemCoreClockUpdate(); Delay_Init(); USART_Printf_Init( 115200 ); OPA_CMP_Unlock( ); /* CMP registers are write-protected */ CMP1_Init( ); CMP_OUT_Init( ); /* PA1 = 1 while V(PA0) > V(PA23) */ while( 1 ); } |
سخن پایانی
بهرهگیری از واحد پیشرفته DMA در کنار باسهای ارتباطی پرسرعت و مبدل ADC در تراشه CH32X035، پردازنده اصلی را از درگیر شدن در انتقالهای مکرر داده بینیاز ساخته و ظرفیت پردازشی قابل توجهی را برای الگوریتمهای کاربردی آزاد میگذارد. همچنین وجود دو تقویتکننده عملیاتی داخلی (PGA) و سه کامپاراتور سریع ریلتوریل، مدار را از افزودن آپامپها و قطعات حفاظتی خارجی بینیاز میکند؛ ویژگی تعیینکنندهای که باعث کاهش محسوس هزینهها (BOM)، کوچکتر شدن ابعاد بردهای مدار چاپی و افزایش ایمنی سیستم در کاربردهای حساس صنعتی و سوئیچینگ توان میگردد. با بهکارگیری تکنیکها و نمونهکدهای ارائهشده در این راهنما، میتوانید سیستمی پایدار، سریع و کمهزینه را پیادهسازی نمایید.
من حسن معماری , طراح محصول یا Product Designer هستم. و محصولات یا دستگاه های مختلف سخت افزاری بر پایه الکترونیک , کامپیوتر و هوش مصنوعی طراحی میکنم. شرکت معمار الکترونیک صنعت
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.