In 2022, as the new definition moved into 21ic, I was lucky to participate in the first evaluation activity of the new definition in 21ic. Based on NBK-RD8x3x as the core baseboard combined with the hardware resources of the NBK-EBS001 touch expansion board, I began to understand the new definition , chip function, performance, selection, and its advantages; later at the end of the year, New Definition officially sent two expansion boards as New Year gifts: NBK-EBS002 basic function expansion board and NBK-EBS003 IOT expansion board; the new definition is constantly enriching its own hardware platform, and is also constantly improving the software suite (Easy Code Magic Box), in order to let developers who use the new definition have a better experience and get started faster , Not limited to the underlying driver of the chip, software architecture, hardware, etc., you can think of, the new definition may be in the process of preparation...let developers put more energy into the product...can achieve project agility While developing, take into account the function and performance of the product...
This article will introduce in detail all the functional tests based on the NBK-RD8x3x core baseboard combined with the NBK-EBS002 basic function expansion board, as well as some attempts and experience during the debugging process, and share with you; first of all, we will prepare in advance List the information:
1. RD8T36P48J chip data sheet
2. EasyCodeCube_RDSV3.2.5_20230105
3. Newly defined NBK-RD8x3x application data
The above three can be downloaded in the download center of the new definition official website: https://www.rdsmcu.com/
4. Schematic diagram of NBK-EBS002 basic function expansion board:
NBK-EBS002 schematic diagram.pdf (308.55 KB)
In addition to software information, our hardware platform is also indispensable:
1. NBK-RD8x3x core board
2. NBK-EBS002 basic function expansion board
3,RD LINK PRO
Because there is already an ISP download function module on the NBK-RD8x3x core board, RD LINK PRO is an optional tool; ISP can only download programs to the MCU to run, and verify whether the function is normal through the actual running results; and RD LINK PRO has In addition to the function of downloading the program, it also has a wealth of functions such as offline programming, online debugging, simulation, etc. You can choose according to your needs.
Careful friends can see that the resources using MCU have been marked next to each functional peripheral, so that we can know the principle design without looking at the schematic diagram, like it
The software functions realized by combining the NBK-EBS002 basic function expansion board are as follows:
1. Custom time slice task management and scheduling mechanism (linked list implementation)
2. GPIO output controls LED, GPIO input detects KEY input status
3. 3-way PWM output to control RGB lights, control RGB brightness adjustment and color switching according to human visual perception
4. 1 channel PWM control buzzer
5. Drive and display 4-digit 8-segment digital tube through the LCD/LED peripheral function of the chip
6. Realize ADC detection and display through on-board adjustable resistors, realize the function of comparator through 2 adjustable resistors
7. NTC real-time temperature detection and display
Finally, a comprehensive function is implemented:
After the system is started, RGB automatically adjusts the brightness and displays, and switches the color; switch the working mode by pressing the button, and each time you press a button to switch a working mode, the corresponding LED lights up once, and the buzzer sounds once. Mode 2 is for the digital tube to display the ADC sampling value, mode 3 is for the digital tube to display the NTC real-time temperature, and mode 4 is for the comparator function, and the comparison result is reflected in the form of a buzzer;
1. Define time slice task management and scheduling mechanism by yourself (linked list implementation)
In the MCU, I have come into contact with two types of operation, one is with RTOS real-time operating system, such as RTX that supports 51MCU, and the one called SmallRTOS that I have come into contact with before; the other is without operating system. Generally, the overall operation and calling of the MCU is composed of the front-end and back-end systems; in order to facilitate the application of project functions, we will have some plans of our own, based on state machines and time slices... and the following is about myself A time slice-based task management and scheduling mechanism written;
Task scheduling is based on the TIM0 timer. The system defaults to a system operating frequency of 32MHz. We set the tick time of TIM0 to 1ms as the basic time of our system; process the operation of each node task in the TIM0 timer interrupt Whether the interval time is reached, if it is reached, set the running flag bit, in order not to take too much time to call the function function and process in the TIM0 interrupt, we call the specific function function by judging the running flag bit in the while(1) loop; code show as below:
/*********************************************************************************************************************** * [url=home.php?mod=space&uid=247401]@brief[/url] : D1->32000000Hz / 32000 = 1000Hz, 1ms D12->32000000Hz / 26666 = 100Hz, 10ms * @param * @retval * @attention *********************************************************************************************************************/ void TimeSliceInitTIM(void) { TIM0_DeInit(); TIM0_TimeBaseInit(TIM0_PRESSEL_FSYS_D1, TIM0_MODE_TIMER); TIM0_WorkModeConfig(TIM0_WORK_MODE1, (65535 - 32000), 0); TIM0_ITConfig(ENABLE, LOW); TIM0_Cmd(ENABLE); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void TIM0_IRQHandler(void) interrupt 1 { TIM0_Mode1SetReloadCounter(65535 - 32000); TIM0_Tick++; TimeSliceRunning(TIM0_Tick); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void TimeSliceRunning(uint32_t Tick) { TimeSlice_NodeTypeDef *Node = Head; while (NULL != Node) { if (0 == (Tick % Node->Period)) { Node->RunFlag = 1; } Node = Node->Next; } } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void TimeSlicePolling(void) { TimeSlice_NodeTypeDef *Node = Head; while (NULL != Node) { if (1 == Node->RunFlag) { Node->RunFlag = 0; Node->Handler(); } Node = Node->Next; } } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void main(void) { BSP_Init(); TimeSliceInitTIM(); enableInterrupts(); while (1) { TimeSlicePolling(); } }
The management of tasks is mainly to create. The first is to define a task, which has the running interval, the runnable flag, the function function called by the task, and the next task node pointer; the tasks of each node are connected in series in the form of a linked list Let’s get it up and manage; it uses malloc and free, two dynamic memory allocation and release functions, which are somewhat different from our use of 32-bit MCU s. The main thing is to define a memory pool and initialize the memory pool before it can be used. Run these malloc functions normally... The specific implementation code is as follows:
/* Exported types *****************************************************************************************************/ typedef void (* Function)(void); /* Exported types *****************************************************************************************************/ typedef struct _Node_Struct { uint32_t Period; uint8_t RunFlag; Function Handler; struct _Node_Struct *Next; } TimeSlice_NodeTypeDef; /* Private variables **************************************************************************************************/ static uint8_t xdata mempool[1024]; TimeSlice_NodeTypeDef *Head = NULL; /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void TimeSliceStartup(Function Handler, uint32_t Period) { TimeSlice_NodeTypeDef *Node = Head; if (NULL == Head) { memset(mempool, 0x00, sizeof(mempool)); init_mempool(mempool, sizeof(mempool)); Head = (TimeSlice_NodeTypeDef *)malloc(sizeof(TimeSlice_NodeTypeDef)); if (NULL == Head) { free(Head); } else { Head->Period = Period; Head->RunFlag = 0; Head->Handler = Handler; Head->Next = NULL; } } else { while (NULL != Node->Next) { if (Node->Handler == Handler) { Node->Period = Period; return; } else { Node = Node->Next; } } Node->Next = (TimeSlice_NodeTypeDef *)malloc(sizeof(TimeSlice_NodeTypeDef)); if (NULL != Node->Next) { Node = Node->Next; Node->Period = Period; Node->RunFlag = 0; Node->Handler = Handler; Node->Next = NULL; } else { free(Node->Next); } } }
2. GPIO output controls LED, GPIO input detects KEY input status
GPIO is the most basic function of MCU. The low-level library function provided by the new definition realizes the control of LED on and off, and realizes the detection and function processing of buttons through the state machine. The specific code is as follows:
/*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void LED_Init(void) { GPIO_Init(GPIO4, GPIO_PIN_6, GPIO_MODE_OUT_PP); /* L1->P46 */ GPIO_Init(GPIO4, GPIO_PIN_7, GPIO_MODE_OUT_PP); /* L2->P47 */ GPIO_WriteLow(GPIO4, GPIO_PIN_6); GPIO_WriteLow(GPIO4, GPIO_PIN_7); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void KEY_StateHandler(uint8_t Index, uint8_t *State, uint8_t *Count, uint8_t InputLevel, uint8_t ActiveLevel) { if (0 == *State) { if (InputLevel == ActiveLevel) { *Count += 1; if (*Count >= 5) { *Count = 0; *State = 1; KEY_EventHandler(Index, 1); } } else { *Count = 0; } } else { if (InputLevel != ActiveLevel) { *Count += 1; if (*Count >= 5) { *Count = 0; *State = 0; KEY_EventHandler(Index, 0); } } else { *Count = 0; } } } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void KEY_Scan(void) { static uint8_t State[2] = { 0, 0 }; static uint8_t Count[2] = { 0, 0 }; KEY_StateHandler(0, &State[0], &Count[0], GPIO_ReadPin(GPIO3, GPIO_PIN_6), RESET); KEY_StateHandler(1, &State[1], &Count[1], GPIO_ReadPin(GPIO5, GPIO_PIN_4), RESET); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void KEY_Init(void) { GPIO_Init(GPIO3, GPIO_PIN_6, GPIO_MODE_IN_PU); /* K1->P36 */ GPIO_Init(GPIO5, GPIO_PIN_4, GPIO_MODE_IN_PU); /* K2->P54 */ TimeSliceStartup(KEY_Scan, 10); }
3. 3-way PWM output to control RGB lights, control RGB brightness adjustment and color switching according to human visual perception
The brightness adjustment of RGB is realized by modifying the duty cycle of PWM, and the switching of colors is realized by switching different PWM channels. In terms of brightness adjustment, we use the form of LOG parameters for post-calculation adjustment to make the human visual perception more vivid. Naturally, the adjustment is more comfortable, the specific code is as follows:
/*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ double RGB_GetLOG(double Level, double MAX) { return pow(10, Level * log10(999) / MAX); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void RGB_Scan(void) { static uint8_t Index = 0; static uint8_t State = 0, Level = 0, MAX = 100; switch (Index) { case 0: PWM_IndependentModeConfig(PWM01, RGB_GetLOG(Level, MAX)); PWM_IndependentModeConfig(PWM00, 0); PWM_IndependentModeConfig(PWM03, 0); break; case 1: PWM_IndependentModeConfig(PWM01, 0); PWM_IndependentModeConfig(PWM00, RGB_GetLOG(Level, MAX)); PWM_IndependentModeConfig(PWM03, 0); break; case 2: PWM_IndependentModeConfig(PWM01, 0); PWM_IndependentModeConfig(PWM00, 0); PWM_IndependentModeConfig(PWM03, RGB_GetLOG(Level, MAX)); break; default: break; } if (State == 0) { if (Level >= MAX) { Level = MAX; State = 1; } else { Level++; } } else { if (Level <= 1) { Level = 1; State = 0; Index = (Index + 1) % 3; } else { Level--; } } } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void RGB_Init(void) { PWM_Init(PWM0_PRESSEL_FHRC_D1, 255); PWM_IndependentModeConfigEX(PWM00, 0, PWM_OUTPUTSTATE_ENABLE); PWM_IndependentModeConfigEX(PWM01, 0, PWM_OUTPUTSTATE_ENABLE); PWM_IndependentModeConfigEX(PWM03, 0, PWM_OUTPUTSTATE_ENABLE); // PWM_Aligned_Mode_Select(PWM0_Edge_Aligned_Mode); // PWM_FaultDetectionConfigEX(PWM0_Type, DISABLE); // PWM_FaultDetectionModeConfigEX(PWM0_Type, PWM0_Immediate_Mode, PWM0_FaultDetectionVoltage_Low, PWM0_WaveFilteringTime_0us); PWM_CmdEX(PWM0_Type, ENABLE); TimeSliceStartup(RGB_Scan, 20); }
Here is the application function of the two functions of PWM_FaultDetectionConfigEX and PWM_FaultDetectionModeConfigEX.
4. 1 channel PWM control buzzer
For the control of the buzzer, we can control it by enabling or disabling the PWM, or by modifying the PWM duty cycle; for both methods, we have tried: use the PWM to enable or disable The prohibition method is convenient to control, and the statement scheduling is simple, but when downloading the MCU program, the PWM has a click sound that triggers the buzzer; and by modifying the PWM duty cycle, the buzzer will not erroneously sound when the program is downloaded. But you need to pay attention to the control, you can’t switch the PWM duty cycle at an extremely fast speed, which may cause the program to be stuck or the MCU to be stuck (the specific card is not analyzed yet), the reference program is as follows:
/*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void BUZZER_Init(void) { #if 0 uint16_t Frequency = 32000000 / BUZZER_PWM_FREQ; uint16_t Duty = 32000000 / BUZZER_PWM_FREQ * BUZZER_PWM_DUTY / 100; PWM_DeInit(); PWM_Init(PWM4_PRESSEL_FHRC_D1, Frequency); PWM_IndependentModeConfigEX(PWM40, Duty, PWM_OUTPUTSTATE_ENABLE); PWM_CmdEX(PWM4_Type, DISABLE); #else PWM_DeInit(); PWM_Init(PWM4_PRESSEL_FHRC_D1, 32000000 / BUZZER_PWM_FREQ); PWM_IndependentModeConfigEX(PWM40, 0, PWM_OUTPUTSTATE_ENABLE); PWM_CmdEX(PWM4_Type, ENABLE); #endif } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void BUZZER_Enable(uint8_t State) { #if 0 if (ENABLE == State) { PWM_CmdEX(PWM4_Type, ENABLE); } else { PWM_CmdEX(PWM4_Type, DISABLE); } #else if (ENABLE == State) { PWM_IndependentModeConfig(PWM40, 32000000 / BUZZER_PWM_FREQ * BUZZER_PWM_DUTY / 100); } else { PWM_IndependentModeConfig(PWM40, 0); } #endif }
5. Drive and display 4-digit 8-segment digital tube through the LCD/LED peripheral function of the chip
The newly defined MCU not only supports touch function, but also has LCD/LED segment code display function. Each segment and bit corresponds to a display memory in the MCU. After configuring the corresponding display parameters according to the hardware design, it can be directly operated The memory shows what we want, the reference code is as follows:
/* Private typedef ****************************************************************************************************/ typedef struct { char ch; uint8_t Data; } LED_CodeTypeDef; /* Private define *****************************************************************************************************/ /* Private macro ******************************************************************************************************/ /* Private variables **************************************************************************************************/ code LED_CodeTypeDef LED_CODE_TAB[38] = { { ' ', 0x00 }, { '0', 0x3F }, { '1', 0x06 }, { '2', 0x5B }, { '3', 0x4F }, { '4', 0x66 }, { '5', 0x6D }, { '6', 0x7D }, { '7', 0x07 }, { '8', 0x7F }, { '9', 0x6F }, { 'A', 0x77 }, { 'b', 0x7C }, { 'c', 0x58 }, { 'C', 0x39 }, { 'd', 0x5E }, { 'E', 0x79 }, { 'F', 0x71 }, { 'g', 0x6F }, { 'H', 0x76 }, { 'h', 0x74 }, { 'i', 0x04 }, { 'I', 0x30 }, { 'J', 0x1E }, { 'l', 0x30 }, { 'L', 0x38 }, { 'n', 0x54 }, { 'o', 0x5C }, { 'O', 0x3F }, { 'P', 0x73 }, { 'q', 0x67 }, { 'r', 0x50 }, { 'S', 0x6D }, { 't', 0x78 }, { 'u', 0x1C }, { 'U', 0x3E }, { 'y', 0x6E }, { '-', 0x40 }, }; uint8_t LED_DisplayMode = 0; /* Private functions **************************************************************************************************/ /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void LED_DisplayNumber(uint8_t Index, uint8_t Data) { DDIC_Control_COM_TypeDef DDIC_COMn[4] = { DDIC_COM3, DDIC_COM2, DDIC_COM1, DDIC_COM0 }; DDIC_Control(DDIC_SEG4, DDIC_COMn[Index], (Data >> 0x00) & 0x01); DDIC_Control(DDIC_SEG16, DDIC_COMn[Index], (Data >> 0x01) & 0x01); DDIC_Control(DDIC_SEG18, DDIC_COMn[Index], (Data >> 0x02) & 0x01); DDIC_Control(DDIC_SEG20, DDIC_COMn[Index], (Data >> 0x03) & 0x01); DDIC_Control(DDIC_SEG23, DDIC_COMn[Index], (Data >> 0x04) & 0x01); DDIC_Control(DDIC_SEG6, DDIC_COMn[Index], (Data >> 0x05) & 0x01); DDIC_Control(DDIC_SEG17, DDIC_COMn[Index], (Data >> 0x06) & 0x01); DDIC_Control(DDIC_SEG19, DDIC_COMn[Index], (Data >> 0x07) & 0x01); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void LED_DisplayString(char *str) { uint8_t i = 0, Index = 0; while ((*str != '\0') && (Index < 4)) { for (i = 0; i < 38; i++) { if (*str == LED_CODE_TAB[i].ch) { LED_DisplayNumber(Index, LED_CODE_TAB[i].Data); } } str++; Index++; } } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void LED_Scan(void) { static uint16_t Count = 0; char str[10]; if (0 == LED_DisplayMode) { memset(str, 0, sizeof(str)); sprintf(str, "%04d", Count); LED_DisplayString(str); Count = (Count + 1) % 10000; } } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void LED_Init(void) { DDIC_DMOD_Selcet(DMOD_LED); DDIC_Init(DDIC_DUTYCYCLE_D4, DDIC_PIN_X0 | DDIC_PIN_X3, DDIC_PIN_X0 | DDIC_PIN_X2, DDIC_PIN_X4 | DDIC_PIN_X5 | DDIC_PIN_X6 | DDIC_PIN_X7, DDIC_PIN_X0 | DDIC_PIN_X1 | DDIC_PIN_X2 | DDIC_PIN_X3); DDIC_OutputPinOfDutycycleD4(SEG4_27COM0_3); DDIC_Cmd(ENABLE); TimeSliceStartup(LED_Scan, 100); }
6. Realize ADC detection and display through on-board adjustable resistors, realize the function of comparator through 2 adjustable resistors
There are two adjustable resistors on board, one adjustable resistor is used for ADC sampling adjustment, and the other resistor is used to adjust the external comparison voltage of the comparator; through these two adjustable resistors, we can realize the function of ADC , can also realize the function of the comparator, the reference code is as follows:
/*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void ADC_Configure(void) { ADC_Init(ADC_PRESSEL_3CLOCK, ADC_Cycle_Null); ADC_EAINConfig(ADC_EAIN_7, ENABLE); ADC_EAINConfig(ADC_EAIN_15, ENABLE); ADC_Cmd(ENABLE); TimeSliceStartup(ADC_Scan, 200); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ uint16_t ADC_GetADCValue(ADC_Channel_TypeDef Channel) { uint16_t Value = 0; ADC_ChannelConfig(Channel, ENABLE); ADC_StartConversion(); while (RESET == ADC_GetFlagStatus()) { _nop_(); } return (ADC_GetConversionValue()); } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void CMP_Scan(void) { static uint8_t State = 0; if (3 == LED_DisplayMode) { if (CMPCON & (ACMP_FLAG_CMPSTA)) /* CMP3 > CMPR */ { if (1 != State) { State = 1; BUZZER_Enable(ENABLE); } } else /* CMP3 < CMPR */ { if (0 != State) { State = 0; BUZZER_Enable(DISABLE); } } LED_DisplayString("----"); } } /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void CMP_Init(void) { ACMP_Init(ACMP_VREF_8D16VDD, ACMP_VREF_EXTERNAL); ACMP_Cmd(ENABLE); TimeSliceStartup(CMP_Scan, 20); }
7. NTC real-time temperature detection and display
NTC is also realized through the ADC function of the MCU. First, the ADC data of the NTC is sampled, and then the table lookup conversion is performed according to the temperature corresponding to the NTC characteristic table, and finally displayed by the LED digital tube. The reference code is as follows:
/* Private variables **************************************************************************************************/ code uint16_t NTC_ADC_MapTable[100] = { 248 , 262 , 277 , 293 , 310 , 327 , 345 , 364 , 384 , 404 , 425 , 447 , 470 , 494 , 518 , 543 , 570 , 597 , 624 , 653 , 683 , 713 , 744 , 776 , 809 , 842 , 877 , 912 , 948 , 984 , 1022, 1060, 1098, 1137, 1177, 1217, 1258, 1299, 1341, 1383, 1425, 1468, 1511, 1554, 1598, 1641, 1685, 1729, 1772, 1816, 1860, 1903, 1946, 1990, 2033, 2075, 2117, 2159, 2201, 2243, 2283, 2324, 2364, 2403, 2442, 2481, 2519, 2556, 2593, 2629, 2664, 2699, 2734, 2767, 2800, 2833, 2864, 2895, 2926, 2956, 2985, 3013, 3041, 3069, 3095, 3121, 3147, 3172, 3195, 3219, 3242, 3265, 3286, 3308, 3328, 3348, 3368, 3388, 3406, 3424, }; /*********************************************************************************************************************** * @brief * @param * @retval * @attention *********************************************************************************************************************/ void ADC_Scan(void) { char str[10]; uint16_t i = 0; uint16_t Value; if (1 == LED_DisplayMode) /* CMP3 */ { Value = ADC_GetADCValue(ADC_CHANNEL_15); memset(str, 0, sizeof(str)); sprintf(str, "%04d", Value); LED_DisplayString(str); } if (2 == LED_DisplayMode) /* NTC */ { Value = ADC_GetADCValue(ADC_CHANNEL_7); for(i = 0; i < 99; i++) { if((Value >= NTC_ADC_MapTable[i]) && (Value < NTC_ADC_MapTable[i + 1])) { break; } } memset(str, 0, sizeof(str)); if(i < 15) { sprintf(str, "-%02dC", 15 - i); } else { sprintf(str, "%03dCC", i - 15); } LED_DisplayString(str); } }
---------------------
Author: xld0932
Link: https://bbs.21ic.com/icview-3283958-1-1.html
Source: 21ic.com
This article has been awarded the original/original award label, the copyright belongs to 21ic, and anyone is prohibited from reprinting without permission.