Placa de sistema mínima CH32V307VCT6 de QinHeng en código abierto

Placa de sistema mínima CH32V307VCT6 de QinHeng, con todos los pines de E/S expuestos, un conector USB Tipo-C con interfaz OTG USB 2.0 de velocidad completa, un chip Flash modelo W25Q64 de 64 Mbits conectado al interfaz SPI2, un regulador de voltaje TL432 de 1,25 V (medido en 1,246 V aproximadamente) que puede conectarse a AD13 de PC3 mediante un jumper, y una referencia de 3,29 V que puede conectarse al voltaje de referencia VREF del ADC mediante una resistencia de 0 Ω.

Grupo de discusión sobre electrónica y microcontroladores: 2169025065

Introducción al CH32V307VCT6

La serie CH32V está basada en microcontroladores industriales de propósito general de 32 bits RISC-V QingKe. Todos los productos incluyen un área de pila de hardware, entrada rápida de interrupciones y otros diseños que mejoran significativamente la velocidad de respuesta a interrupciones en comparación con el estándar. La serie CH32V303_305_307 utiliza el núcleo V4F, compatible con el conjunto de instrucciones de coma flotante de precisión simple, ofreciendo un mayor rendimiento de cálculo. En cuanto a funciones, admite una frecuencia de reloj de 144 MHz sin espera, y proporciona una estructura de recursos adaptada a diferentes aplicaciones, como 8 puertos USART/UART, 4 temporizadores para motores, interfaz USB 2.0 de alta velocidad (480 Mbps) con transceptor PHY integrado, MAC de Ethernet Gigabit, etc.

Introducción oficial del producto en el sitio web de QinHeng: https://url.zeruns.com/Hcd8p

Solicitud gratuita de muestras (envío a pagar): https://url.zeruns.com/x67TF

Descarga del manual del chip: https://url.zeruns.com/hDzqw Código de extracción: cn7c

Imágenes reales





Esquemático

El conector Tipo-C en la imagen tiene un problema: CC1 y CC2 deben tener resistencias de 5,1 kΩ cada una conectadas a tierra. Al conectarlas en paralelo, causa problemas de alimentación con cables Emark. El proyecto en la plataforma de código abierto de LCSC ya ha sido corregido.

PCB

Capa superior:

La referencia de voltaje de 1,25 V del TL432 (medida en 1,246 V aproximadamente) puede conectarse al pin ADC13 de GPIOPC3 mediante un jumper en el conector H8.

SW3 controla los pines de arranque BOOT0 y BOOT1, permitiendo configurar el modo de arranque (ver sección 2.5.2 del manual del chip sobre memoria interna y modos de arranque). Ambos deben estar en nivel bajo (apagado).

El conector de pin H9 se utiliza para programar con WCH-Link.

El conector de pin H5 selecciona la fuente de alimentación del pin VBAT. Si no hay batería, se conecta a 3,3 V mediante un jumper; si hay batería, se conecta a BAT.

Capa inferior:

Si no se requiere alimentación externa para VDDA y VREF, deben colocarse resistencias de 0 Ω en R11 y R7 en la parte inferior. Luego, VREF+ puede seleccionarse mediante R8 y R9 para conectarse al voltaje de 3,3 V del LDO o a la referencia de 3,29 V del TL432. Normalmente se conecta R9 y se deja el otro sin conectar.

Enlaces de compra de componentes

Se recomienda comprar componentes en la tienda LCSC. Enlace de registro con descuento: https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html

Todos los componentes de la placa están disponibles en la tienda LCSC. En el enlace del proyecto de código abierto, en la lista de materiales, puedes hacer clic en “Agregar al carrito en LCSC” para importar todos los componentes utilizados.

Código de ejemplo

Enlace de descarga del proyecto completo: https://url.zeruns.com/2q4tX Código de extracción: 9527

Enlace del proyecto en la plataforma de código abierto de LCSC: https://url.zeruns.com/Avda8

El ejemplo utiliza el sistema Harmony LiteOS-M. En el proyecto se crean 3 tareas: LED2 parpadea cada 500 ms, LED3 cada 1 segundo y LED4 cada 2 segundos. (La animación de arriba está acelerada 1.5x)

El proyecto debe abrirse con el software MounRiver Studio.

Código de main.c:

/*
 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "debug.h"
#include "los_tick.h"
#include "los_task.h"
#include "los_config.h"
#include "los_interrupt.h"
#include "los_debug.h"
#include "los_compiler.h"

/* Global define */
``````c
/* Variable Global */
__attribute__((aligned (8))) UINT8 g_memStart[LOSCFG_SYS_HEAP_SIZE];
UINT32 g_VlaueSp=0;

u8 i = 0,j=0,k=0;

/*********************************************************************
 * @fn      taskSampleEntry3
 *
 * @brief   Programa taskSampleEntry3.
 *
 * @return  none
 */
VOID taskSampleEntry3(VOID)
{
    while(1) {
      LOS_TaskDelay(2000);
      printf("taskSampleEntry3 running,task3 SP:%08x\n",__get_SP());
      GPIO_WriteBit(GPIOD, GPIO_Pin_13, (k == 0) ? (k = Bit_SET) : (k = Bit_RESET));
    }
}

