Transparent PCB

This was made by PCBWay. I designed it in KiCad. It’s basically four shift registers and LEDs. Since it was my first time seeing a transparent PCB, I designed it to explore how it could be used. It’s not transparent like glass. It’s more like translucent and diffuses light. One advantage is that you can see all the tracks.

Mine is without a solder mask. But you can select a transparent solder mask, which is under Advanced PCB.

Pässu and I first look into it:

Test code

This is a test code that uses the CH32V003 microcontroller and MounRiver Studio 2. It shifts round a 1-bit in a 32-bit register to light up the corresponding LED.

#include "debug.h"

// Define our pins on Port C
#define DATA_PIN  GPIO_Pin_0
#define CLOCK_PIN GPIO_Pin_1
#define LATCH_PIN GPIO_Pin_2
#define GPIO_PORT GPIOC


/* Global Variable */
vu8 val;

// Global tick counter
volatile uint32_t global_ms = 0;

/*********************************************************************
 * @fn      USARTx_CFG
 *
 * @brief   Initializes the USART2 & USART3 peripheral.
 *
 * @return  none
 */
void USARTx_CFG(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure = {0};
    USART_InitTypeDef USART_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_USART1, ENABLE);

    /* USART1 TX-->D.5   RX-->D.6 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 115200;
    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_Mode_Rx;

    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

// Update the global tick every 1ms
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void SysTick_Handler(void) {
    global_ms++;
    SysTick->SR = 0; // Clear interrupt flag
}

void SysTick_Init(void) {
    // SystemCoreClock is usually 48MHz
    // Set reload to trigger every 1ms
    SysTick->CMP = SystemCoreClock / 1000;
    SysTick->CNT = 0;
    SysTick->CTLR = 0x0F; // Enable interrupt, enable counter, HCLK as clock
    NVIC_EnableIRQ(SysTick_IRQn);
}

void SPI_1_config(void) {
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    SPI_InitTypeDef  SPI_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1, ENABLE);

    // PC5 (SCK) & PC6 (MOSI) as Alternate Function Push-Pull
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    // PC1 (Latch) as standard Output
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; // 3MHz
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);
}

void SPI_send_data(uint32_t data) {
    GPIO_WriteBit(GPIOC, GPIO_Pin_1, Bit_RESET); // Latch LOW

    // Send 4 bytes (32 bits)
    for(int i = 3; i >= 0; i--) {
        while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
        SPI_I2S_SendData(SPI1, (uint8_t)(data >> (i * 8)));
    }

    // Wait for last byte to finish
    while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET);
    GPIO_WriteBit(GPIOC, GPIO_Pin_1, Bit_SET); // Latch HIGH
}

/*********************************************************************
 * @fn      main
 *
 * @brief   Main program.
 *
 * @return  none
 */
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 2?
    SystemCoreClockUpdate();
    Delay_Init();
 
    SPI_1_config();
    SysTick_Init();

#if (SDI_PRINT == SDI_PR_OPEN)
    SDI_Printf_Enable();
#else
    USART_Printf_Init(115200);
#endif
    printf("SystemClk:%d\r\n",SystemCoreClock);
    printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );

    USARTx_CFG();

  
    uint32_t data = 0x01;
    uint32_t last_update_time = 0;
    const uint32_t interval = 15; // 100ms update rate

    while(1)
    {
        if (global_ms - last_update_time >= interval) {
            last_update_time = global_ms;

            SPI_send_data(data);

            // Rotate bits for a "running light" effect
            data = (data << 1);
            if (data == 0) {
                data = 0x01;
            }
        }
    } // while end
}

Links

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.