starting LedBar for rasperry pi via smi (C)
This commit is contained in:
		
							
								
								
									
										346
									
								
								RpiLedBars/src/rpi_dma_utils.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								RpiLedBars/src/rpi_dma_utils.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,346 @@
 | 
			
		||||
// Raspberry Pi DMA utilities; see https://iosoft.blog for details
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 Jeremy P Bentham
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
//
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
#include "rpi_dma_utils.h"
 | 
			
		||||
 | 
			
		||||
// If non-zero, print debug information
 | 
			
		||||
#define DEBUG           0
 | 
			
		||||
// If non-zero, enable PWM hardware output
 | 
			
		||||
#define PWM_OUT         0
 | 
			
		||||
 | 
			
		||||
char *dma_regstrs[] = {"DMA CS", "CB_AD", "TI", "SRCE_AD", "DEST_AD",
 | 
			
		||||
    "TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""};
 | 
			
		||||
char *gpio_mode_strs[] = {GPIO_MODE_STRS};
 | 
			
		||||
 | 
			
		||||
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
 | 
			
		||||
MEM_MAP pwm_regs, gpio_regs, dma_regs, clk_regs;
 | 
			
		||||
 | 
			
		||||
// Use mmap to obtain virtual address, given physical
 | 
			
		||||
void *map_periph(MEM_MAP *mp, void *phys, int size)
 | 
			
		||||
{
 | 
			
		||||
    mp->phys = phys;
 | 
			
		||||
    mp->size = PAGE_ROUNDUP(size);
 | 
			
		||||
    mp->bus = (void *)((uint32_t)phys - PHYS_REG_BASE + BUS_REG_BASE);
 | 
			
		||||
    mp->virt = map_segment(phys, mp->size);
 | 
			
		||||
    return(mp->virt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Allocate uncached memory, get bus & phys addresses
 | 
			
		||||
void *map_uncached_mem(MEM_MAP *mp, int size)
 | 
			
		||||
{
 | 
			
		||||
    void *ret;
 | 
			
		||||
    mp->size = PAGE_ROUNDUP(size);
 | 
			
		||||
    mp->fd = open_mbox();
 | 
			
		||||
    ret = (mp->h = alloc_vc_mem(mp->fd, mp->size, DMA_MEM_FLAGS)) > 0 &&
 | 
			
		||||
        (mp->bus = lock_vc_mem(mp->fd, mp->h)) != 0 &&
 | 
			
		||||
        (mp->virt = map_segment(BUS_PHYS_ADDR(mp->bus), mp->size)) != 0
 | 
			
		||||
        ? mp->virt : 0;
 | 
			
		||||
    printf("VC mem handle %u, phys %p, virt %p\n", mp->h, mp->bus, mp->virt);
 | 
			
		||||
    return(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Free mapped peripheral or memory
 | 
			
		||||
void unmap_periph_mem(MEM_MAP *mp)
 | 
			
		||||
{
 | 
			
		||||
    if (mp)
 | 
			
		||||
    {
 | 
			
		||||
        if (mp->fd)
 | 
			
		||||
        {
 | 
			
		||||
            unmap_segment(mp->virt, mp->size);
 | 
			
		||||
            unlock_vc_mem(mp->fd, mp->h);
 | 
			
		||||
            free_vc_mem(mp->fd, mp->h);
 | 
			
		||||
            close_mbox(mp->fd);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            unmap_segment(mp->virt, mp->size);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----- GPIO -----
 | 
			
		||||
 | 
			
		||||
// Set input or output with pullups
 | 
			
		||||
void gpio_set(int pin, int mode, int pull)
 | 
			
		||||
{
 | 
			
		||||
    gpio_mode(pin, mode);
 | 
			
		||||
    gpio_pull(pin, pull);
 | 
			
		||||
}
 | 
			
		||||
// Set I/P pullup or pulldown
 | 
			
		||||
void gpio_pull(int pin, int pull)
 | 
			
		||||
{
 | 
			
		||||
    volatile uint32_t *reg = REG32(gpio_regs, GPIO_GPPUDCLK0) + pin / 32;
 | 
			
		||||
 | 
			
		||||
    *REG32(gpio_regs, GPIO_GPPUD) = pull;
 | 
			
		||||
    usleep(2);
 | 
			
		||||
    *reg = pin << (pin % 32);
 | 
			
		||||
    usleep(2);
 | 
			
		||||
    *REG32(gpio_regs, GPIO_GPPUD) = 0;
 | 
			
		||||
    *reg = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set input or output
 | 
			
		||||
void gpio_mode(int pin, int mode)
 | 
			
		||||
{
 | 
			
		||||
    volatile uint32_t *reg = REG32(gpio_regs, GPIO_MODE0) + pin / 10, shift = (pin % 10) * 3;
 | 
			
		||||
    *reg = (*reg & ~(7 << shift)) | (mode << shift);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set an O/P pin
 | 
			
		||||
void gpio_out(int pin, int val)
 | 
			
		||||
{
 | 
			
		||||
    volatile uint32_t *reg = REG32(gpio_regs, val ? GPIO_SET0 : GPIO_CLR0) + pin/32;
 | 
			
		||||
    *reg = 1 << (pin % 32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get an I/P pin value
 | 
			
		||||
uint8_t gpio_in(int pin)
 | 
			
		||||
{
 | 
			
		||||
    volatile uint32_t *reg = REG32(gpio_regs, GPIO_LEV0) + pin/32;
 | 
			
		||||
    return (((*reg) >> (pin % 32)) & 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Display the values in a GPIO mode register
 | 
			
		||||
void disp_mode_vals(uint32_t mode)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i=0; i<10; i++)
 | 
			
		||||
        printf("%u:%-4s ", i, gpio_mode_strs[(mode>>(i*3)) & 7]);
 | 
			
		||||
    printf("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----- VIDEOCORE MAILBOX -----
 | 
			
		||||
 | 
			
		||||
// Open mailbox interface, return file descriptor
 | 
			
		||||
int open_mbox(void)
 | 
			
		||||
{
 | 
			
		||||
   int fd;
 | 
			
		||||
 | 
			
		||||
   if ((fd = open("/dev/vcio", 0)) < 0)
 | 
			
		||||
       fail("Error: can't open VC mailbox\n");
 | 
			
		||||
   return(fd);
 | 
			
		||||
}
 | 
			
		||||
// Close mailbox interface
 | 
			
		||||
void close_mbox(int fd)
 | 
			
		||||
{
 | 
			
		||||
    if (fd >= 0)
 | 
			
		||||
        close(fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send message to mailbox, return first response int, 0 if error
 | 
			
		||||
uint32_t msg_mbox(int fd, VC_MSG *msgp)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t ret=0, i;
 | 
			
		||||
 | 
			
		||||
    for (i=msgp->dlen/4; i<=msgp->blen/4; i+=4)
 | 
			
		||||
        msgp->uints[i++] = 0;
 | 
			
		||||
    msgp->len = (msgp->blen + 6) * 4;
 | 
			
		||||
    msgp->req = 0;
 | 
			
		||||
    if (ioctl(fd, _IOWR(100, 0, void *), msgp) < 0)
 | 
			
		||||
        printf("VC IOCTL failed\n");
 | 
			
		||||
    else if ((msgp->req&0x80000000) == 0)
 | 
			
		||||
        printf("VC IOCTL error\n");
 | 
			
		||||
    else if (msgp->req == 0x80000001)
 | 
			
		||||
        printf("VC IOCTL partial error\n");
 | 
			
		||||
    else
 | 
			
		||||
        ret = msgp->uints[0];
 | 
			
		||||
#if DEBUG
 | 
			
		||||
    disp_vc_msg(msgp);
 | 
			
		||||
#endif
 | 
			
		||||
    return(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Allocate memory on PAGE_SIZE boundary, return handle
 | 
			
		||||
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags)
 | 
			
		||||
{
 | 
			
		||||
    VC_MSG msg={.tag=0x3000c, .blen=12, .dlen=12,
 | 
			
		||||
        .uints={PAGE_ROUNDUP(size), PAGE_SIZE, flags}};
 | 
			
		||||
    return(msg_mbox(fd, &msg));
 | 
			
		||||
}
 | 
			
		||||
// Lock allocated memory, return bus address
 | 
			
		||||
void *lock_vc_mem(int fd, int h)
 | 
			
		||||
{
 | 
			
		||||
    VC_MSG msg={.tag=0x3000d, .blen=4, .dlen=4, .uints={h}};
 | 
			
		||||
    return(h ? (void *)msg_mbox(fd, &msg) : 0);
 | 
			
		||||
}
 | 
			
		||||
// Unlock allocated memory
 | 
			
		||||
uint32_t unlock_vc_mem(int fd, int h)
 | 
			
		||||
{
 | 
			
		||||
    VC_MSG msg={.tag=0x3000e, .blen=4, .dlen=4, .uints={h}};
 | 
			
		||||
    return(h ? msg_mbox(fd, &msg) : 0);
 | 
			
		||||
}
 | 
			
		||||
// Free memory
 | 
			
		||||
uint32_t free_vc_mem(int fd, int h)
 | 
			
		||||
{
 | 
			
		||||
    VC_MSG msg={.tag=0x3000f, .blen=4, .dlen=4, .uints={h}};
 | 
			
		||||
    return(h ? msg_mbox(fd, &msg) : 0);
 | 
			
		||||
}
 | 
			
		||||
uint32_t set_vc_clock(int fd, int id, uint32_t freq)
 | 
			
		||||
{
 | 
			
		||||
    VC_MSG msg1={.tag=0x38001, .blen=8, .dlen=8, .uints={id, 1}};
 | 
			
		||||
    VC_MSG msg2={.tag=0x38002, .blen=12, .dlen=12, .uints={id, freq, 0}};
 | 
			
		||||
    msg_mbox(fd, &msg1);
 | 
			
		||||
    disp_vc_msg(&msg1);
 | 
			
		||||
    msg_mbox(fd, &msg2);
 | 
			
		||||
    disp_vc_msg(&msg2);
 | 
			
		||||
    return(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Display mailbox message
 | 
			
		||||
void disp_vc_msg(VC_MSG *msgp)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    printf("VC msg len=%X, req=%X, tag=%X, blen=%x, dlen=%x, data ",
 | 
			
		||||
        msgp->len, msgp->req, msgp->tag, msgp->blen, msgp->dlen);
 | 
			
		||||
    for (i=0; i<msgp->blen/4; i++)
 | 
			
		||||
        printf("%08X ", msgp->uints[i]);
 | 
			
		||||
    printf("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----- VIRTUAL MEMORY -----
 | 
			
		||||
 | 
			
		||||
// Get virtual memory segment for peripheral regs or physical mem
 | 
			
		||||
void *map_segment(void *addr, int size)
 | 
			
		||||
{
 | 
			
		||||
    int fd;
 | 
			
		||||
    void *mem;
 | 
			
		||||
 | 
			
		||||
    size = PAGE_ROUNDUP(size);
 | 
			
		||||
    if ((fd = open ("/dev/mem", O_RDWR|O_SYNC|O_CLOEXEC)) < 0)
 | 
			
		||||
        fail("Error: can't open /dev/mem, run using sudo\n");
 | 
			
		||||
    mem = mmap(0, size, PROT_WRITE|PROT_READ, MAP_SHARED, fd, (uint32_t)addr);
 | 
			
		||||
    close(fd);
 | 
			
		||||
#if DEBUG
 | 
			
		||||
    printf("Map %p -> %p\n", (void *)addr, mem);
 | 
			
		||||
#endif
 | 
			
		||||
    if (mem == MAP_FAILED)
 | 
			
		||||
        fail("Error: can't map memory\n");
 | 
			
		||||
    return(mem);
 | 
			
		||||
}
 | 
			
		||||
// Free mapped memory
 | 
			
		||||
void unmap_segment(void *mem, int size)
 | 
			
		||||
{
 | 
			
		||||
    if (mem)
 | 
			
		||||
        munmap(mem, PAGE_ROUNDUP(size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----- DMA -----
 | 
			
		||||
 | 
			
		||||
// Enable and reset DMA
 | 
			
		||||
void enable_dma(int chan)
 | 
			
		||||
{
 | 
			
		||||
    *REG32(dma_regs, DMA_ENABLE) |= (1 << chan);
 | 
			
		||||
    *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start DMA, given first control block
 | 
			
		||||
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval)
 | 
			
		||||
{
 | 
			
		||||
    *REG32(dma_regs, DMA_REG(chan, DMA_CONBLK_AD)) = MEM_BUS_ADDR(mp, cbp);
 | 
			
		||||
    *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 2;        // Clear 'end' flag
 | 
			
		||||
    *REG32(dma_regs, DMA_REG(chan, DMA_DEBUG)) = 7;     // Clear error bits
 | 
			
		||||
    *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1|csval;  // Start DMA
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return remaining transfer length
 | 
			
		||||
uint32_t dma_transfer_len(int chan)
 | 
			
		||||
{
 | 
			
		||||
    return(*REG32(dma_regs, DMA_REG(chan, DMA_TXFR_LEN)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check if DMA is active
 | 
			
		||||
uint32_t dma_active(int chan)
 | 
			
		||||
{
 | 
			
		||||
    return((*REG32(dma_regs, DMA_REG(chan, DMA_CS))) & 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Halt current DMA operation by resetting controller
 | 
			
		||||
void stop_dma(int chan)
 | 
			
		||||
{
 | 
			
		||||
    if (dma_regs.virt)
 | 
			
		||||
        *REG32(dma_regs, DMA_REG(chan, DMA_CS)) = 1 << 31;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Display DMA registers
 | 
			
		||||
void disp_dma(int chan)
 | 
			
		||||
{
 | 
			
		||||
    volatile uint32_t *p=REG32(dma_regs, DMA_REG(chan, DMA_CS));
 | 
			
		||||
    int i=0;
 | 
			
		||||
 | 
			
		||||
    while (dma_regstrs[i][0])
 | 
			
		||||
    {
 | 
			
		||||
        printf("%-7s %08X ", dma_regstrs[i++], *p++);
 | 
			
		||||
        if (i%5==0 || dma_regstrs[i][0]==0)
 | 
			
		||||
            printf("\n");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----- PWM -----
 | 
			
		||||
 | 
			
		||||
// Initialise PWM
 | 
			
		||||
void init_pwm(int freq, int range, int val)
 | 
			
		||||
{
 | 
			
		||||
    stop_pwm();
 | 
			
		||||
    if (*REG32(pwm_regs, PWM_STA) & 0x100)
 | 
			
		||||
    {
 | 
			
		||||
        printf("PWM bus error\n");
 | 
			
		||||
        *REG32(pwm_regs, PWM_STA) = 0x100;
 | 
			
		||||
    }
 | 
			
		||||
#if USE_VC_CLOCK_SET
 | 
			
		||||
    set_vc_clock(mbox_fd, PWM_CLOCK_ID, freq);
 | 
			
		||||
#else
 | 
			
		||||
    int divi=CLOCK_HZ / freq;
 | 
			
		||||
    *REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | (1 << 5);
 | 
			
		||||
    while (*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) ;
 | 
			
		||||
    *REG32(clk_regs, CLK_PWM_DIV) = CLK_PASSWD | (divi << 12);
 | 
			
		||||
    *REG32(clk_regs, CLK_PWM_CTL) = CLK_PASSWD | 6 | (1 << 4);
 | 
			
		||||
    while ((*REG32(clk_regs, CLK_PWM_CTL) & (1 << 7)) == 0) ;
 | 
			
		||||
#endif
 | 
			
		||||
    usleep(100);
 | 
			
		||||
    *REG32(pwm_regs, PWM_RNG1) = range;
 | 
			
		||||
    *REG32(pwm_regs, PWM_FIF1) = val;
 | 
			
		||||
#if PWM_OUT
 | 
			
		||||
    gpio_mode(PWM_PIN, PWM_PIN==12 ? GPIO_ALT0 : GPIO_ALT5);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start PWM operation
 | 
			
		||||
void start_pwm(void)
 | 
			
		||||
{
 | 
			
		||||
    *REG32(pwm_regs, PWM_CTL) = PWM_CTL_USEF1 | PWM_ENAB;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop PWM operation
 | 
			
		||||
void stop_pwm(void)
 | 
			
		||||
{
 | 
			
		||||
    if (pwm_regs.virt)
 | 
			
		||||
    {
 | 
			
		||||
        *REG32(pwm_regs, PWM_CTL) = 0;
 | 
			
		||||
        usleep(100);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EOF
 | 
			
		||||
							
								
								
									
										202
									
								
								RpiLedBars/src/rpi_dma_utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								RpiLedBars/src/rpi_dma_utils.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
// Raspberry Pi DMA utility definitions; see https://iosoft.blog for details
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 Jeremy P Bentham
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Location of peripheral registers in physical memory
 | 
			
		||||
#define PHYS_REG_BASE   PI_01_REG_BASE
 | 
			
		||||
#define PI_01_REG_BASE  0x20000000  // Pi Zero or 1
 | 
			
		||||
#define PI_23_REG_BASE  0x3F000000  // Pi 2 or 3
 | 
			
		||||
#define PI_4_REG_BASE   0xFE000000  // Pi 4
 | 
			
		||||
 | 
			
		||||
#define CLOCK_HZ      250000000   // Pi 2 - 4
 | 
			
		||||
//#define CLOCK_HZ        400000000   // Pi Zero
 | 
			
		||||
 | 
			
		||||
// Location of peripheral registers in bus memory
 | 
			
		||||
#define BUS_REG_BASE    0x7E000000
 | 
			
		||||
 | 
			
		||||
// If non-zero, print debug information
 | 
			
		||||
#define DEBUG           0
 | 
			
		||||
 | 
			
		||||
// If non-zero, set PWM clock using VideoCore mailbox
 | 
			
		||||
#define USE_VC_CLOCK_SET 0
 | 
			
		||||
 | 
			
		||||
// Size of memory page
 | 
			
		||||
#define PAGE_SIZE       0x1000
 | 
			
		||||
// Round up to nearest page
 | 
			
		||||
#define PAGE_ROUNDUP(n) ((n)%PAGE_SIZE==0 ? (n) : ((n)+PAGE_SIZE)&~(PAGE_SIZE-1))
 | 
			
		||||
 | 
			
		||||
// Structure for mapped peripheral or memory
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int fd,         // File descriptor
 | 
			
		||||
        h,          // Memory handle
 | 
			
		||||
        size;       // Memory size
 | 
			
		||||
    void *bus,      // Bus address
 | 
			
		||||
        *virt,      // Virtual address
 | 
			
		||||
        *phys;      // Physical address
 | 
			
		||||
} MEM_MAP;
 | 
			
		||||
 | 
			
		||||
// Get virtual 8 and 32-bit pointers to register
 | 
			
		||||
#define REG8(m, x)  ((volatile uint8_t *) ((uint32_t)(m.virt)+(uint32_t)(x)))
 | 
			
		||||
#define REG32(m, x) ((volatile uint32_t *)((uint32_t)(m.virt)+(uint32_t)(x)))
 | 
			
		||||
// Get bus address of register
 | 
			
		||||
#define REG_BUS_ADDR(m, x)  ((uint32_t)(m.bus)  + (uint32_t)(x))
 | 
			
		||||
// Convert uncached memory virtual address to bus address
 | 
			
		||||
#define MEM_BUS_ADDR(mp, a) ((uint32_t)a-(uint32_t)mp->virt+(uint32_t)mp->bus)
 | 
			
		||||
// Convert bus address to physical address (for mmap)
 | 
			
		||||
#define BUS_PHYS_ADDR(a)    ((void *)((uint32_t)(a)&~0xC0000000))
 | 
			
		||||
 | 
			
		||||
// GPIO register definitions
 | 
			
		||||
#define GPIO_BASE       (PHYS_REG_BASE + 0x200000)
 | 
			
		||||
#define GPIO_MODE0      0x00
 | 
			
		||||
#define GPIO_SET0       0x1c
 | 
			
		||||
#define GPIO_CLR0       0x28
 | 
			
		||||
#define GPIO_LEV0       0x34
 | 
			
		||||
#define GPIO_GPPUD      0x94
 | 
			
		||||
#define GPIO_GPPUDCLK0  0x98
 | 
			
		||||
// GPIO I/O definitions
 | 
			
		||||
#define GPIO_IN         0
 | 
			
		||||
#define GPIO_OUT        1
 | 
			
		||||
#define GPIO_ALT0       4
 | 
			
		||||
#define GPIO_ALT1       5
 | 
			
		||||
#define GPIO_ALT2       6
 | 
			
		||||
#define GPIO_ALT3       7
 | 
			
		||||
#define GPIO_ALT4       3
 | 
			
		||||
#define GPIO_ALT5       2
 | 
			
		||||
#define GPIO_MODE_STRS  "IN","OUT","ALT5","ALT4","ALT0","ALT1","ALT2","ALT3"
 | 
			
		||||
#define GPIO_NOPULL     0
 | 
			
		||||
#define GPIO_PULLDN     1
 | 
			
		||||
#define GPIO_PULLUP     2
 | 
			
		||||
 | 
			
		||||
// Videocore mailbox memory allocation flags, see:
 | 
			
		||||
//     https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
 | 
			
		||||
typedef enum {
 | 
			
		||||
    MEM_FLAG_DISCARDABLE    = 1<<0, // can be resized to 0 at any time. Use for cached data
 | 
			
		||||
    MEM_FLAG_NORMAL         = 0<<2, // normal allocating alias. Don't use from ARM
 | 
			
		||||
    MEM_FLAG_DIRECT         = 1<<2, // 0xC alias uncached
 | 
			
		||||
    MEM_FLAG_COHERENT       = 2<<2, // 0x8 alias. Non-allocating in L2 but coherent
 | 
			
		||||
    MEM_FLAG_ZERO           = 1<<4, // initialise buffer to all zeros
 | 
			
		||||
    MEM_FLAG_NO_INIT        = 1<<5, // don't initialise (default is initialise to all ones)
 | 
			
		||||
    MEM_FLAG_HINT_PERMALOCK = 1<<6, // Likely to be locked for long periods of time
 | 
			
		||||
    MEM_FLAG_L1_NONALLOCATING=(MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) // Allocating in L2
 | 
			
		||||
} VC_ALLOC_FLAGS;
 | 
			
		||||
// VC flags for unchached DMA memory
 | 
			
		||||
#define DMA_MEM_FLAGS (MEM_FLAG_DIRECT|MEM_FLAG_ZERO)
 | 
			
		||||
 | 
			
		||||
// Mailbox command/response structure
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t len,   // Overall length (bytes)
 | 
			
		||||
        req,        // Zero for request, 1<<31 for response
 | 
			
		||||
        tag,        // Command number
 | 
			
		||||
        blen,       // Buffer length (bytes)
 | 
			
		||||
        dlen;       // Data length (bytes)
 | 
			
		||||
        uint32_t uints[32-5];   // Data (108 bytes maximum)
 | 
			
		||||
} VC_MSG __attribute__ ((aligned (16)));
 | 
			
		||||
 | 
			
		||||
// DMA channels and data requests
 | 
			
		||||
#define DMA_CHAN_A      10
 | 
			
		||||
#define DMA_CHAN_B      11
 | 
			
		||||
#define DMA_PWM_DREQ    5
 | 
			
		||||
#define DMA_SPI_TX_DREQ 6
 | 
			
		||||
#define DMA_SPI_RX_DREQ 7
 | 
			
		||||
#define DMA_BASE        (PHYS_REG_BASE + 0x007000)
 | 
			
		||||
// DMA register addresses offset by 0x100 * chan_num
 | 
			
		||||
#define DMA_CS          0x00
 | 
			
		||||
#define DMA_CONBLK_AD   0x04
 | 
			
		||||
#define DMA_TI          0x08
 | 
			
		||||
#define DMA_SRCE_AD     0x0c
 | 
			
		||||
#define DMA_DEST_AD     0x10
 | 
			
		||||
#define DMA_TXFR_LEN    0x14
 | 
			
		||||
#define DMA_STRIDE      0x18
 | 
			
		||||
#define DMA_NEXTCONBK   0x1c
 | 
			
		||||
#define DMA_DEBUG       0x20
 | 
			
		||||
#define DMA_REG(ch, r)  ((r)==DMA_ENABLE ? DMA_ENABLE : (ch)*0x100+(r))
 | 
			
		||||
#define DMA_ENABLE      0xff0
 | 
			
		||||
// DMA register values
 | 
			
		||||
#define DMA_WAIT_RESP   (1 << 3)
 | 
			
		||||
#define DMA_CB_DEST_INC (1 << 4)
 | 
			
		||||
#define DMA_DEST_DREQ   (1 << 6)
 | 
			
		||||
#define DMA_CB_SRCE_INC (1 << 8)
 | 
			
		||||
#define DMA_SRCE_DREQ   (1 << 10)
 | 
			
		||||
#define DMA_PRIORITY(n) ((n) << 16)
 | 
			
		||||
 | 
			
		||||
// DMA control block (must be 32-byte aligned)
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t ti,    // Transfer info
 | 
			
		||||
        srce_ad,    // Source address
 | 
			
		||||
        dest_ad,    // Destination address
 | 
			
		||||
        tfr_len,    // Transfer length
 | 
			
		||||
        stride,     // Transfer stride
 | 
			
		||||
        next_cb,    // Next control block
 | 
			
		||||
        debug,      // Debug register, zero in control block
 | 
			
		||||
        unused;
 | 
			
		||||
} DMA_CB __attribute__ ((aligned(32)));
 | 
			
		||||
 | 
			
		||||
// PWM controller registers
 | 
			
		||||
#define PWM_BASE        (PHYS_REG_BASE + 0x20C000)
 | 
			
		||||
#define PWM_CTL         0x00   // Control
 | 
			
		||||
#define PWM_STA         0x04   // Status
 | 
			
		||||
#define PWM_DMAC        0x08   // DMA control
 | 
			
		||||
#define PWM_RNG1        0x10   // Channel 1 range
 | 
			
		||||
#define PWM_DAT1        0x14   // Channel 1 data
 | 
			
		||||
#define PWM_FIF1        0x18   // Channel 1 fifo
 | 
			
		||||
#define PWM_RNG2        0x20   // Channel 2 range
 | 
			
		||||
#define PWM_DAT2        0x24   // Channel 2 data
 | 
			
		||||
// PWM register values
 | 
			
		||||
#define PWM_CTL_RPTL1   (1<<2)  // Chan 1: repeat last data when FIFO empty
 | 
			
		||||
#define PWM_CTL_USEF1   (1<<5)  // Chan 1: use FIFO
 | 
			
		||||
#define PWM_DMAC_ENAB   (1<<31) // Start PWM DMA
 | 
			
		||||
#define PWM_ENAB        1       // Enable PWM
 | 
			
		||||
#define PWM_PIN         12      // GPIO pin for PWM output, 12 or 18
 | 
			
		||||
 | 
			
		||||
// Clock registers and values
 | 
			
		||||
#define CLK_BASE        (PHYS_REG_BASE + 0x101000)
 | 
			
		||||
#define CLK_PWM_CTL     0xa0
 | 
			
		||||
#define CLK_PWM_DIV     0xa4
 | 
			
		||||
#define CLK_PASSWD      0x5a000000
 | 
			
		||||
#define PWM_CLOCK_ID    0xa
 | 
			
		||||
 | 
			
		||||
void fail(char *s);
 | 
			
		||||
void *map_periph(MEM_MAP *mp, void *phys, int size);
 | 
			
		||||
void *map_uncached_mem(MEM_MAP *mp, int size);
 | 
			
		||||
void unmap_periph_mem(MEM_MAP *mp);
 | 
			
		||||
void gpio_set(int pin, int mode, int pull);
 | 
			
		||||
void gpio_pull(int pin, int pull);
 | 
			
		||||
void gpio_mode(int pin, int mode);
 | 
			
		||||
void gpio_out(int pin, int val);
 | 
			
		||||
uint8_t gpio_in(int pin);
 | 
			
		||||
void disp_mode_vals(uint32_t mode);
 | 
			
		||||
int open_mbox(void);
 | 
			
		||||
void close_mbox(int fd);
 | 
			
		||||
uint32_t msg_mbox(int fd, VC_MSG *msgp);
 | 
			
		||||
void *map_segment(void *addr, int size);
 | 
			
		||||
void unmap_segment(void *addr, int size);
 | 
			
		||||
uint32_t alloc_vc_mem(int fd, uint32_t size, VC_ALLOC_FLAGS flags);
 | 
			
		||||
void *lock_vc_mem(int fd, int h);
 | 
			
		||||
uint32_t unlock_vc_mem(int fd, int h);
 | 
			
		||||
uint32_t free_vc_mem(int fd, int h);
 | 
			
		||||
uint32_t set_vc_clock(int fd, int id, uint32_t freq);
 | 
			
		||||
void disp_vc_msg(VC_MSG *msgp);
 | 
			
		||||
void enable_dma(int chan);
 | 
			
		||||
void start_dma(MEM_MAP *mp, int chan, DMA_CB *cbp, uint32_t csval);
 | 
			
		||||
uint32_t dma_transfer_len(int chan);
 | 
			
		||||
uint32_t dma_active(int chan);
 | 
			
		||||
void stop_dma(int chan);
 | 
			
		||||
void disp_dma(int chan);
 | 
			
		||||
void init_pwm(int freq, int range, int val);
 | 
			
		||||
void start_pwm(void);
 | 
			
		||||
void stop_pwm(void);
 | 
			
		||||
 | 
			
		||||
// EOF
 | 
			
		||||
							
								
								
									
										389
									
								
								RpiLedBars/src/rpi_pixleds.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										389
									
								
								RpiLedBars/src/rpi_pixleds.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,389 @@
 | 
			
		||||
// Raspberry Pi WS2812 LED driver using SMI
 | 
			
		||||
// For detailed description, see https://iosoft.blog
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 Jeremy P Bentham
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
//
 | 
			
		||||
// v0.01 JPB 16/7/20 Adapted from rpi_smi_adc_test v0.06
 | 
			
		||||
// v0.02 JPB 15/9/20 Addded RGB to GRB conversion
 | 
			
		||||
// v0.03 JPB 15/9/20 Added red-green flashing
 | 
			
		||||
// v0.04 JPB 16/9/20 Added test mode
 | 
			
		||||
// v0.05 JPB 19/9/20 Changed test mode colours
 | 
			
		||||
// v0.06 JPB 20/9/20 Outlined command-line data input
 | 
			
		||||
// v0.07 JPB 25/9/20 Command-line data input if not in test mode
 | 
			
		||||
// v0.08 JPB 26/9/20 Changed from 4 to 3 pulses per LED bit
 | 
			
		||||
//                   Added 4-bit zero preamble
 | 
			
		||||
//                   Added raw Tx data test
 | 
			
		||||
// v0.09 JPB 27/9/20 Added 16-channel option
 | 
			
		||||
// v0.10 JPB 28/9/20 Corrected Pi Zero caching problem
 | 
			
		||||
// v0.11 JPB 29/9/20 Added enable_dma before transfer (in case still active)
 | 
			
		||||
//                   Corrected DMA nsamp value (was byte count)
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include "rpi_dma_utils.h"
 | 
			
		||||
#include "rpi_smi_defs.h"
 | 
			
		||||
 | 
			
		||||
#if PHYS_REG_BASE==PI_4_REG_BASE        // Timings for RPi v4 (1.5 GHz)
 | 
			
		||||
#define SMI_TIMING       10, 15, 30, 15    // 400 ns cycle time
 | 
			
		||||
#else                                   // Timings for RPi v0-3 (1 GHz)
 | 
			
		||||
#define SMI_TIMING       10, 10, 20, 10   // 400 ns cycle time
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TX_TEST         0   // If non-zero, use dummy Tx data
 | 
			
		||||
#define LED_D0_PIN      8   // GPIO pin for D0 output
 | 
			
		||||
#define LED_NCHANS      8   // Number of LED channels (8 or 16)
 | 
			
		||||
#define LED_NBITS       24  // Number of data bits per LED
 | 
			
		||||
#define LED_PREBITS     4   // Number of zero bits before LED data
 | 
			
		||||
#define LED_POSTBITS    4   // Number of zero bits after LED data
 | 
			
		||||
#define BIT_NPULSES     3   // Number of O/P pulses per LED bit
 | 
			
		||||
#define CHAN_MAXLEDS    50  // Maximum number of LEDs per channel
 | 
			
		||||
#define CHASE_MSEC      100 // Delay time for chaser light test
 | 
			
		||||
#define REQUEST_THRESH  2   // DMA request threshold
 | 
			
		||||
#define DMA_CHAN        10  // DMA channel to use
 | 
			
		||||
 | 
			
		||||
// Length of data for 1 row (1 LED on each channel)
 | 
			
		||||
#define LED_DLEN        (LED_NBITS * BIT_NPULSES)
 | 
			
		||||
 | 
			
		||||
// Transmit data type, 8 or 16 bits
 | 
			
		||||
#if LED_NCHANS > 8
 | 
			
		||||
#define TXDATA_T        uint16_t
 | 
			
		||||
#else
 | 
			
		||||
#define TXDATA_T        uint8_t
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Structures for mapped I/O devices, and non-volatile memory
 | 
			
		||||
extern MEM_MAP gpio_regs, dma_regs;
 | 
			
		||||
MEM_MAP vc_mem, clk_regs, smi_regs;
 | 
			
		||||
 | 
			
		||||
// Pointers to SMI registers
 | 
			
		||||
volatile SMI_CS_REG  *smi_cs;
 | 
			
		||||
volatile SMI_L_REG   *smi_l;
 | 
			
		||||
volatile SMI_A_REG   *smi_a;
 | 
			
		||||
volatile SMI_D_REG   *smi_d;
 | 
			
		||||
volatile SMI_DMC_REG *smi_dmc;
 | 
			
		||||
volatile SMI_DSR_REG *smi_dsr;
 | 
			
		||||
volatile SMI_DSW_REG *smi_dsw;
 | 
			
		||||
volatile SMI_DCS_REG *smi_dcs;
 | 
			
		||||
volatile SMI_DCA_REG *smi_dca;
 | 
			
		||||
volatile SMI_DCD_REG *smi_dcd;
 | 
			
		||||
 | 
			
		||||
// Ofset into Tx data buffer, given LED number in chan
 | 
			
		||||
#define LED_TX_OSET(n)      (LED_PREBITS + (LED_DLEN * (n)))
 | 
			
		||||
 | 
			
		||||
// Size of data buffers & NV memory, given number of LEDs per chan
 | 
			
		||||
#define TX_BUFF_LEN(n)      (LED_TX_OSET(n) + LED_POSTBITS)
 | 
			
		||||
#define TX_BUFF_SIZE(n)     (TX_BUFF_LEN(n) * sizeof(TXDATA_T))
 | 
			
		||||
#define VC_MEM_SIZE         (PAGE_SIZE + TX_BUFF_SIZE(CHAN_MAXLEDS))
 | 
			
		||||
 | 
			
		||||
// RGB values for test mode (1 value for each of 16 channels)
 | 
			
		||||
int on_rgbs[16] = {0xff0000, 0x00ff00, 0x0000ff, 0xffffff,
 | 
			
		||||
                  0xff4040, 0x40ff40, 0x4040ff, 0x404040,
 | 
			
		||||
                  0xff0000, 0x00ff00, 0x0000ff, 0xffffff,
 | 
			
		||||
                  0xff4040, 0x40ff40, 0x4040ff, 0x404040};
 | 
			
		||||
int off_rgbs[16];
 | 
			
		||||
 | 
			
		||||
#if TX_TEST
 | 
			
		||||
// Data for simple transmission test
 | 
			
		||||
TXDATA_T tx_test_data[] = {1, 2, 3, 4, 5, 6, 7, 0};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
TXDATA_T *txdata;                       // Pointer to uncached Tx data buffer
 | 
			
		||||
TXDATA_T tx_buffer[TX_BUFF_LEN(CHAN_MAXLEDS)];  // Tx buffer for assembling data
 | 
			
		||||
int testmode, chan_ledcount=1;          // Command-line parameters
 | 
			
		||||
int rgb_data[CHAN_MAXLEDS][LED_NCHANS]; // RGB data
 | 
			
		||||
int chan_num;                           // Current channel for data I/P
 | 
			
		||||
 | 
			
		||||
void rgb_txdata(int *rgbs, TXDATA_T *txd);
 | 
			
		||||
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan);
 | 
			
		||||
void swap_bytes(void *data, int len);
 | 
			
		||||
int hexdig(char c);
 | 
			
		||||
void map_devices(void);
 | 
			
		||||
void fail(char *s);
 | 
			
		||||
void terminate(int sig);
 | 
			
		||||
void init_smi(int width, int ns, int setup, int hold, int strobe);
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp);
 | 
			
		||||
void start_smi(MEM_MAP *mp);
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
    int args=0, n, oset=0;
 | 
			
		||||
 | 
			
		||||
    while (argc > ++args)               // Process command-line args
 | 
			
		||||
    {
 | 
			
		||||
        if (argv[args][0] == '-')
 | 
			
		||||
        {
 | 
			
		||||
            switch (toupper(argv[args][1]))
 | 
			
		||||
            {
 | 
			
		||||
            case 'N':                   // -N: number of LEDs per channel
 | 
			
		||||
                if (args >= argc-1)
 | 
			
		||||
                    fprintf(stderr, "Error: no numeric value\n");
 | 
			
		||||
                else
 | 
			
		||||
                    chan_ledcount = atoi(argv[++args]);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'T':                   // -T: test mode
 | 
			
		||||
                testmode = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            default:                    // Otherwise error
 | 
			
		||||
                printf("Unrecognised option '%c'\n", argv[args][1]);
 | 
			
		||||
                printf("Options:\n"
 | 
			
		||||
                       "  -n num    number of LEDs per channel\n"\
 | 
			
		||||
                       "  -t        Test mode (flash LEDs)\n"\
 | 
			
		||||
                      );
 | 
			
		||||
                return(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (chan_num<LED_NCHANS && hexdig(argv[args][0])>=0 &&
 | 
			
		||||
                 (n=str_rgb(argv[args], rgb_data, chan_num))>0)
 | 
			
		||||
        {
 | 
			
		||||
            chan_ledcount = n > chan_ledcount ? n : chan_ledcount;
 | 
			
		||||
            chan_num++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    signal(SIGINT, terminate);
 | 
			
		||||
    map_devices();
 | 
			
		||||
    init_smi(LED_NCHANS>8 ? SMI_16_BITS : SMI_8_BITS, SMI_TIMING);
 | 
			
		||||
    map_uncached_mem(&vc_mem, VC_MEM_SIZE);
 | 
			
		||||
#if TX_TEST
 | 
			
		||||
    oset = oset;
 | 
			
		||||
    setup_smi_dma(&vc_mem, sizeof(tx_test_data)/sizeof(TXDATA_T));
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
    swap_bytes(tx_test_data, sizeof(tx_test_data));
 | 
			
		||||
#endif
 | 
			
		||||
    memcpy(txdata, tx_test_data, sizeof(tx_test_data));
 | 
			
		||||
    start_smi(&vc_mem);
 | 
			
		||||
    usleep(10);
 | 
			
		||||
    while (dma_active(DMA_CHAN))
 | 
			
		||||
        usleep(10);
 | 
			
		||||
#else
 | 
			
		||||
    setup_smi_dma(&vc_mem, TX_BUFF_LEN(chan_ledcount));
 | 
			
		||||
    printf("%s %u LED%s per channel, %u channels\n", testmode ? "Testing" : "Setting",
 | 
			
		||||
           chan_ledcount, chan_ledcount==1 ? "" : "s", LED_NCHANS);
 | 
			
		||||
    
 | 
			
		||||
    if (testmode)
 | 
			
		||||
    {
 | 
			
		||||
        while (1)
 | 
			
		||||
        {
 | 
			
		||||
            if (chan_ledcount < 2)
 | 
			
		||||
                rgb_txdata(oset&1 ? off_rgbs : on_rgbs, tx_buffer);
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                for (n=0; n<chan_ledcount; n++)
 | 
			
		||||
                {
 | 
			
		||||
                    rgb_txdata(n==oset%chan_ledcount ? on_rgbs : off_rgbs,
 | 
			
		||||
                               &tx_buffer[LED_TX_OSET(n)]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            oset++;
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
            swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
#endif
 | 
			
		||||
            memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
            start_smi(&vc_mem);
 | 
			
		||||
            usleep(CHASE_MSEC * 1000);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        for (n=0; n<chan_ledcount; n++)
 | 
			
		||||
            rgb_txdata(rgb_data[n], &tx_buffer[LED_TX_OSET(n)]);
 | 
			
		||||
#if LED_NCHANS <= 8
 | 
			
		||||
        swap_bytes(tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
#endif
 | 
			
		||||
        memcpy(txdata, tx_buffer, TX_BUFF_SIZE(chan_ledcount));
 | 
			
		||||
        enable_dma(DMA_CHAN);
 | 
			
		||||
        start_smi(&vc_mem);
 | 
			
		||||
        usleep(10);
 | 
			
		||||
        while (dma_active(DMA_CHAN))
 | 
			
		||||
            usleep(10);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    terminate(0);
 | 
			
		||||
    return(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert RGB text string into integer data, for given channel
 | 
			
		||||
// Return number of data points for this channel
 | 
			
		||||
int str_rgb(char *s, int rgbs[][LED_NCHANS], int chan)
 | 
			
		||||
{
 | 
			
		||||
    int i=0;
 | 
			
		||||
    char *p;
 | 
			
		||||
 | 
			
		||||
    while (chan<LED_NCHANS && i<CHAN_MAXLEDS && hexdig(*s)>=0)
 | 
			
		||||
    {
 | 
			
		||||
        rgbs[i++][chan] = strtoul(s, &p, 16);
 | 
			
		||||
        s = *p ? p+1 : p;
 | 
			
		||||
    }
 | 
			
		||||
    return(i);
 | 
			
		||||
}
 | 
			
		||||
// Set Tx data for 8 or 16 chans, 1 LED per chan, given 1 RGB val per chan
 | 
			
		||||
// Logic 1 is 0.8us high, 0.4 us low, logic 0 is 0.4us high, 0.8us low
 | 
			
		||||
void rgb_txdata(int *rgbs, TXDATA_T *txd)
 | 
			
		||||
{
 | 
			
		||||
    int i, n, msk;
 | 
			
		||||
 | 
			
		||||
    // For each bit of the 24-bit RGB values..
 | 
			
		||||
    for (n=0; n<LED_NBITS; n++)
 | 
			
		||||
    {
 | 
			
		||||
        // Mask to convert RGB to GRB, M.S bit first
 | 
			
		||||
        msk = n==0 ? 0x8000 : n==8 ? 0x800000 : n==16 ? 0x80 : msk>>1;
 | 
			
		||||
        // 1st byte or word is a high pulse on all lines
 | 
			
		||||
        txd[0] = (TXDATA_T)0xffff;
 | 
			
		||||
        // 2nd has high or low bits from data
 | 
			
		||||
        // 3rd is a low pulse
 | 
			
		||||
        txd[1] = txd[2] = 0;
 | 
			
		||||
        for (i=0; i<LED_NCHANS; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (rgbs[i] & msk)
 | 
			
		||||
                txd[1] |= (1 << i);
 | 
			
		||||
        }
 | 
			
		||||
        txd += BIT_NPULSES;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Swap adjacent bytes in transmit data
 | 
			
		||||
void swap_bytes(void *data, int len)
 | 
			
		||||
{
 | 
			
		||||
    uint16_t *wp = (uint16_t *)data;
 | 
			
		||||
 | 
			
		||||
    len = (len + 1) / 2;
 | 
			
		||||
    while (len-- > 0)
 | 
			
		||||
    {
 | 
			
		||||
        *wp = __builtin_bswap16(*wp);
 | 
			
		||||
        wp++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return hex digit value, -ve if not hex
 | 
			
		||||
int hexdig(char c)
 | 
			
		||||
{
 | 
			
		||||
    c = toupper(c);
 | 
			
		||||
    return((c>='0' && c<='9') ? c-'0' : (c>='A' && c<='F') ? c-'A'+10 : -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Map GPIO, DMA and SMI registers into virtual mem (user space)
 | 
			
		||||
// If any of these fail, program will be terminated
 | 
			
		||||
void map_devices(void)
 | 
			
		||||
{
 | 
			
		||||
    map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
 | 
			
		||||
    map_periph(&dma_regs, (void *)DMA_BASE, PAGE_SIZE);
 | 
			
		||||
    map_periph(&clk_regs, (void *)CLK_BASE, PAGE_SIZE);
 | 
			
		||||
    map_periph(&smi_regs, (void *)SMI_BASE, PAGE_SIZE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Catastrophic failure in initial setup
 | 
			
		||||
void fail(char *s)
 | 
			
		||||
{
 | 
			
		||||
    printf(s);
 | 
			
		||||
    terminate(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Free memory segments and exit
 | 
			
		||||
void terminate(int sig)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    printf("Closing\n");
 | 
			
		||||
    if (gpio_regs.virt)
 | 
			
		||||
    {
 | 
			
		||||
        for (i=0; i<LED_NCHANS; i++)
 | 
			
		||||
            gpio_mode(LED_D0_PIN+i, GPIO_IN);
 | 
			
		||||
    }
 | 
			
		||||
    if (smi_regs.virt)
 | 
			
		||||
        *REG32(smi_regs, SMI_CS) = 0;
 | 
			
		||||
    stop_dma(DMA_CHAN);
 | 
			
		||||
    unmap_periph_mem(&vc_mem);
 | 
			
		||||
    unmap_periph_mem(&smi_regs);
 | 
			
		||||
    unmap_periph_mem(&dma_regs);
 | 
			
		||||
    unmap_periph_mem(&gpio_regs);
 | 
			
		||||
    exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initialise SMI, given data width, time step, and setup/hold/strobe counts
 | 
			
		||||
// Step value is in nanoseconds: even numbers, 2 to 30
 | 
			
		||||
void init_smi(int width, int ns, int setup, int strobe, int hold)
 | 
			
		||||
{
 | 
			
		||||
    int i, divi = ns / 2;
 | 
			
		||||
 | 
			
		||||
    smi_cs  = (SMI_CS_REG *) REG32(smi_regs, SMI_CS);
 | 
			
		||||
    smi_l   = (SMI_L_REG *)  REG32(smi_regs, SMI_L);
 | 
			
		||||
    smi_a   = (SMI_A_REG *)  REG32(smi_regs, SMI_A);
 | 
			
		||||
    smi_d   = (SMI_D_REG *)  REG32(smi_regs, SMI_D);
 | 
			
		||||
    smi_dmc = (SMI_DMC_REG *)REG32(smi_regs, SMI_DMC);
 | 
			
		||||
    smi_dsr = (SMI_DSR_REG *)REG32(smi_regs, SMI_DSR0);
 | 
			
		||||
    smi_dsw = (SMI_DSW_REG *)REG32(smi_regs, SMI_DSW0);
 | 
			
		||||
    smi_dcs = (SMI_DCS_REG *)REG32(smi_regs, SMI_DCS);
 | 
			
		||||
    smi_dca = (SMI_DCA_REG *)REG32(smi_regs, SMI_DCA);
 | 
			
		||||
    smi_dcd = (SMI_DCD_REG *)REG32(smi_regs, SMI_DCD);
 | 
			
		||||
    smi_cs->value = smi_l->value = smi_a->value = 0;
 | 
			
		||||
    smi_dsr->value = smi_dsw->value = smi_dcs->value = smi_dca->value = 0;
 | 
			
		||||
    if (*REG32(clk_regs, CLK_SMI_DIV) != divi << 12)
 | 
			
		||||
    {
 | 
			
		||||
        *REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | (1 << 5);
 | 
			
		||||
        usleep(10);
 | 
			
		||||
        while (*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) ;
 | 
			
		||||
        usleep(10);
 | 
			
		||||
        *REG32(clk_regs, CLK_SMI_DIV) = CLK_PASSWD | (divi << 12);
 | 
			
		||||
        usleep(10);
 | 
			
		||||
        *REG32(clk_regs, CLK_SMI_CTL) = CLK_PASSWD | 6 | (1 << 4);
 | 
			
		||||
        usleep(10);
 | 
			
		||||
        while ((*REG32(clk_regs, CLK_SMI_CTL) & (1 << 7)) == 0) ;
 | 
			
		||||
        usleep(100);
 | 
			
		||||
    }
 | 
			
		||||
    if (smi_cs->seterr)
 | 
			
		||||
        smi_cs->seterr = 1;
 | 
			
		||||
    smi_dsr->rsetup = smi_dsw->wsetup = setup;
 | 
			
		||||
    smi_dsr->rstrobe = smi_dsw->wstrobe = strobe;
 | 
			
		||||
    smi_dsr->rhold = smi_dsw->whold = hold;
 | 
			
		||||
    smi_dmc->panicr = smi_dmc->panicw = 8;
 | 
			
		||||
    smi_dmc->reqr = smi_dmc->reqw = REQUEST_THRESH;
 | 
			
		||||
    smi_dsr->rwidth = smi_dsw->wwidth = width;
 | 
			
		||||
    for (i=0; i<LED_NCHANS; i++)
 | 
			
		||||
        gpio_mode(LED_D0_PIN+i, GPIO_ALT1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set up SMI transfers using DMA
 | 
			
		||||
void setup_smi_dma(MEM_MAP *mp, int nsamp)
 | 
			
		||||
{
 | 
			
		||||
    DMA_CB *cbs=mp->virt;
 | 
			
		||||
 | 
			
		||||
    txdata = (TXDATA_T *)(cbs+1);
 | 
			
		||||
    smi_dmc->dmaen = 1;
 | 
			
		||||
    smi_cs->enable = 1;
 | 
			
		||||
    smi_cs->clear = 1;
 | 
			
		||||
    smi_cs->pxldat = 1;
 | 
			
		||||
    smi_l->len = nsamp * sizeof(TXDATA_T);
 | 
			
		||||
    smi_cs->write = 1;
 | 
			
		||||
    enable_dma(DMA_CHAN);
 | 
			
		||||
    cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC | DMA_WAIT_RESP;
 | 
			
		||||
    cbs[0].tfr_len = nsamp;
 | 
			
		||||
    cbs[0].srce_ad = MEM_BUS_ADDR(mp, txdata);
 | 
			
		||||
    cbs[0].dest_ad = REG_BUS_ADDR(smi_regs, SMI_D);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start SMI DMA transfers
 | 
			
		||||
void start_smi(MEM_MAP *mp)
 | 
			
		||||
{
 | 
			
		||||
    DMA_CB *cbs=mp->virt;
 | 
			
		||||
 | 
			
		||||
    start_dma(mp, DMA_CHAN, &cbs[0], 0);
 | 
			
		||||
    smi_cs->start = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EOF
 | 
			
		||||
							
								
								
									
										100
									
								
								RpiLedBars/src/rpi_smi_defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								RpiLedBars/src/rpi_smi_defs.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
// Secondary Memory Interface definitions for Raspberry Pi
 | 
			
		||||
//
 | 
			
		||||
// v0.01 JPB 12/7/20 Adapted from rpi_smi_test v0.19
 | 
			
		||||
 | 
			
		||||
// Register definitions
 | 
			
		||||
#define SMI_BASE    (PHYS_REG_BASE + 0x600000)
 | 
			
		||||
#define SMI_CS      0x00    // Control & status
 | 
			
		||||
#define SMI_L       0x04    // Transfer length
 | 
			
		||||
#define SMI_A       0x08    // Address
 | 
			
		||||
#define SMI_D       0x0c    // Data
 | 
			
		||||
#define SMI_DSR0    0x10    // Read settings device 0
 | 
			
		||||
#define SMI_DSW0    0x14    // Write settings device 0
 | 
			
		||||
#define SMI_DSR1    0x18    // Read settings device 1
 | 
			
		||||
#define SMI_DSW1    0x1c    // Write settings device 1
 | 
			
		||||
#define SMI_DSR2    0x20    // Read settings device 2
 | 
			
		||||
#define SMI_DSW2    0x24    // Write settings device 2
 | 
			
		||||
#define SMI_DSR3    0x28    // Read settings device 3
 | 
			
		||||
#define SMI_DSW3    0x2c    // Write settings device 3
 | 
			
		||||
#define SMI_DMC     0x30    // DMA control
 | 
			
		||||
#define SMI_DCS     0x34    // Direct control/status
 | 
			
		||||
#define SMI_DCA     0x38    // Direct address
 | 
			
		||||
#define SMI_DCD     0x3c    // Direct data
 | 
			
		||||
#define SMI_FD      0x40    // FIFO debug
 | 
			
		||||
#define SMI_REGLEN  (SMI_FD * 4)
 | 
			
		||||
 | 
			
		||||
// Data widths
 | 
			
		||||
#define SMI_8_BITS  0
 | 
			
		||||
#define SMI_16_BITS 1
 | 
			
		||||
#define SMI_18_BITS 2
 | 
			
		||||
#define SMI_9_BITS  3
 | 
			
		||||
 | 
			
		||||
// DMA request
 | 
			
		||||
#define DMA_SMI_DREQ 4
 | 
			
		||||
 | 
			
		||||
// Union of 32-bit value with register bitfields
 | 
			
		||||
#define REG_DEF(name, fields) typedef union {struct {volatile uint32_t fields;}; volatile uint32_t value;} name
 | 
			
		||||
 | 
			
		||||
// Control and status register
 | 
			
		||||
#define SMI_CS_FIELDS \
 | 
			
		||||
    enable:1, done:1, active:1, start:1, clear:1, write:1, _x1:2,\
 | 
			
		||||
    teen:1, intd:1, intt:1, intr:1, pvmode:1, seterr:1, pxldat:1, edreq:1,\
 | 
			
		||||
    _x2:8, _x3:1, aferr:1, txw:1, rxr:1, txd:1, rxd:1, txe:1, rxf:1
 | 
			
		||||
REG_DEF(SMI_CS_REG, SMI_CS_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Data length register
 | 
			
		||||
#define SMI_L_FIELDS \
 | 
			
		||||
    len:32
 | 
			
		||||
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Address & device number
 | 
			
		||||
#define SMI_A_FIELDS \
 | 
			
		||||
    addr:6, _x1:2, dev:2
 | 
			
		||||
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Data FIFO
 | 
			
		||||
#define SMI_D_FIELDS \
 | 
			
		||||
    data:32
 | 
			
		||||
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
 | 
			
		||||
 | 
			
		||||
// DMA control register
 | 
			
		||||
#define SMI_DMC_FIELDS \
 | 
			
		||||
    reqw:6, reqr:6, panicw:6, panicr:6, dmap:1, _x1:3, dmaen:1
 | 
			
		||||
REG_DEF(SMI_DMC_REG, SMI_DMC_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Device settings: read (1 of 4)
 | 
			
		||||
#define SMI_DSR_FIELDS \
 | 
			
		||||
    rstrobe:7, rdreq:1, rpace:7, rpaceall:1, \
 | 
			
		||||
    rhold:6, fsetup:1, mode68:1, rsetup:6, rwidth:2
 | 
			
		||||
REG_DEF(SMI_DSR_REG, SMI_DSR_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Device settings: write (1 of 4)
 | 
			
		||||
#define SMI_DSW_FIELDS \
 | 
			
		||||
    wstrobe:7, wdreq:1, wpace:7, wpaceall:1, \
 | 
			
		||||
    whold:6, wswap:1, wformat:1, wsetup:6, wwidth:2
 | 
			
		||||
REG_DEF(SMI_DSW_REG, SMI_DSW_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Direct control register
 | 
			
		||||
#define SMI_DCS_FIELDS \
 | 
			
		||||
    enable:1, start:1, done:1, write:1
 | 
			
		||||
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Direct control address & device number
 | 
			
		||||
#define SMI_DCA_FIELDS \
 | 
			
		||||
    addr:6, _x1:2, dev:2
 | 
			
		||||
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Direct control data
 | 
			
		||||
#define SMI_DCD_FIELDS \
 | 
			
		||||
    data:32
 | 
			
		||||
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
 | 
			
		||||
 | 
			
		||||
// Debug register
 | 
			
		||||
#define SMI_FLVL_FIELDS \
 | 
			
		||||
    fcnt:6, _x1:2, flvl:6
 | 
			
		||||
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
 | 
			
		||||
 | 
			
		||||
#define CLK_SMI_CTL     0xb0
 | 
			
		||||
#define CLK_SMI_DIV     0xb4
 | 
			
		||||
 | 
			
		||||
// EOF
 | 
			
		||||
		Reference in New Issue
	
	Block a user