/*********************************************************************
 * @fn      taskSampleEntry2
 *
 * @brief   Programa taskSampleEntry2.
 *
 * @return  none
 */
VOID taskSampleEntry2(VOID)
{
    while(1) {
      LOS_TaskDelay(1000);
      printf("taskSampleEntry2 running,task2 SP:%08x\n",__get_SP());
      GPIO_WriteBit(GPIOD, GPIO_Pin_12, (j == 0) ? (j = Bit_SET) : (j = Bit_RESET));
    }
}

/*********************************************************************
 * @fn      taskSampleEntry1
 *
 * @brief   Programa taskSampleEntry1.
 *
 * @return  none
 */
VOID taskSampleEntry1(VOID)
{
    while(1) {
      LOS_TaskDelay(500);
      printf("taskSampleEntry1 running,task1 SP:%08x\n",__get_SP());
      GPIO_WriteBit(GPIOD, GPIO_Pin_11, (i == 0) ? (i = Bit_SET) : (i = Bit_RESET));
    }

}
// https://blog.zeruns.com
/*********************************************************************
 * @fn      EXTI0_INT_INIT
 *
 * @brief   Inicializa la colección EXTI0.
 *
 * @return  none
 */
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;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

   /* GPIOA ----> 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 = EXTI0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void GPIO_INIT(void) {
    GPIO_InitTypeDef GPIO_InitStructure = { 0 };

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11|GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

}

/*********************************************************************
 * @fn      taskSample
 *
 * @brief   Programa taskSample.
 *
 * @return  none
 */
UINT32 taskSample(VOID) {
    UINT32 uwRet;
    UINT32 taskID1, taskID2, taskID3;
    TSK_INIT_PARAM_S stTask = { 0 };
    stTask.pfnTaskEntry = (TSK_ENTRY_FUNC) taskSampleEntry1;
    stTask.uwStackSize = 0X500;
    stTask.pcName = "taskSampleEntry1";
    stTask.usTaskPrio = 6;/* Alta prioridad */
    uwRet = LOS_TaskCreate(&taskID1, &stTask);
    if (uwRet != LOS_OK) {
        printf("create task1 failed\n");
    }

    stTask.pfnTaskEntry = (TSK_ENTRY_FUNC) taskSampleEntry2;
    stTask.uwStackSize = 0X500;
    stTask.pcName = "taskSampleEntry2";
    stTask.usTaskPrio = 7;/* Baja prioridad */
    uwRet = LOS_TaskCreate(&taskID2, &stTask);
    if (uwRet != LOS_OK) {
        printf("create task2 failed\n");
    }

    stTask.pfnTaskEntry = (TSK_ENTRY_FUNC) taskSampleEntry3;
        stTask.uwStackSize = 0X500;
        stTask.pcName = "taskSampleEntry3";
        stTask.usTaskPrio = 7;/* Baja prioridad */
        uwRet = LOS_TaskCreate(&taskID3, &stTask);
        if (uwRet != LOS_OK) {
            printf("create task3 failed\n");
        }
// https://blog.vpszj.cn
    EXTI0_INT_INIT();
    return LOS_OK;
}

/*********************************************************************
 * @fn      main
 *
 * @brief   Programa principal.
 *
 * @return  none
 */
LITE_OS_SEC_TEXT_INIT int main(void)
{
    unsigned int ret;

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    SystemCoreClockUpdate();
    Delay_Init();
    GPIO_INIT();
	USART_Printf_Init(115200);
		
	printf("SystemClk:%d\r\n",SystemCoreClock);
	printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );

    ret = LOS_KernelInit();
    taskSample();
    if (ret == LOS_OK)
    {
        LOS_Start();
    }

    GPIO_WriteBit(GPIOC, GPIO_Pin_1,RESET);

    while (1) {
        __asm volatile("nop");
    }

}


/*********************************************************************
 * @fn      EXTI0_IRQHandler
 *
 * @brief   Esta función maneja el Handler EXTI0.
 *
 * @return  none
 */
void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void EXTI0_IRQHandler(void)
{
  /* La pila de interrupciones usa el valor establecido originalmente para llamar a main, separando la pila de interrupciones y la pila de hilos.
   * De esta manera, cuando un hilo salta a una interrupción, si la función de interrupción tiene una gran profundidad de anidamiento,
   * la pila del hilo no se llenará y desbordará. Pero con el método actual, los 16 registros caller guardados por el compilador
   * aún se presionan en la pila del hilo. Si se desea que los registros caller se presionen en la pila de interrupciones,
   * entonces la entrada y salida de la función de interrupción deben usar ensamblador, y solo se llama a la función de manejo de interrupción del usuario en el medio.
   * Véase el ejemplo ipq_entry en los_exc.S
   *  */
  GET_INT_SP();
  HalIntEnter();
  if(EXTI_GetITStatus(EXTI_Line0)!=RESET)
  {
    g_VlaueSp= __get_SP();
    printf("Run at EXTI:");
    printf("interruption sp:%08x\r\n",g_VlaueSp);
    HalDisplayTaskInfo();
    EXTI_ClearITPendingBit(EXTI_Line0);     /* Limpiar Flag */
  }
  HalIntExit();
  FREE_INT_SP();
}

Otros proyectos de código abierto recomendados

Recomendados