เนื่องจาก CH32V003J4M6 มีจำนวนขาเพียง 8 ขาเท่านั้น เลยมีการใส่ function การทำงานของขาที่ทับซ้อนลงไปในขา ที่ 1 และ 8 เพื่อมี function ต่างๆที่จำเป็นครบถ้วนขายใน 8 ขา
ดังนั้นเวลาที่เราใช้งาน USART ที่เป็นค่าเริ่มต้นของโปรแกรมจะใช้ขา PD5/PD6 เป็นขา TX/RX ทำให้มีการส่งข้อมูลออกมาที่ขาที่ 8 เพราะมีการใช้งานรวมกัน หากเราดูตามเอกสารอ้างอิงที่ WCH ให้มา
จากภาพและตาราง จะเห็นว่าขาที่ 8 จะมี function ที่ทับซ้อนกัน เช่น GPIO / URX / UTX / SWIO ตามรูปด้านล่าง
หากเราดูที่ Code เริ่มต้นการทำงานอย่างง่ายๆ เช่นตัวอย่าง code ของ GPIO
#include "debug.h" vu8 val; /********************************************************************* * @fn main * * @brief Main program. * * @return none */ int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Delay_Init(); USART_Printf_Init(115200); printf("SystemClk:%d\r\n",SystemCoreClock); while(1) { } }
ในส่วนของ USART_Printf_Init(115200);
ที่ทำหน้าที่เกี่ยวกับ USART ส่งข้อมูลไม่ว่าจะเป็น OUTPUT หรือ DEBUG ออกมา
/********************************************************************* * @fn USART_Printf_Init * * @brief Initializes the USARTx peripheral. * * @param baudrate - USART communication baud rate. * * @return None */ void USART_Printf_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = baudrate; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); }
ตรงส่วนที่กำหนดขาของ GPIO ที่กำหนดขา PIN GPIO_Pin_5 ซึ่งปกติถ้าคิดแบบทั่วไปเพียงเปลี่ยน จาก GPIO_Pin_5 เป็น GPIO_Pin_6 ก็ควรจะทำงานแล้ว…
แต่มันไม่สามารถทำงานได้ เพราะว่ามีค่า REGISTER กำหนดการทำงานอยู่ ต้องไปแก้ไขหรือเปิดการทำงานของ function สำรองในการเปลี่ยนขา หรือ สลับขาก่อน ซึ่งการที่จะเปลี่ยนหรือสลับขาได้ต้องไปดูเอกสารการใช้งานของ MCU CH32V003 ก่อนว่าต้องเปิดหรือตั้งค่าอะไรบ้าง
มีมาดูค่า REGISTER ที่จำเป็นที่จะต้องกำหนดค่า ตามตารางด้านล่าง bit ที่ 21 จะต้องมีการตั้งค่าให้ถูกต้องในการสลับขา
เมื่อไปดูในส่วนของ Code ที่กำหนดไว้จะเป็นค่าที่ define ไว้แล้ว และค่าที่เราต้องใช้ในการ Remap จากภาพด้านบน คือ ค่า 10 ที่ Remap TX/PD6, RX/PD5
เมื่อเทียบ Code ดูแล้วจะพบว่า ค่า จะได้ดังนี้
GPIO_PartialRemap1_USART1 = Remap [01]
GPIO_PartialRemap2_USART1 = Remap [10]
GPIO_FullRemap_USART1 = Remap [11] ค่าที่ define ไว้ใน code
และค่า REGISTER นี้อยู่ในส่วนของ AFIO (alternate functions) หรือ function อื่นๆ ที่รวมของใน ขานั้นๆ จำเป็นที่จะตั้งเปิด clock ให้เดียวเพื่อใช้งาน function รอง เหล่านี้
ดังนั้นในส่วนของ code จะมี function ที่กำหนดค่านี้อยู่คือ GPIO_PinRemapConfig
/********************************************************************* * @fn GPIO_PinRemapConfig * * @brief Changes the mapping of the specified pin. * * @param GPIO_Remap - selects the pin to remap. * GPIO_Remap_SPI1 - SPI1 Alternate Function mapping * GPIO_PartialRemap_I2C1 - I2C1 Partial Alternate Function mapping * GPIO_PartialRemap_I2C1 - I2C1 Full Alternate Function mapping * GPIO_PartialRemap1_USART1 - USART1 Partial1 Alternate Function mapping * GPIO_PartialRemap2_USART1 - USART1 Partial2 Alternate Function mapping * GPIO_FullRemap_USART1 - USART1 Full Alternate Function mapping * GPIO_PartialRemap1_TIM1 - TIM1 Partial1 Alternate Function mapping * GPIO_PartialRemap2_TIM1 - TIM1 Partial2 Alternate Function mapping * GPIO_FullRemap_TIM1 - TIM1 Full Alternate Function mapping * GPIO_PartialRemap1_TIM2 - TIM2 Partial1 Alternate Function mapping * GPIO_PartialRemap2_TIM2 - TIM2 Partial2 Alternate Function mapping * GPIO_FullRemap_TIM2 - TIM2 Full Alternate Function mapping * GPIO_Remap_PA12 - PA12 Alternate Function mapping * GPIO_Remap_ADC1_ETRGINJ - ADC1 External Trigger Injected Conversion remapping * GPIO_Remap_ADC1_ETRGREG - ADC1 External Trigger Regular Conversion remapping * GPIO_Remap_LSI_CAL - LSI calibration Alternate Function mapping * GPIO_Remap_SDI_Disable - SDI Disabled * NewState - ENABLE or DISABLE. * * @return none */ void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) { uint32_t tmp = 0x00, tmp1 = 0x00, tmpreg = 0x00, tmpmask = 0x00; tmpreg = AFIO->PCFR1; tmpmask = (GPIO_Remap & DBGAFR_POSITION_MASK) >> 0x10; tmp = GPIO_Remap & LSB_MASK; if((GPIO_Remap & 0x10000000) == 0x10000000) { tmpreg &= ~((1<<1) | (1<<22)); tmpreg |= ~DBGAFR_SDI_MASK; if(NewState != DISABLE) { tmpreg |= (GPIO_Remap & 0xEFFFFFFF); } } else if((GPIO_Remap & 0x80000000) == 0x80000000) { tmpreg &= ~((1<<2) | (1<<21)); tmpreg |= ~DBGAFR_SDI_MASK; if(NewState != DISABLE) { tmpreg |= (GPIO_Remap & 0x7FFFFFFF); } } else if((GPIO_Remap & (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK)) == (DBGAFR_LOCATION_MASK | DBGAFR_NUMBITS_MASK))/* SDI */ { tmpreg &= DBGAFR_SDI_MASK; AFIO->PCFR1 &= DBGAFR_SDI_MASK; if(NewState != DISABLE) { tmpreg |= (tmp << ((GPIO_Remap >> 0x15) * 0x10)); } } else if((GPIO_Remap & DBGAFR_NUMBITS_MASK) == DBGAFR_NUMBITS_MASK)/* [15:0] 2bit */ { tmp1 = ((uint32_t)0x03) << tmpmask; tmpreg &= ~tmp1; tmpreg |= ~DBGAFR_SDI_MASK; if(NewState != DISABLE) { tmpreg |= (tmp << ((GPIO_Remap >> 0x15) * 0x10)); } } else/* [31:0] 1bit */ { tmpreg &= ~(tmp << ((GPIO_Remap >> 0x15) * 0x10)); tmpreg |= ~DBGAFR_SDI_MASK; if(NewState != DISABLE) { tmpreg |= (tmp << ((GPIO_Remap >> 0x15) * 0x10)); } } AFIO->PCFR1 = tmpreg; }
สามารถแก้ code เดิมได้ดังนี้
/********************************************************************* * @fn USART_Printf_Init * * @brief Initializes the USARTx peripheral. * * @param baudrate - USART communication baud rate. * * @return None */ void USART_Printf_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO, ENABLE); // เปิด clock ให้กับ AFIO หากไม่เปิดจะไม่ทำงาน GPIO_PinRemapConfig(GPIO_PartialRemap2_USART1, ENABLE); // กำหนดค่า REMAP TX:PD6, RX:PD5 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // กำหนดขา GPIO GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = baudrate; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); }
ตัว Framework ที่ทาง WCH ให้มาเป็นค่าตั้งต้น จะว่าเขียน code ไม่ง่าย ไม่ยาก ถ้ามาจากสายของ Arduino จำเป็นต้องล้างความคิดเดิมใหม่ทั้งหมด หากมาทางสาย STM32 จะสะดวกมากกว่า
การเขียนแนวนี้มันทำให้เราลงลึกไปถึงระดับ bit และ register กันเลยที่เดียว แต่นี้ก็ถือว่าเป็นโอกาศดีที่เราสามารถเรียนรู้ได้อย่างลึกซึ้ง สามารถนำไปต่อยอดกับ MCU ค่ายอื่นๆ ที่ไม่ได้รองรับ Arduino