kopia lustrzana https://github.com/cariboulabs/cariboulite
updated structure
rodzic
b7e2b9588d
commit
46d51fc9ae
61
README.md
61
README.md
|
@ -1,27 +1,44 @@
|
|||
# CaribouLite
|
||||
CaribouLite is an affordable, educational, open-source SDR platform and a HAT for the Raspberry-Pi family of boards (40-pin versions only). It is built for makers, hackers, and researchers and was designed to complement the current SDR (Software Defined Radio) eco-systems offering with a scalable, standalone dual-channel software-defined radio.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="hardware/rev2/pictures/DSC_1151.jpg" alt="CaribouLite R2"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CaribouLite Rev#2 SDR mounted on a RPI-Zero</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
CaribouLite has been submitted to CrowdSupply and has been pre-launched! [Visit our page](https://www.crowdsupply.com/cariboulabs/cariboulite)
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="hardware/rev1/DSC_1044.jpg" alt="CaribouLite R1"></td>
|
||||
<td><img src="hardware/rev2/pictures/DSC_1146.jpg" alt="CaribouLite Connectors"></td>
|
||||
<td><img src="hardware/rev2/pictures/DSC_1151.jpg" alt="CaribouLite R2" height="200"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CaribouLite Rev#1 - the prototype version</td>
|
||||
<td>CaribouLite Rev#2 - connectors view</td>
|
||||
<td>CaribouLite R2.5 SDR mounted on a RPI-Zero</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Getting Started & Installation
|
||||
Use the following steps for successful installation of the CaribouLite on your choice of RPI board
|
||||
1. Mount the CaribouLite on a **un-powered** RPI device using the 40-pin header.
|
||||
2. Power the RPI device, wait for it to finish boot sequence.
|
||||
3. Clone this repository in your choice of directory
|
||||
```
|
||||
mkdir projects
|
||||
git clone https://github.com/cariboulabs/cariboulite
|
||||
cd cariboulite
|
||||
```
|
||||
4. Depending on whether you run on a headless (not monitor nor keyboard / mouse) system or not use the following setup command:
|
||||
```
|
||||
sudo setup.sh gui
|
||||
# or
|
||||
sudo setup.sh
|
||||
```
|
||||
|
||||
The setup script requires internet connection and it follows the following automatic steps:
|
||||
I. System type discovery and dependency check
|
||||
II. Installation of the not-found dependencies
|
||||
III. CaribouLite code compilation and installation
|
||||
IV. EEPROM setup - boot time module's loading and device tree definition.
|
||||
|
||||
Following these steps, the RPI has to be rebooted before starting using it as an SDR.
|
||||
|
||||
# SMI Interface
|
||||
|
||||
Unlike many other HAT projects, CaribouLite utilizes the <B>SMI</B> (Secondary Memory Interface) present on all the 40-pin RPI versions. This interface is not thoroughly documented by both Raspberry-Pi documentation and Broadcomm's reference manuals. An amazing work done by [Lean2](https://iosoft.blog/2020/07/16/raspberry-pi-smi/) (code in [git repo](https://github.com/jbentham/rpi)) in hacking this interface has contributed to CaribouLite's technical feasibility. A deeper overview of the interface is provided by G.J. Van Loo, 2017 [Secondary_Memory_Interface.pdf](docs/smi/Secondary%20Memory%20Interface.pdf). The SMI interface allows exchanging up to ~500Mbit/s between the RPI and the HAT, and yet, the results vary between the different versions of RPI. The results further depend on the specific RPI version's DMA speeds.
|
||||
|
||||
The SMI interface can be accessed from the user space Linux applications as shown in [Lean2](https://iosoft.blog/2020/07/16/raspberry-pi-smi/), but it also provides a neat minimal charachter device interface in the `/dev` directory using the `open`, `close`, `write`, `read`, and `ioctl` system calls. More on this interesting interface in the [designated readme file](software/libcariboulite/src/caribou_smi/index.md).
|
||||
|
@ -30,7 +47,7 @@ The SMI interface is used as memory interface that pipes the I/Q complex samples
|
|||
A single ADC sample contains 13 bit (I) and 13 bit (Q), that are streamed with a maximal sample rate of 4 MSPS from the AT86RF215 IC to an FPGA. The FPGA emulated SMI compliant memory interface for the RPI SoC.
|
||||
Each RF channel (CaribouLite has two of them) requires 4 bytes (samples padded to 32-bit) per sample (and I/Q pair) => 16 MBytes/sec which are 128 MBits/sec. In addition to the 13 bit for each of I/Q, the Tx/Rx streams of data contain flow control and configuration bits. The modem (AT86RF215) IC by Microchip contains two RX I/Q outputs from its ADCs (one for each physical channel - sub-1GHz and 2.4GHz), and a single TX I/Q intput directed to the DACs.
|
||||
|
||||
CaribouLite has been submitted to CrowdSupply and has been pre-launched! [Visit our page](https://www.crowdsupply.com/cariboulabs/cariboulite)
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -44,6 +61,18 @@ CaribouLite has been submitted to CrowdSupply and has been pre-launched! [Visit
|
|||
# Hardware Revisions
|
||||
The board first prototyping (**Red**) revision ([REV1](hardware/rev1)) has been produced and tested to meet our vision on the board's capabilities. This revision was used to test its RF parts, the digital parts, and to develop its firmware and software support over the RPI.
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="hardware/rev1/DSC_1044.jpg" alt="CaribouLite R1"></td>
|
||||
<td><img src="hardware/rev2/pictures/DSC_1146.jpg" alt="CaribouLite Connectors"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CaribouLite Rev#1 - the prototype version</td>
|
||||
<td>CaribouLite Rev#2 - connectors view</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The second revision ([REV2](hardware/rev2)) - **White** - was then designed to further refine the design as described below:
|
||||
1. Image rejection filtering improvement - U10 and U12 (HPF & LPF) - have been replaced by integrated LTCC filters by MiniCircuits with much better out-of-band rejection than the former ones.
|
||||
2. Removing FPGA flash - redundant given the fact that the the RPI configures the FPGA in <1sec over SPI. Even if we have a whole library of custom made FPGA firmware files, switching between them is as simple and fast as a single linux command.
|
||||
|
@ -124,10 +153,6 @@ Receive noise figure | <5 dB | <6 dB @ 30-3500 MHz, <
|
|||
12. Wide band SMA connector
|
||||
13. Sub 1-GHz SMA connector
|
||||
|
||||
|
||||
# Getting Started & Installation
|
||||
TBD
|
||||
|
||||
# Disclaimer
|
||||
CaribouLite is a test equipment for RF systems. You are responsible for using your CaribouLite legally.
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# RPI CaribouLite - Headless Installation
|
||||
1. SSH into the RPI device:
|
||||
In your favourite ssh client connect `pi@raspberrypi.local`.
|
||||
2. Create the
|
|
@ -1,5 +0,0 @@
|
|||
# RPI SDR Soapy Driver
|
||||
TODO
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
|
|
@ -1,5 +1,8 @@
|
|||
# CaribouLite Software
|
||||
TBD
|
||||
Containing all the software side for:
|
||||
- `libcariboulite`: SDR APIs library - including SoapySDR
|
||||
- `devicetrees`: linux device-trees overlays sources and binaries
|
||||
- `utils`: various utilities
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
|
|
@ -1,6 +0,0 @@
|
|||
# CaribouLite RPI Low-Level API
|
||||
TODO
|
||||
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
|
Plik binarny nie jest wyświetlany.
|
@ -134,6 +134,14 @@ io_utils/io_utils_spi.h
|
|||
at86rf215_regs.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/at86rf215/at86rf215_regs.h
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/at86rf215/at86rf215_events.c
|
||||
stdio.h
|
||||
-
|
||||
zf_log/zf_log.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/at86rf215/zf_log/zf_log.h
|
||||
at86rf215_common.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/at86rf215/at86rf215_common.h
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/at86rf215/at86rf215_radio.c
|
||||
stdint.h
|
||||
-
|
||||
|
|
|
@ -54,109 +54,39 @@ pthread.h
|
|||
io_utils.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/io_utils.h
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/command.c
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/io_utils_sys_info.c
|
||||
stdio.h
|
||||
-
|
||||
arpa/inet.h
|
||||
-
|
||||
time.h
|
||||
-
|
||||
zf_log/zf_log.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/zf_log/zf_log.h
|
||||
io_utils_sys_info.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/io_utils_sys_info.h
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/io_utils_sys_info.h
|
||||
stdio.h
|
||||
-
|
||||
string.h
|
||||
-
|
||||
stdarg.h
|
||||
-
|
||||
stdlib.h
|
||||
-
|
||||
ctype.h
|
||||
-
|
||||
inttypes.h
|
||||
-
|
||||
pigpio.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/pigpio.h
|
||||
command.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/command.h
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/command.h
|
||||
stdio.h
|
||||
-
|
||||
string.h
|
||||
-
|
||||
pigpio.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/pigpio.h
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/custom.cext
|
||||
pigpio.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/pigpio.h
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/pigpio.c
|
||||
stdio.h
|
||||
-
|
||||
string.h
|
||||
-
|
||||
strings.h
|
||||
-
|
||||
stdlib.h
|
||||
-
|
||||
stdint.h
|
||||
-
|
||||
inttypes.h
|
||||
-
|
||||
stdarg.h
|
||||
-
|
||||
ctype.h
|
||||
-
|
||||
syslog.h
|
||||
-
|
||||
poll.h
|
||||
stdlib.h
|
||||
-
|
||||
unistd.h
|
||||
-
|
||||
fcntl.h
|
||||
-
|
||||
termios.h
|
||||
-
|
||||
signal.h
|
||||
-
|
||||
errno.h
|
||||
-
|
||||
time.h
|
||||
string.h
|
||||
-
|
||||
sys/ioctl.h
|
||||
-
|
||||
limits.h
|
||||
-
|
||||
pthread.h
|
||||
-
|
||||
sys/time.h
|
||||
-
|
||||
sys/mman.h
|
||||
-
|
||||
sys/types.h
|
||||
-
|
||||
sys/stat.h
|
||||
-
|
||||
sys/file.h
|
||||
-
|
||||
sys/socket.h
|
||||
-
|
||||
sys/sysmacros.h
|
||||
-
|
||||
netinet/tcp.h
|
||||
-
|
||||
arpa/inet.h
|
||||
-
|
||||
sys/select.h
|
||||
-
|
||||
fnmatch.h
|
||||
-
|
||||
glob.h
|
||||
-
|
||||
arpa/inet.h
|
||||
-
|
||||
zf_log/zf_log.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/zf_log/zf_log.h
|
||||
pigpio.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/pigpio.h
|
||||
command.h
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/command.h
|
||||
custom.cext
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/custom.cext
|
||||
|
||||
/home/pi/projects/cariboulite/software/libcariboulite/src/io_utils/pigpio/pigpio.h
|
||||
stddef.h
|
||||
|
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,46 @@
|
|||
# CaribouLite RPI API
|
||||
This folder contains the low-level and SoapySDR APIs for CaribouLite
|
||||
|
||||
# Building
|
||||
To start from scratch we need to check for the dependencies and install them as needed.
|
||||
|
||||
Dependencies installation:
|
||||
```
|
||||
# Update the package definitions
|
||||
sudo apt update
|
||||
|
||||
# We used gcc version 8.3+
|
||||
sudo apt install gcc
|
||||
|
||||
# We used libsoapysdr-dev version 0.6.1-4+
|
||||
sudo apt install libsoapysdr-dev
|
||||
|
||||
# cmake version 3.15+
|
||||
sudo apt install cmake
|
||||
```
|
||||
|
||||
Now to compile we use `cmake` as follows:
|
||||
|
||||
```
|
||||
# create the building directory to contain
|
||||
# compilation / linking artifacts. if already exists skip
|
||||
# the creation
|
||||
mkdir build
|
||||
|
||||
# goto the build directory
|
||||
cd build
|
||||
|
||||
# tell cmake to create the Makefiles
|
||||
# according to the code in the parent directory
|
||||
cmake ../
|
||||
|
||||
# build the code (this will take about 30 seconds @ RPi4)
|
||||
make
|
||||
|
||||
# install the package in your Linux
|
||||
# environment (including SoapyAPIs)
|
||||
sudo make install
|
||||
```
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_Main"
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_BaseBand"
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_Events"
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_Radio"
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "FPGA"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOU_SMI_Main"
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Raspberry Pi source files, see https://iosoft.blog for details
|
|
@ -1,471 +0,0 @@
|
|||
// Raspberry Pi ADC DMA tests; 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.
|
||||
//
|
||||
// v0.01 JPB 9/6/20 Adapted from rpi_adc_ads7884 v0.11
|
||||
// Modified MCP3008 functions to match
|
||||
// v0.02 JPB 9/6/20 Corrected MCP3008 min & max frequencies
|
||||
// v0.03 JPB 10/6/20 Changed MCP3008 max frequency from 2 to 2.6 MHz
|
||||
// v0.04 JPB 11/6/20 Moved CLOCK_HZ definition to top of rpi_dma_utils.h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "rpi_dma_utils.h"
|
||||
|
||||
#define VERSION "0.04"
|
||||
|
||||
#define MCP3008 // ADC to use: MCP3008 or ADS7884
|
||||
#define SAMPLE_RATE 10000 // Default sample rate (samples/sec)
|
||||
|
||||
#if defined(MCP3008)
|
||||
#define RX_SAMPLE_SIZE 4 // Number of raw Rx bytes per sample
|
||||
#define ADC_CHAN 0 // ADC channel number
|
||||
#define ADC_9_BITS 0 // Set non-zero for 9-bit data
|
||||
#define SPI_CSVAL 0 // Additional CS register settings
|
||||
#define MIN_SPI_FREQ 10000 // Minimum SPI frequency
|
||||
#define MAX_SPI_FREQ 2600000 // Maximum SPI frequency
|
||||
#define VC_MEM_SIZE(ns) (PAGE_SIZE + ((ns)+4)*RX_SAMPLE_SIZE)
|
||||
#define ADC_VOLTAGE(n) (((n) * 3.3) / 1024.0)
|
||||
|
||||
#elif defined(ADS7884)
|
||||
#define RX_SAMPLE_SIZE 2 // Number of raw Rx bytes per sample
|
||||
#define ADC_CHAN 0 // ADC channel number (ignored)
|
||||
#define SPI_CSVAL 0 // Additional CS register settings
|
||||
#define MIN_SPI_FREQ 1000 // Minimum SPI frequency
|
||||
#define MAX_SPI_FREQ 42000000// Maximum SPI frequency
|
||||
#define ADC_9_BITS 1 // Set non-0 for 9-bit data (0 for 8-bit)
|
||||
#define VC_MEM_SIZE(ns) (PAGE_SIZE + ((ns)+4)*RX_SAMPLE_SIZE)
|
||||
#define ADC_VOLTAGE(n) (((n) * 3.3) / 1024.0)
|
||||
#endif
|
||||
|
||||
#define SPI0_CE_NUM 0
|
||||
#define SPI0_CE0_PIN 8
|
||||
#define SPI0_MISO_PIN 9
|
||||
#define SPI0_MOSI_PIN 10
|
||||
#define SPI0_SCLK_PIN 11
|
||||
|
||||
#define SPI0_BASE (PHYS_REG_BASE + 0x204000)
|
||||
#define SPI_CS 0x00
|
||||
#define SPI_FIFO 0x04
|
||||
#define SPI_CLK 0x08
|
||||
#define SPI_DLEN 0x0c
|
||||
#define SPI_DC 0x14
|
||||
#define SPI_FIFO_CLR (3 << 4)
|
||||
#define SPI_TX_FIFO_CLR (1 << 4)
|
||||
#define SPI_TFR_ACT (1 << 7)
|
||||
#define SPI_DMA_EN (1 << 8)
|
||||
#define SPI_AUTO_CS (1 << 11)
|
||||
|
||||
// SPI register strings
|
||||
char *spi_regstrs[] = {"CS", "FIFO", "CLK", "DLEN", "LTOH", "DC", ""};
|
||||
|
||||
// Buffer for processed ADC samples
|
||||
uint16_t *sample_buff;
|
||||
|
||||
// Virtual memory pointers to acceess GPIO, DMA & SPI from user space
|
||||
extern MEM_MAP gpio_regs, dma_regs;
|
||||
MEM_MAP vc_mem, spi_regs;
|
||||
|
||||
void terminate(int sig);
|
||||
void map_devices(void);
|
||||
void get_uncached_mem(MEM_MAP *mp, int size);
|
||||
int adc_sample(int chan);
|
||||
int adc_dma_sample_mcp3008(MEM_MAP *mp, int chan);
|
||||
int adc_sample_ads7884(int chan);
|
||||
int adc_dma_samples(MEM_MAP *mp, int chan, uint16_t *buff, int nsamp);
|
||||
int adc_dma_samples_mcp3008(MEM_MAP *mp, int chan, uint16_t *buff, int nsamp);
|
||||
int adc_dma_samples_ads7884(MEM_MAP *mp, int chan, uint16_t *buff, int nsamp);
|
||||
void dma_test_spi(MEM_MAP *mp);
|
||||
void dma_wait(int chan);
|
||||
int mcp3008_tx_data(void *buff, int chan);
|
||||
int mcp3008_rx_value(void *buff);
|
||||
int init_spi(int hz);
|
||||
void spi_clear(void);
|
||||
void spi_cs(int set);
|
||||
void spi_xfer(uint8_t *txd, uint8_t *rxd, int len);
|
||||
void spi_disable(void);
|
||||
void disp_spi(void);
|
||||
|
||||
// Main program
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int args=0, sample_count=0, sample_rate=SAMPLE_RATE;
|
||||
int f, spi_freq, val, i, n;
|
||||
|
||||
printf("SPI ADC test v" VERSION "\n");
|
||||
while (argc > ++args) // Process command-line args
|
||||
{
|
||||
if (argv[args][0] == '-')
|
||||
{
|
||||
switch (toupper(argv[args][1]))
|
||||
{
|
||||
case 'N': // -N: number of samples
|
||||
if (args>=argc-1 || !isdigit(argv[args+1][0]))
|
||||
fprintf(stderr, "Error: no sample count\n");
|
||||
else
|
||||
sample_count = atoi(argv[++args]);
|
||||
break;
|
||||
case 'R': // -R: sample rate (samples/sec)
|
||||
if (args>=argc-1 || !isdigit(argv[args+1][0]))
|
||||
fprintf(stderr, "Error: no sample rate\n");
|
||||
else
|
||||
sample_rate = atoi(argv[++args]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
map_devices();
|
||||
map_uncached_mem(&vc_mem, VC_MEM_SIZE(sample_count));
|
||||
signal(SIGINT, terminate);
|
||||
spi_freq = sample_rate * RX_SAMPLE_SIZE*8;
|
||||
if (spi_freq < MIN_SPI_FREQ)
|
||||
fail("Invalid sample rate\n");
|
||||
f = init_spi(spi_freq);
|
||||
if (sample_count == 0)
|
||||
{
|
||||
printf("SPI frequency %u Hz, %u sample/s\n", f, f/(RX_SAMPLE_SIZE*8));
|
||||
val = adc_sample(ADC_CHAN);
|
||||
printf("ADC value %u = %4.3fV\n", val, val*3.3/1024.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(sample_buff = malloc((sample_count+4) * 2)))
|
||||
fail("Can't allocate sample buffer\n");
|
||||
printf("%u samples at %u S/s\n", sample_count, f/(RX_SAMPLE_SIZE*8));
|
||||
n = adc_dma_samples(&vc_mem, ADC_CHAN, sample_buff, sample_count);
|
||||
for (i=0; i<n; i++)
|
||||
printf("%4.3f\n", ADC_VOLTAGE(sample_buff[i]));
|
||||
}
|
||||
terminate(0);
|
||||
}
|
||||
|
||||
// Catastrophic failure in initial setup
|
||||
void fail(char *s)
|
||||
{
|
||||
printf(s);
|
||||
terminate(0);
|
||||
}
|
||||
|
||||
// Free memory segments and exit
|
||||
void terminate(int sig)
|
||||
{
|
||||
printf("Closing\n");
|
||||
spi_disable();
|
||||
stop_dma(DMA_CHAN_A);
|
||||
stop_dma(DMA_CHAN_B);
|
||||
unmap_periph_mem(&vc_mem);
|
||||
unmap_periph_mem(&spi_regs);
|
||||
unmap_periph_mem(&dma_regs);
|
||||
unmap_periph_mem(&gpio_regs);
|
||||
if (sample_buff)
|
||||
free(sample_buff);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Map GPIO, DMA and SPI 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(&spi_regs, (void *)SPI0_BASE, PAGE_SIZE);
|
||||
}
|
||||
|
||||
// Get uncached memory
|
||||
void get_uncached_mem(MEM_MAP *mp, int size)
|
||||
{
|
||||
if (!map_uncached_mem(mp, size))
|
||||
fail("Error: can't allocate uncached memory\n");
|
||||
}
|
||||
|
||||
// Fetch sample from ADC
|
||||
int adc_sample(int chan)
|
||||
{
|
||||
#if defined(MCP3008)
|
||||
return(adc_dma_sample_mcp3008(&vc_mem, chan));
|
||||
#elif defined(ADS7884)
|
||||
adc_sample_ads7884(chan);
|
||||
return(adc_sample_ads7884(chan));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Fetch single sample from MCP3008 ADC using DMA
|
||||
int adc_dma_sample_mcp3008(MEM_MAP *mp, int chan)
|
||||
{
|
||||
DMA_CB *cbs=mp->virt;
|
||||
uint32_t dlen, *txd=(uint32_t *)(cbs+2);
|
||||
uint8_t *rxdata=(uint8_t *)(txd+0x10);
|
||||
|
||||
enable_dma(DMA_CHAN_A);
|
||||
enable_dma(DMA_CHAN_B);
|
||||
dlen = 4;
|
||||
txd[0] = (dlen << 16) | SPI_TFR_ACT;
|
||||
mcp3008_tx_data(&txd[1], chan);
|
||||
cbs[0].ti = DMA_SRCE_DREQ | (DMA_SPI_RX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_DEST_INC;
|
||||
cbs[0].tfr_len = dlen;
|
||||
cbs[0].srce_ad = REG_BUS_ADDR(spi_regs, SPI_FIFO);
|
||||
cbs[0].dest_ad = MEM_BUS_ADDR(mp, rxdata);
|
||||
cbs[1].ti = DMA_DEST_DREQ | (DMA_SPI_TX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_SRCE_INC;
|
||||
cbs[1].tfr_len = dlen + 4;
|
||||
cbs[1].srce_ad = MEM_BUS_ADDR(mp, txd);
|
||||
cbs[1].dest_ad = REG_BUS_ADDR(spi_regs, SPI_FIFO);
|
||||
*REG32(spi_regs, SPI_DC) = (8 << 24) | (4 << 16) | (8 << 8) | 4;
|
||||
*REG32(spi_regs, SPI_CS) = SPI_TFR_ACT | SPI_DMA_EN | SPI_AUTO_CS | SPI_FIFO_CLR | SPI_CSVAL;
|
||||
*REG32(spi_regs, SPI_DLEN) = 0;
|
||||
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
|
||||
start_dma(mp, DMA_CHAN_B, &cbs[1], 0);
|
||||
dma_wait(DMA_CHAN_A);
|
||||
#if DEBUG
|
||||
for (int i=0; i<dlen; i++)
|
||||
printf("%02X ", rxdata[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
return(mcp3008_rx_value(rxdata));
|
||||
}
|
||||
|
||||
// Fetch last sample from ADS7884 (call twice for current value)
|
||||
int adc_sample_ads7884(int chan)
|
||||
{
|
||||
uint8_t txdata[4]={0xff,0,0,0xff}, rxdata[4];
|
||||
|
||||
spi_clear();
|
||||
spi_cs(1);
|
||||
spi_xfer(txdata, rxdata, sizeof(txdata));
|
||||
spi_cs(0);
|
||||
return(((int)rxdata[1] << 4) | ((int)rxdata[2] >> 4));
|
||||
}
|
||||
|
||||
// Fetch samples from ADC using DMA
|
||||
int adc_dma_samples(MEM_MAP *mp, int chan, uint16_t *buff, int nsamp)
|
||||
{
|
||||
#if defined(MCP3008)
|
||||
return(adc_dma_samples_mcp3008(mp, chan, buff, nsamp));
|
||||
#elif defined(ADS7884)
|
||||
return(adc_dma_samples_ads7884(mp, chan, buff, nsamp));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Fetch samples from MCP3008 ADC using DMA
|
||||
int adc_dma_samples_mcp3008(MEM_MAP *mp, int chan, uint16_t *buff, int nsamp)
|
||||
{
|
||||
DMA_CB *cbs=mp->virt;
|
||||
uint32_t i, dlen, *txd=(uint32_t *)(cbs+4), *pindata=(uint32_t *)(txd+0x10);
|
||||
uint8_t *rxdata=(uint8_t *)(txd+0x20);
|
||||
|
||||
gpio_out(SPI0_CE0_PIN, 1); // Get control of CE pin
|
||||
gpio_mode(SPI0_CE0_PIN, GPIO_OUT);
|
||||
enable_dma(DMA_CHAN_A); // Enable 2 DMA channels
|
||||
enable_dma(DMA_CHAN_B);
|
||||
mcp3008_tx_data(&txd[1], chan); // Create Tx data word
|
||||
dlen = nsamp * 4; // One 4-byte word per sample
|
||||
*pindata = 1 << SPI0_CE0_PIN; // Data to set/clear Chip Select pin
|
||||
txd[0] = (dlen << 16) | SPI_TFR_ACT;// First Tx byte: SPI settings
|
||||
// Control block 0: read data from SPI FIFO
|
||||
cbs[0].ti = DMA_SRCE_DREQ | (DMA_SPI_RX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_DEST_INC;
|
||||
cbs[0].tfr_len = dlen;
|
||||
cbs[0].srce_ad = REG_BUS_ADDR(spi_regs, SPI_FIFO);
|
||||
cbs[0].dest_ad = MEM_BUS_ADDR(mp, rxdata);
|
||||
// Control block 1: CS high
|
||||
cbs[1].srce_ad = cbs[2].srce_ad = MEM_BUS_ADDR(mp, pindata);
|
||||
cbs[1].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_SET0);
|
||||
cbs[1].tfr_len = cbs[2].tfr_len = cbs[3].tfr_len = 4;
|
||||
cbs[1].ti = cbs[2].ti = DMA_DEST_DREQ | (DMA_SPI_TX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_SRCE_INC;
|
||||
// Control block 2: CS low
|
||||
cbs[2].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_CLR0);
|
||||
// Control block 3: write data to Tx FIFO
|
||||
cbs[3].ti = DMA_DEST_DREQ | (DMA_SPI_TX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_SRCE_INC;
|
||||
cbs[3].srce_ad = MEM_BUS_ADDR(mp, &txd[1]);
|
||||
cbs[3].dest_ad = REG_BUS_ADDR(spi_regs, SPI_FIFO);
|
||||
// Link CB1, CB2 and CB3 in endless loop
|
||||
cbs[1].next_cb = MEM_BUS_ADDR(mp, &cbs[2]);
|
||||
cbs[2].next_cb = MEM_BUS_ADDR(mp, &cbs[3]);
|
||||
cbs[3].next_cb = MEM_BUS_ADDR(mp, &cbs[1]);
|
||||
// DMA request every 4 bytes, panic if 8 bytes
|
||||
*REG32(spi_regs, SPI_DC) = (8 << 24) | (4 << 16) | (8 << 8) | 4;
|
||||
// Clear SPI length register and Tx & Rx FIFOs, enable DMA
|
||||
*REG32(spi_regs, SPI_DLEN) = 0;
|
||||
*REG32(spi_regs, SPI_CS) = SPI_TFR_ACT | SPI_DMA_EN | SPI_AUTO_CS | SPI_FIFO_CLR | SPI_CSVAL;
|
||||
// Send out first Tx bytes
|
||||
*REG32(spi_regs, SPI_FIFO) = txd[0];
|
||||
// Start DMA, wait until complete
|
||||
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
|
||||
start_dma(mp, DMA_CHAN_B, &cbs[3], DMA_PRIORITY(15));
|
||||
dma_wait(DMA_CHAN_A);
|
||||
// Relinquish control of CE pin
|
||||
gpio_mode(SPI0_CE0_PIN, GPIO_ALT0);
|
||||
#if DEBUG
|
||||
for (i=0; i<dlen; i++)
|
||||
printf("%02X ", rxdata[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
// Convert 32-bit raw values to 16-bit data
|
||||
for (i=0; i<nsamp; i++)
|
||||
buff[i] = mcp3008_rx_value(&rxdata[i*4]);
|
||||
return(nsamp);
|
||||
}
|
||||
|
||||
// Fetch samples from ADS7884 ADC using DMA
|
||||
int adc_dma_samples_ads7884(MEM_MAP *mp, int chan, uint16_t *buff, int nsamp)
|
||||
{
|
||||
DMA_CB *cbs=mp->virt;
|
||||
uint32_t i, dlen, shift, *txd=(uint32_t *)(cbs+3);
|
||||
uint8_t *rxdata=(uint8_t *)(txd+0x10);
|
||||
|
||||
enable_dma(DMA_CHAN_A); // Enable DMA channels
|
||||
enable_dma(DMA_CHAN_B);
|
||||
dlen = (nsamp+3) * 2; // 2 bytes/sample, plus 3 dummy samples
|
||||
// Control block 0: store Rx data in buffer
|
||||
cbs[0].ti = DMA_SRCE_DREQ | (DMA_SPI_RX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_DEST_INC;
|
||||
cbs[0].tfr_len = dlen;
|
||||
cbs[0].srce_ad = REG_BUS_ADDR(spi_regs, SPI_FIFO);
|
||||
cbs[0].dest_ad = MEM_BUS_ADDR(mp, rxdata);
|
||||
// Control block 1: continuously repeat last Tx word (pulse CS low)
|
||||
cbs[1].srce_ad = MEM_BUS_ADDR(mp, &txd[2]);
|
||||
cbs[1].dest_ad = REG_BUS_ADDR(spi_regs, SPI_FIFO);
|
||||
cbs[1].tfr_len = 4;
|
||||
cbs[1].ti = DMA_DEST_DREQ | (DMA_SPI_TX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_SRCE_INC;
|
||||
cbs[1].next_cb = MEM_BUS_ADDR(mp, &cbs[1]);
|
||||
// Control block 2: send first 2 Tx words, then switch to CB1 for the rest
|
||||
cbs[2].srce_ad = MEM_BUS_ADDR(mp, &txd[0]);
|
||||
cbs[2].dest_ad = REG_BUS_ADDR(spi_regs, SPI_FIFO);
|
||||
cbs[2].tfr_len = 8;
|
||||
cbs[2].ti = DMA_DEST_DREQ | (DMA_SPI_TX_DREQ << 16) | DMA_WAIT_RESP | DMA_CB_SRCE_INC;
|
||||
cbs[2].next_cb = MEM_BUS_ADDR(mp, &cbs[1]);
|
||||
// DMA request every 4 bytes, panic if 8 bytes
|
||||
*REG32(spi_regs, SPI_DC) = (8 << 24) | (4 << 16) | (8 << 8) | 4;
|
||||
// Clear SPI length register and Tx & Rx FIFOs, enable DMA
|
||||
*REG32(spi_regs, SPI_DLEN) = 0;
|
||||
*REG32(spi_regs, SPI_CS) = SPI_TFR_ACT | SPI_DMA_EN | SPI_AUTO_CS | SPI_FIFO_CLR | SPI_CSVAL;
|
||||
// Data to be transmited: 32-bit words, MS bit of LS byte is sent first
|
||||
txd[0] = (dlen << 16) | SPI_TFR_ACT;// SPI config: data len & TI setting
|
||||
txd[1] = 0xffffffff; // Set CS high
|
||||
txd[2] = 0x01000100; // Pulse CS low
|
||||
// Enable DMA, wait until complete
|
||||
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
|
||||
start_dma(mp, DMA_CHAN_B, &cbs[2], 0);
|
||||
dma_wait(DMA_CHAN_A);
|
||||
#if DEBUG
|
||||
for (i=0; i<dlen; i++)
|
||||
printf("%02X ", rxdata[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
// Check whether Rx data has 1 bit delay with respect to Tx
|
||||
shift = rxdata[4] & 0x80 ? 3 : 4;
|
||||
// Convert raw data to 16-bit unsigned values, ignoring first 3
|
||||
for (i=0; i<nsamp; i++)
|
||||
buff[i] = ((rxdata[i*2+6]<<8 | rxdata[i*2+7]) >> shift) & 0x3ff;
|
||||
return(nsamp);
|
||||
}
|
||||
|
||||
// Wait until DMA is complete
|
||||
void dma_wait(int chan)
|
||||
{
|
||||
int n = 1000;
|
||||
|
||||
do {
|
||||
usleep(100);
|
||||
} while (dma_transfer_len(chan) && --n);
|
||||
if (n == 0)
|
||||
printf("DMA transfer timeout\n");
|
||||
}
|
||||
|
||||
// Return Tx data for MCP3008
|
||||
int mcp3008_tx_data(void *buff, int chan)
|
||||
{
|
||||
#if ADC_9_BITS
|
||||
uint8_t txd[2]={0xc0|(chan<<3), 0x00};
|
||||
#else
|
||||
uint8_t txd[3]={0x01, 0x80|(chan<<4), 0x00};
|
||||
#endif
|
||||
memcpy(buff, txd, sizeof(txd));
|
||||
return(sizeof(txd));
|
||||
}
|
||||
|
||||
// Return value from ADC Rx data
|
||||
int mcp3008_rx_value(void *buff)
|
||||
{
|
||||
uint8_t *rxd=buff;
|
||||
#if ADC_9_BITS
|
||||
return((((int)rxd[0] << 9) | ((int)rxd[1] << 1)) & 0x3ff);
|
||||
#else
|
||||
return(((int)(rxd[1]&3)<<8) | rxd[2]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Initialise SPI0, given desired clock freq; return actual value
|
||||
int init_spi(int hz)
|
||||
{
|
||||
int f, div = (CLOCK_HZ / hz + 1) & ~1;
|
||||
|
||||
gpio_set(SPI0_CE0_PIN, GPIO_ALT0, GPIO_NOPULL);
|
||||
gpio_set(SPI0_MISO_PIN, GPIO_ALT0, GPIO_PULLUP);
|
||||
gpio_set(SPI0_MOSI_PIN, GPIO_ALT0, GPIO_NOPULL);
|
||||
gpio_set(SPI0_SCLK_PIN, GPIO_ALT0, GPIO_NOPULL);
|
||||
while (div==0 || (f = CLOCK_HZ/div) > MAX_SPI_FREQ)
|
||||
div += 2;
|
||||
*REG32(spi_regs, SPI_CS) = 0x30;
|
||||
*REG32(spi_regs, SPI_CLK) = div;
|
||||
return(f);
|
||||
}
|
||||
|
||||
// Clear SPI FIFOs
|
||||
void spi_clear(void)
|
||||
{
|
||||
*REG32(spi_regs, SPI_CS) = SPI_FIFO_CLR;
|
||||
}
|
||||
|
||||
// Set / clear SPI chip select
|
||||
void spi_cs(int set)
|
||||
{
|
||||
uint32_t csval = *REG32(spi_regs, SPI_CS);
|
||||
|
||||
*REG32(spi_regs, SPI_CS) = set ? csval | 0x80 : csval & ~0x80;
|
||||
}
|
||||
|
||||
// Transfer SPI bytes
|
||||
void spi_xfer(uint8_t *txd, uint8_t *rxd, int len)
|
||||
{
|
||||
while (len--)
|
||||
{
|
||||
*REG8(spi_regs, SPI_FIFO) = *txd++;
|
||||
while((*REG32(spi_regs, SPI_CS) & (1<<17)) == 0) ;
|
||||
*rxd++ = *REG32(spi_regs, SPI_FIFO);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable SPI
|
||||
void spi_disable(void)
|
||||
{
|
||||
*REG32(spi_regs, SPI_CS) = SPI_FIFO_CLR;
|
||||
*REG32(spi_regs, SPI_CS) = 0;
|
||||
}
|
||||
|
||||
// Display DMA registers
|
||||
void disp_spi(void)
|
||||
{
|
||||
volatile uint32_t *p=REG32(spi_regs, SPI_CS);
|
||||
int i=0;
|
||||
|
||||
while (spi_regstrs[i][0])
|
||||
printf("%-4s %08X ", spi_regstrs[i++], *p++);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,551 +0,0 @@
|
|||
// DMA tests for the Raspberry Pi, 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.
|
||||
//
|
||||
// v0.10 JPB 25/5/20
|
||||
//
|
||||
|
||||
#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>
|
||||
|
||||
// Location of peripheral registers in physical memory
|
||||
//#define PHYS_REG_BASE 0x20000000 // Pi Zero or 1
|
||||
#define PHYS_REG_BASE 0x3F000000 // Pi 2 or 3
|
||||
//#define PHYS_REG_BASE 0xFE000000 // Pi 4
|
||||
|
||||
// Location of peripheral registers in bus memory
|
||||
#define BUS_REG_BASE 0x7E000000
|
||||
|
||||
// If non-zero, print debug information
|
||||
#define DEBUG 0
|
||||
|
||||
// Output pin to use for LED
|
||||
//#define LED_PIN 47 // Pi Zero onboard LED
|
||||
#define LED_PIN 21 // Offboard LED pin
|
||||
|
||||
// PWM clock frequency and range (FREQ/RANGE = LED flash freq)
|
||||
#define PWM_FREQ 100000
|
||||
#define PWM_RANGE 20000
|
||||
|
||||
// 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))
|
||||
|
||||
// Size of uncached memory for DMA control blocks and data
|
||||
#define DMA_MEM_SIZE PAGE_SIZE
|
||||
|
||||
// GPIO 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 VIRT_GPIO_REG(a) ((uint32_t *)((uint32_t)virt_gpio_regs + (a)))
|
||||
#define BUS_GPIO_REG(a) (GPIO_BASE-PHYS_REG_BASE+BUS_REG_BASE+(uint32_t)(a))
|
||||
#define GPIO_IN 0
|
||||
#define GPIO_OUT 1
|
||||
#define GPIO_ALT0 4
|
||||
#define GPIO_ALT2 6
|
||||
#define GPIO_ALT3 7
|
||||
#define GPIO_ALT4 3
|
||||
#define GPIO_ALT5 2
|
||||
|
||||
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
|
||||
void *virt_gpio_regs, *virt_dma_regs, *virt_pwm_regs;
|
||||
// VC mailbox file descriptor & handle, and bus memory pointer
|
||||
int mbox_fd, dma_mem_h;
|
||||
void *bus_dma_mem;
|
||||
|
||||
// Convert memory bus address to physical address (for mmap)
|
||||
#define BUS_PHYS_ADDR(a) ((void *)((uint32_t)(a)&~0xC0000000))
|
||||
|
||||
// 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 uncached 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 register definitions
|
||||
#define DMA_CHAN 5
|
||||
#define DMA_PWM_DREQ 5
|
||||
#define DMA_BASE (PHYS_REG_BASE + 0x007000)
|
||||
#define DMA_CS (DMA_CHAN*0x100)
|
||||
#define DMA_CONBLK_AD (DMA_CHAN*0x100 + 0x04)
|
||||
#define DMA_TI (DMA_CHAN*0x100 + 0x08)
|
||||
#define DMA_SRCE_AD (DMA_CHAN*0x100 + 0x0c)
|
||||
#define DMA_DEST_AD (DMA_CHAN*0x100 + 0x10)
|
||||
#define DMA_TXFR_LEN (DMA_CHAN*0x100 + 0x14)
|
||||
#define DMA_STRIDE (DMA_CHAN*0x100 + 0x18)
|
||||
#define DMA_NEXTCONBK (DMA_CHAN*0x100 + 0x1c)
|
||||
#define DMA_DEBUG (DMA_CHAN*0x100 + 0x20)
|
||||
#define DMA_ENABLE 0xff0
|
||||
#define VIRT_DMA_REG(a) ((volatile uint32_t *)((uint32_t)virt_dma_regs + a))
|
||||
char *dma_regstrs[] = {"DMA CS", "CB_AD", "TI", "SRCE_AD", "DEST_AD",
|
||||
"TFR_LEN", "STRIDE", "NEXT_CB", "DEBUG", ""};
|
||||
|
||||
// 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)));
|
||||
#define DMA_CB_DEST_INC (1<<4)
|
||||
#define DMA_CB_SRC_INC (1<<8)
|
||||
|
||||
// Virtual memory for DMA descriptors and data buffers (uncached)
|
||||
void *virt_dma_mem;
|
||||
|
||||
// Convert virtual DMA data address to a bus address
|
||||
#define BUS_DMA_MEM(a) ((uint32_t)a-(uint32_t)virt_dma_mem+(uint32_t)bus_dma_mem)
|
||||
|
||||
// PWM controller
|
||||
#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
|
||||
#define VIRT_PWM_REG(a) ((volatile uint32_t *)((uint32_t)virt_pwm_regs + (a)))
|
||||
#define BUS_PWM_REG(a) (PWM_BASE-PHYS_REG_BASE+BUS_REG_BASE+(uint32_t)(a))
|
||||
#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 18 // GPIO pin for PWM output
|
||||
#define PWM_OUT 1 // Set non-zero to enable PWM output pin
|
||||
|
||||
// Clock
|
||||
void *virt_clk_regs;
|
||||
#define CLK_BASE (PHYS_REG_BASE + 0x101000)
|
||||
#define CLK_PWM_CTL 0xa0
|
||||
#define CLK_PWM_DIV 0xa4
|
||||
#define VIRT_CLK_REG(a) ((volatile uint32_t *)((uint32_t)virt_clk_regs + (a)))
|
||||
#define CLK_PASSWD 0x5a000000
|
||||
#define CLOCK_KHZ 250000
|
||||
#define PWM_CLOCK_ID 0xa
|
||||
|
||||
#define FAIL(x) {printf(x); terminate(0);}
|
||||
|
||||
int dma_test_mem_transfer(void);
|
||||
void dma_test_led_flash(int pin);
|
||||
void dma_test_pwm_trigger(int pin);
|
||||
void terminate(int sig);
|
||||
void gpio_mode(int pin, int mode);
|
||||
void gpio_out(int pin, int val);
|
||||
uint8_t gpio_in(int pin);
|
||||
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(void);
|
||||
void start_dma(DMA_CB *cbp);
|
||||
void stop_dma(void);
|
||||
void disp_dma(void);
|
||||
void init_pwm(int freq);
|
||||
void start_pwm(void);
|
||||
void stop_pwm(void);
|
||||
|
||||
// Main program
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Ensure cleanup if user hits ctrl-C
|
||||
signal(SIGINT, terminate);
|
||||
|
||||
// Map GPIO, DMA and PWM registers into virtual mem (user space)
|
||||
virt_gpio_regs = map_segment((void *)GPIO_BASE, PAGE_SIZE);
|
||||
virt_dma_regs = map_segment((void *)DMA_BASE, PAGE_SIZE);
|
||||
virt_pwm_regs = map_segment((void *)PWM_BASE, PAGE_SIZE);
|
||||
virt_clk_regs = map_segment((void *)CLK_BASE, PAGE_SIZE);
|
||||
enable_dma();
|
||||
|
||||
// Set LED pin as output, and set high
|
||||
gpio_mode(LED_PIN, GPIO_OUT);
|
||||
gpio_out(LED_PIN, 1);
|
||||
|
||||
// Use mailbox to get uncached memory for DMA decriptors and buffers
|
||||
mbox_fd = open_mbox();
|
||||
if ((dma_mem_h = alloc_vc_mem(mbox_fd, DMA_MEM_SIZE, DMA_MEM_FLAGS)) <= 0 ||
|
||||
(bus_dma_mem = lock_vc_mem(mbox_fd, dma_mem_h)) == 0 ||
|
||||
(virt_dma_mem = map_segment(BUS_PHYS_ADDR(bus_dma_mem), DMA_MEM_SIZE)) == 0)
|
||||
FAIL("Error: can't allocate uncached memory\n");
|
||||
printf("VC mem handle %u, phys %p, virt %p\n", dma_mem_h, bus_dma_mem, virt_dma_mem);
|
||||
|
||||
// Run DMA tests
|
||||
dma_test_mem_transfer();
|
||||
dma_test_led_flash(LED_PIN);
|
||||
dma_test_pwm_trigger(LED_PIN);
|
||||
terminate(0);
|
||||
}
|
||||
|
||||
// DMA memory-to-memory test
|
||||
int dma_test_mem_transfer(void)
|
||||
{
|
||||
DMA_CB *cbp = virt_dma_mem;
|
||||
char *srce = (char *)(cbp+1);
|
||||
char *dest = srce + 0x100;
|
||||
|
||||
strcpy(srce, "memory transfer OK");
|
||||
memset(cbp, 0, sizeof(DMA_CB));
|
||||
cbp->ti = DMA_CB_SRC_INC | DMA_CB_DEST_INC;
|
||||
cbp->srce_ad = BUS_DMA_MEM(srce);
|
||||
cbp->dest_ad = BUS_DMA_MEM(dest);
|
||||
cbp->tfr_len = strlen(srce) + 1;
|
||||
start_dma(cbp);
|
||||
usleep(10);
|
||||
#if DEBUG
|
||||
disp_dma();
|
||||
#endif
|
||||
printf("DMA test: %s\n", dest[0] ? dest : "failed");
|
||||
return(dest[0] != 0);
|
||||
}
|
||||
|
||||
// DMA memory-to-GPIO test: flash LED
|
||||
void dma_test_led_flash(int pin)
|
||||
{
|
||||
DMA_CB *cbp=virt_dma_mem;
|
||||
uint32_t *data = (uint32_t *)(cbp+1), n;
|
||||
|
||||
printf("DMA test: flashing LED on GPIO pin %u\n", pin);
|
||||
memset(cbp, 0, sizeof(DMA_CB));
|
||||
*data = 1 << pin;
|
||||
cbp->tfr_len = 4;
|
||||
cbp->srce_ad = BUS_DMA_MEM(data);
|
||||
for (n=0; n<16; n++)
|
||||
{
|
||||
usleep(200000);
|
||||
cbp->dest_ad = BUS_GPIO_REG(n&1 ? GPIO_CLR0 : GPIO_SET0);
|
||||
start_dma(cbp);
|
||||
}
|
||||
}
|
||||
|
||||
// DMA trigger test: fLash LED using PWM trigger
|
||||
void dma_test_pwm_trigger(int pin)
|
||||
{
|
||||
DMA_CB *cbs=virt_dma_mem;
|
||||
uint32_t n, *pindata=(uint32_t *)(cbs+4), *pwmdata=pindata+1;
|
||||
|
||||
printf("DMA test: PWM trigger, ctrl-C to exit\n");
|
||||
memset(cbs, 0, sizeof(DMA_CB)*4);
|
||||
// Transfers are triggered by PWM request
|
||||
cbs[0].ti = cbs[1].ti = cbs[2].ti = cbs[3].ti = (1 << 6) | (DMA_PWM_DREQ << 16);
|
||||
// Control block 0 and 2: clear & set LED pin, 4-byte transfer
|
||||
cbs[0].srce_ad = cbs[2].srce_ad = BUS_DMA_MEM(pindata);
|
||||
cbs[0].dest_ad = BUS_GPIO_REG(GPIO_CLR0);
|
||||
cbs[2].dest_ad = BUS_GPIO_REG(GPIO_SET0);
|
||||
cbs[0].tfr_len = cbs[2].tfr_len = 4;
|
||||
*pindata = 1 << pin;
|
||||
// Control block 1 and 3: update PWM FIFO (to clear DMA request)
|
||||
cbs[1].srce_ad = cbs[3].srce_ad = BUS_DMA_MEM(pwmdata);
|
||||
cbs[1].dest_ad = cbs[3].dest_ad = BUS_PWM_REG(PWM_FIF1);
|
||||
cbs[1].tfr_len = cbs[3].tfr_len = 4;
|
||||
*pwmdata = PWM_RANGE / 2;
|
||||
// Link control blocks 0 to 3 in endless loop
|
||||
for (n=0; n<4; n++)
|
||||
cbs[n].next_cb = BUS_DMA_MEM(&cbs[(n+1)%4]);
|
||||
// Enable PWM with data threshold 1, and DMA
|
||||
init_pwm(PWM_FREQ);
|
||||
*VIRT_PWM_REG(PWM_DMAC) = PWM_DMAC_ENAB|1;
|
||||
start_pwm();
|
||||
start_dma(&cbs[0]);
|
||||
// Nothing to do while LED is flashing
|
||||
sleep(4);
|
||||
}
|
||||
|
||||
// Free memory segments and exit
|
||||
void terminate(int sig)
|
||||
{
|
||||
printf("Closing\n");
|
||||
stop_pwm();
|
||||
stop_dma();
|
||||
unmap_segment(virt_dma_mem, DMA_MEM_SIZE);
|
||||
unlock_vc_mem(mbox_fd, dma_mem_h);
|
||||
free_vc_mem(mbox_fd, dma_mem_h);
|
||||
close_mbox(mbox_fd);
|
||||
unmap_segment(virt_clk_regs, PAGE_SIZE);
|
||||
unmap_segment(virt_pwm_regs, PAGE_SIZE);
|
||||
unmap_segment(virt_dma_regs, PAGE_SIZE);
|
||||
unmap_segment(virt_gpio_regs, PAGE_SIZE);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// ----- GPIO -----
|
||||
|
||||
// Set input or output
|
||||
void gpio_mode(int pin, int mode)
|
||||
{
|
||||
uint32_t *reg = VIRT_GPIO_REG(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)
|
||||
{
|
||||
uint32_t *reg = VIRT_GPIO_REG(val ? GPIO_SET0 : GPIO_CLR0) + pin/32;
|
||||
*reg = 1 << (pin % 32);
|
||||
}
|
||||
|
||||
// Get an I/P pin value
|
||||
uint8_t gpio_in(int pin)
|
||||
{
|
||||
uint32_t *reg = VIRT_GPIO_REG(GPIO_LEV0) + pin/32;
|
||||
return (((*reg) >> (pin % 32)) & 1);
|
||||
}
|
||||
|
||||
// ----- 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(void)
|
||||
{
|
||||
*VIRT_DMA_REG(DMA_ENABLE) |= (1 << DMA_CHAN);
|
||||
*VIRT_DMA_REG(DMA_CS) = 1 << 31;
|
||||
}
|
||||
|
||||
// Start DMA, given first control block
|
||||
void start_dma(DMA_CB *cbp)
|
||||
{
|
||||
*VIRT_DMA_REG(DMA_CONBLK_AD) = BUS_DMA_MEM(cbp);
|
||||
*VIRT_DMA_REG(DMA_CS) = 2; // Clear 'end' flag
|
||||
*VIRT_DMA_REG(DMA_DEBUG) = 7; // Clear error bits
|
||||
*VIRT_DMA_REG(DMA_CS) = 1; // Start DMA
|
||||
}
|
||||
|
||||
// Halt current DMA operation by resetting controller
|
||||
void stop_dma(void)
|
||||
{
|
||||
if (virt_dma_regs)
|
||||
*VIRT_DMA_REG(DMA_CS) = 1 << 31;
|
||||
}
|
||||
|
||||
// Display DMA registers
|
||||
void disp_dma(void)
|
||||
{
|
||||
uint32_t *p=(uint32_t *)VIRT_DMA_REG(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)
|
||||
{
|
||||
stop_pwm();
|
||||
if (*VIRT_PWM_REG(PWM_STA) & 0x100)
|
||||
{
|
||||
printf("PWM bus error\n");
|
||||
*VIRT_PWM_REG(PWM_STA) = 0x100;
|
||||
}
|
||||
#if USE_VC_CLOCK_SET
|
||||
set_vc_clock(mbox_fd, PWM_CLOCK_ID, freq);
|
||||
#else
|
||||
int divi=(CLOCK_KHZ*1000) / freq;
|
||||
*VIRT_CLK_REG(CLK_PWM_CTL) = CLK_PASSWD | (1 << 5);
|
||||
while (*VIRT_CLK_REG(CLK_PWM_CTL) & (1 << 7)) ;
|
||||
*VIRT_CLK_REG(CLK_PWM_DIV) = CLK_PASSWD | (divi << 12);
|
||||
*VIRT_CLK_REG(CLK_PWM_CTL) = CLK_PASSWD | 6 | (1 << 4);
|
||||
while ((*VIRT_CLK_REG(CLK_PWM_CTL) & (1 << 7)) == 0) ;
|
||||
#endif
|
||||
usleep(100);
|
||||
*VIRT_PWM_REG(PWM_RNG1) = PWM_RANGE;
|
||||
*VIRT_PWM_REG(PWM_FIF1) = PWM_RANGE/2;
|
||||
#if PWM_OUT
|
||||
gpio_mode(PWM_PIN, GPIO_ALT5);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Start PWM operation
|
||||
void start_pwm(void)
|
||||
{
|
||||
*VIRT_PWM_REG(PWM_CTL) = PWM_CTL_USEF1 | PWM_ENAB;
|
||||
}
|
||||
|
||||
// Stop PWM operation
|
||||
void stop_pwm(void)
|
||||
{
|
||||
if (virt_pwm_regs)
|
||||
{
|
||||
*VIRT_PWM_REG(PWM_CTL) = 0;
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,346 +0,0 @@
|
|||
// 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
|
|
@ -1,202 +0,0 @@
|
|||
// 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 1
|
||||
|
||||
// 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 uncached 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
|
|
@ -1,388 +0,0 @@
|
|||
// 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
|
|
@ -1,386 +0,0 @@
|
|||
// Test of parallel AD9226 ADC using Raspberry Pi SMI (Secondary Memory Interface)
|
||||
// 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.06 JPB 16/7/20 Tidied up for Github
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "rpi_dma_utils.h"
|
||||
#include "rpi_smi_defs.h"
|
||||
|
||||
// Set zero for single value, non-zero for block read
|
||||
#define USE_DMA 1
|
||||
// Use test pin in place of GPIO mode setting (to check timing)
|
||||
#define USE_TEST_PIN 0
|
||||
|
||||
// SMI cycle timings
|
||||
#define SMI_NUM_BITS SMI_16_BITS
|
||||
#define SMI_TIMING SMI_TIMING_20M
|
||||
|
||||
#if PHYS_REG_BASE==PI_4_REG_BASE // Timings for RPi v4 (1.5 GHz)
|
||||
#define SMI_TIMING_1M 10, 38, 74, 38 // 1 MS/s
|
||||
#define SMI_TIMING_10M 6, 6, 13, 6 // 10 MS/s
|
||||
#define SMI_TIMING_20M 4, 5, 9, 5 // 19.74 MS/s
|
||||
#define SMI_TIMING_25M 4, 3, 8, 4 // 25 MS/s
|
||||
#define SMI_TIMING_31M 4, 3, 6, 3 // 31.25 MS/s
|
||||
#else // Timings for RPi v0-3 (1 GHz)
|
||||
#define SMI_TIMING_1M 10, 25, 50, 25 // 1 MS/s
|
||||
#define SMI_TIMING_10M 4, 6, 13, 6 // 10 MS/s
|
||||
#define SMI_TIMING_20M 2, 6, 13, 6 // 20 MS/s
|
||||
#define SMI_TIMING_25M 2, 5, 10, 5 // 25 MS/s
|
||||
#define SMI_TIMING_31M 2, 4, 6, 4 // 31.25 MS/s
|
||||
#define SMI_TIMING_42M 2, 3, 6, 3 // 41.66 MS/s
|
||||
#define SMI_TIMING_50M 2, 3, 5, 2 // 50 MS/s
|
||||
#endif
|
||||
|
||||
// Number of raw bytes per ADC sample
|
||||
#define SAMPLE_SIZE 2
|
||||
|
||||
// Number of samples to be captured, and number to be discarded
|
||||
#define NSAMPLES 500
|
||||
#define PRE_SAMP 24
|
||||
|
||||
// Voltage calibration
|
||||
#define ADC_ZERO 2080
|
||||
#define ADC_SCALE 410.0
|
||||
|
||||
// GPIO pin numbers
|
||||
#define ADC_D0_PIN 12
|
||||
#define ADC_NPINS 12
|
||||
#define SMI_SOE_PIN 6
|
||||
#define SMI_SWE_PIN 7
|
||||
#define SMI_DREQ_PIN 24
|
||||
#define TEST_PIN 25
|
||||
|
||||
// DMA request threshold
|
||||
#define REQUEST_THRESH 4
|
||||
|
||||
// SMI register names for diagnostic print
|
||||
char *smi_regstrs[] = {
|
||||
"CS","LEN","A","D","DSR0","DSW0","DSR1","DSW1",
|
||||
"DSR2","DSW2","DSR3","DSW3","DMC","DCS","DCA","DCD",""
|
||||
};
|
||||
|
||||
// SMI CS register field names for diagnostic print
|
||||
#define STRS(x) STRS_(x) ","
|
||||
#define STRS_(...) #__VA_ARGS__
|
||||
char *smi_cs_regstrs = STRS(SMI_CS_FIELDS);
|
||||
|
||||
// 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;
|
||||
|
||||
// Buffer for captured samples
|
||||
uint16_t sample_data[NSAMPLES];
|
||||
|
||||
// Non-volatile memory size
|
||||
#define VC_MEM_SIZE(nsamp) (PAGE_SIZE + ((nsamp)+4)*SAMPLE_SIZE)
|
||||
|
||||
void map_devices(void);
|
||||
void fail(char *s);
|
||||
void terminate(int sig);
|
||||
void smi_start(int nsamples, int packed);
|
||||
uint32_t *adc_dma_start(MEM_MAP *mp, int nsamp);
|
||||
int adc_dma_end(void *buff, uint16_t *data, int nsamp);
|
||||
void init_smi(int width, int ns, int setup, int hold, int strobe);
|
||||
void disp_smi(void);
|
||||
void mode_word(uint32_t *wp, int n, uint32_t mode);
|
||||
float val_volts(int val);
|
||||
int adc_gpio_val(void);
|
||||
void disp_reg_fields(char *regstrs, char *name, uint32_t val);
|
||||
void dma_wait(int chan);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
void *rxbuff;
|
||||
int i;
|
||||
|
||||
signal(SIGINT, terminate);
|
||||
map_devices();
|
||||
for (i=0; i<ADC_NPINS; i++)
|
||||
gpio_mode(ADC_D0_PIN+i, GPIO_IN);
|
||||
gpio_mode(SMI_SOE_PIN, GPIO_ALT1);
|
||||
#if !USE_DMA
|
||||
init_smi(SMI_NUM_BITS, SMI_TIMING_1M);
|
||||
while (1)
|
||||
{
|
||||
smi_start(PRE_SAMP, 1);
|
||||
usleep(20);
|
||||
int val = adc_gpio_val();
|
||||
printf("%4u %1.3f\n", val, val_volts(val));
|
||||
sleep(1);
|
||||
}
|
||||
#else
|
||||
init_smi(SMI_NUM_BITS, SMI_TIMING);
|
||||
#if USE_TEST_PIN
|
||||
gpio_mode(TEST_PIN, GPIO_OUT);
|
||||
gpio_out(TEST_PIN, 0);
|
||||
#endif
|
||||
map_uncached_mem(&vc_mem, VC_MEM_SIZE(NSAMPLES+PRE_SAMP));
|
||||
smi_dmc->dmaen = 1;
|
||||
smi_cs->enable = 1;
|
||||
smi_cs->clear = 1;
|
||||
rxbuff = adc_dma_start(&vc_mem, NSAMPLES);
|
||||
smi_start(NSAMPLES, 1);
|
||||
while (dma_active(DMA_CHAN_A)) ;
|
||||
adc_dma_end(rxbuff, sample_data, NSAMPLES);
|
||||
disp_reg_fields(smi_cs_regstrs, "CS", *REG32(smi_regs, SMI_CS));
|
||||
smi_cs->enable = smi_dcs->enable = 0;
|
||||
for (i=0; i<NSAMPLES; i++)
|
||||
printf("%1.3f\n", val_volts(sample_data[i]));
|
||||
#endif
|
||||
terminate(0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
// 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<ADC_NPINS; i++)
|
||||
gpio_mode(ADC_D0_PIN+i, GPIO_IN);
|
||||
}
|
||||
if (smi_regs.virt)
|
||||
*REG32(smi_regs, SMI_CS) = 0;
|
||||
stop_dma(DMA_CHAN_A);
|
||||
unmap_periph_mem(&vc_mem);
|
||||
unmap_periph_mem(&smi_regs);
|
||||
unmap_periph_mem(&dma_regs);
|
||||
unmap_periph_mem(&gpio_regs);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Start SMI, given number of samples, optionally pack bytes into words
|
||||
void smi_start(int nsamples, int packed)
|
||||
{
|
||||
smi_l->len = nsamples + PRE_SAMP;
|
||||
smi_cs->pxldat = (packed != 0);
|
||||
smi_cs->enable = 1;
|
||||
smi_cs->clear = 1;
|
||||
smi_cs->start = 1;
|
||||
}
|
||||
|
||||
// Start DMA for SMI ADC, return Rx data buffer
|
||||
uint32_t *adc_dma_start(MEM_MAP *mp, int nsamp)
|
||||
{
|
||||
DMA_CB *cbs=mp->virt;
|
||||
uint32_t *data=(uint32_t *)(cbs+4), *pindata=data+8, *modes=data+0x10;
|
||||
uint32_t *modep1=data+0x18, *modep2=modep1+1, *rxdata=data+0x20, i;
|
||||
|
||||
// Get current mode register values
|
||||
for (i=0; i<3; i++)
|
||||
modes[i] = modes[i+3] = *REG32(gpio_regs, GPIO_MODE0 + i*4);
|
||||
|
||||
// Get mode values with ADC pins set to SMI
|
||||
for (i=ADC_D0_PIN; i<ADC_D0_PIN+ADC_NPINS; i++)
|
||||
mode_word(&modes[i/10], i%10, GPIO_ALT1);
|
||||
|
||||
// Copy mode values into 32-bit words
|
||||
*modep1 = modes[1];
|
||||
*modep2 = modes[2];
|
||||
*pindata = 1 << TEST_PIN;
|
||||
enable_dma(DMA_CHAN_A);
|
||||
// Control blocks 0 and 1: enable SMI I/P pins
|
||||
cbs[0].ti = DMA_SRCE_DREQ | (DMA_SMI_DREQ << 16) | DMA_WAIT_RESP;
|
||||
#if USE_TEST_PIN
|
||||
cbs[0].tfr_len = 4;
|
||||
cbs[0].srce_ad = MEM_BUS_ADDR(mp, pindata);
|
||||
cbs[0].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_SET0);
|
||||
cbs[0].next_cb = MEM_BUS_ADDR(mp, &cbs[2]);
|
||||
#else
|
||||
cbs[0].tfr_len = 4;
|
||||
cbs[0].srce_ad = MEM_BUS_ADDR(mp, modep1);
|
||||
cbs[0].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0+4);
|
||||
cbs[0].next_cb = MEM_BUS_ADDR(mp, &cbs[1]);
|
||||
#endif
|
||||
cbs[1].tfr_len = 4;
|
||||
cbs[1].srce_ad = MEM_BUS_ADDR(mp, modep2);
|
||||
cbs[1].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0+8);
|
||||
cbs[1].next_cb = MEM_BUS_ADDR(mp, &cbs[2]);
|
||||
// Control block 2: read data
|
||||
cbs[2].ti = DMA_SRCE_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_DEST_INC;
|
||||
cbs[2].tfr_len = (nsamp + PRE_SAMP) * SAMPLE_SIZE;
|
||||
cbs[2].srce_ad = REG_BUS_ADDR(smi_regs, SMI_D);
|
||||
cbs[2].dest_ad = MEM_BUS_ADDR(mp, rxdata);
|
||||
cbs[2].next_cb = MEM_BUS_ADDR(mp, &cbs[3]);
|
||||
// Control block 3: disable SMI I/P pins
|
||||
cbs[3].ti = DMA_CB_SRCE_INC | DMA_CB_DEST_INC;
|
||||
#if USE_TEST_PIN
|
||||
cbs[3].tfr_len = 4;
|
||||
cbs[3].srce_ad = MEM_BUS_ADDR(mp, pindata);
|
||||
cbs[3].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_CLR0);
|
||||
#else
|
||||
cbs[3].tfr_len = 3 * 4;
|
||||
cbs[3].srce_ad = MEM_BUS_ADDR(mp, &modes[3]);
|
||||
cbs[3].dest_ad = REG_BUS_ADDR(gpio_regs, GPIO_MODE0);
|
||||
#endif
|
||||
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
|
||||
return(rxdata);
|
||||
}
|
||||
|
||||
// ADC DMA is complete, get data
|
||||
int adc_dma_end(void *buff, uint16_t *data, int nsamp)
|
||||
{
|
||||
uint16_t *bp = (uint16_t *)buff;
|
||||
int i;
|
||||
|
||||
for (i=0; i<nsamp+PRE_SAMP; i++)
|
||||
{
|
||||
if (i >= PRE_SAMP)
|
||||
*data++ = bp[i] >> 4;
|
||||
}
|
||||
return(nsamp);
|
||||
}
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
|
||||
// Display SMI registers
|
||||
void disp_smi(void)
|
||||
{
|
||||
volatile uint32_t *p=REG32(smi_regs, SMI_CS);
|
||||
int i=0;
|
||||
|
||||
while (smi_regstrs[i][0])
|
||||
{
|
||||
printf("%4s=%08X ", smi_regstrs[i++], *p++);
|
||||
if (i%8==0 || smi_regstrs[i][0]==0)
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Get GPIO mode value into 32-bit word
|
||||
void mode_word(uint32_t *wp, int n, uint32_t mode)
|
||||
{
|
||||
uint32_t mask = 7 << (n * 3);
|
||||
|
||||
*wp = (*wp & ~mask) | (mode << (n * 3));
|
||||
}
|
||||
|
||||
// Convert ADC value to voltage
|
||||
float val_volts(int val)
|
||||
{
|
||||
return((ADC_ZERO - val) / ADC_SCALE);
|
||||
}
|
||||
|
||||
// Return ADC value, using GPIO inputs
|
||||
int adc_gpio_val(void)
|
||||
{
|
||||
int v = *REG32(gpio_regs, GPIO_LEV0);
|
||||
|
||||
return((v>>ADC_D0_PIN) & ((1 << ADC_NPINS)-1));
|
||||
}
|
||||
|
||||
// Display bit values in register
|
||||
void disp_reg_fields(char *regstrs, char *name, uint32_t val)
|
||||
{
|
||||
char *p=regstrs, *q, *r=regstrs;
|
||||
uint32_t nbits, v;
|
||||
|
||||
printf("%s %08X", name, val);
|
||||
while ((q = strchr(p, ':')) != 0)
|
||||
{
|
||||
p = q + 1;
|
||||
nbits = 0;
|
||||
while (*p>='0' && *p<='9')
|
||||
nbits = nbits * 10 + *p++ - '0';
|
||||
v = val & ((1 << nbits) - 1);
|
||||
val >>= nbits;
|
||||
if (v && *r!='_')
|
||||
printf(" %.*s=%X", q-r, r, v);
|
||||
while (*p==',' || *p==' ')
|
||||
p = r = p + 1;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,359 +0,0 @@
|
|||
// Test of an R-2R DAC on the Raspberry Pi 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 Derived from rpi_smi_test v0.20
|
||||
// Tidied up for github
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "rpi_dma_utils.h"
|
||||
|
||||
#define USE_SMI 1
|
||||
#define USE_DMA 1
|
||||
#define DISP_ZEROS 0
|
||||
|
||||
#define SMI_A0_PIN 5
|
||||
#define SMI_SOE_PIN 6
|
||||
#define SMI_SWE_PIN 7
|
||||
|
||||
#define DAC_D0_PIN 8
|
||||
#define DAC_NPINS 8
|
||||
|
||||
#define NSAMPLES 512
|
||||
|
||||
#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)
|
||||
|
||||
#define SMI_DEV 0
|
||||
#define SMI_8_BITS 0
|
||||
#define SMI_16_BITS 1
|
||||
#define SMI_18_BITS 2
|
||||
#define SMI_9_BITS 3
|
||||
|
||||
#define DMA_SMI_DREQ 4
|
||||
|
||||
char *smi_regstrs[] = {
|
||||
"CS","LEN","A","D","DSR0","DSW0","DSR1","DSW1",
|
||||
"DSR2","DSW2","DSR3","DSW3","DMC","DCS","DCA","DCD",""
|
||||
};
|
||||
|
||||
#define STRS(x) STRS_(x) ","
|
||||
#define STRS_(...) #__VA_ARGS__
|
||||
#define REG_DEF(name, fields) typedef union {struct {volatile uint32_t fields;}; volatile uint32_t value;} name
|
||||
|
||||
#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);
|
||||
#define SMI_L_FIELDS \
|
||||
len:32
|
||||
REG_DEF(SMI_L_REG, SMI_L_FIELDS);
|
||||
#define SMI_A_FIELDS \
|
||||
addr:6, _x1:2, dev:2
|
||||
REG_DEF(SMI_A_REG, SMI_A_FIELDS);
|
||||
#define SMI_D_FIELDS \
|
||||
data:32
|
||||
REG_DEF(SMI_D_REG, SMI_D_FIELDS);
|
||||
#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);
|
||||
#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);
|
||||
#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);
|
||||
#define SMI_DCS_FIELDS \
|
||||
enable:1, start:1, done:1, write:1
|
||||
REG_DEF(SMI_DCS_REG, SMI_DCS_FIELDS);
|
||||
#define SMI_DCA_FIELDS \
|
||||
addr:6, _x1:2, dev:2
|
||||
REG_DEF(SMI_DCA_REG, SMI_DCA_FIELDS);
|
||||
#define SMI_DCD_FIELDS \
|
||||
data:32
|
||||
REG_DEF(SMI_DCD_REG, SMI_DCD_FIELDS);
|
||||
#define SMI_FLVL_FIELDS \
|
||||
fcnt:6, _x1:2, flvl:6
|
||||
REG_DEF(SMI_FLVL_REG, SMI_FLVL_FIELDS);
|
||||
|
||||
char *smi_cs_regstrs = STRS(SMI_CS_FIELDS);
|
||||
|
||||
#define CLK_SMI_CTL 0xb0
|
||||
#define CLK_SMI_DIV 0xb4
|
||||
|
||||
extern MEM_MAP gpio_regs, dma_regs;
|
||||
MEM_MAP vc_mem, clk_regs, smi_regs;
|
||||
|
||||
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;
|
||||
|
||||
#define TX_SAMPLE_SIZE 1 // Number of raw bytes per sample
|
||||
#define VC_MEM_SIZE(ns) (PAGE_SIZE + ((ns)+4)*TX_SAMPLE_SIZE)
|
||||
|
||||
uint8_t sample_buff[NSAMPLES];
|
||||
|
||||
void dac_ladder_init(void);
|
||||
void dac_ladder_write(int val);
|
||||
void dac_ladder_dma(MEM_MAP *mp, uint8_t *data, int len, int repeat);
|
||||
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 disp_smi(void);
|
||||
void disp_reg_fields(char *regstrs, char *name, uint32_t val);
|
||||
void dma_wait(int chan);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, sample_count=NSAMPLES;
|
||||
|
||||
signal(SIGINT, terminate);
|
||||
map_devices();
|
||||
init_smi(0, 1000, 25, 50, 25);
|
||||
gpio_mode(SMI_SOE_PIN, GPIO_ALT1);
|
||||
gpio_mode(SMI_SWE_PIN, GPIO_ALT1);
|
||||
smi_cs->clear = 1;
|
||||
dac_ladder_init();
|
||||
for (i=0; i<sample_count; i++)
|
||||
sample_buff[i] = (uint8_t)i;
|
||||
#if !USE_DMA
|
||||
while (1)
|
||||
{
|
||||
i = (i + 1) % sample_count;
|
||||
dac_ladder_write(sample_buff[i]);
|
||||
usleep(10);
|
||||
}
|
||||
#else
|
||||
for (i=0; i<DAC_NPINS; i++)
|
||||
gpio_mode(DAC_D0_PIN+i, GPIO_ALT1);
|
||||
map_uncached_mem(&vc_mem, VC_MEM_SIZE(sample_count));
|
||||
smi_dsr->rwidth = SMI_8_BITS;
|
||||
smi_l->len = sample_count;
|
||||
smi_dmc->dmaen = 1;
|
||||
smi_cs->write = 1;
|
||||
smi_cs->enable = 1;
|
||||
smi_cs->clear = 1;
|
||||
dac_ladder_dma(&vc_mem, sample_buff, sample_count, 0);
|
||||
smi_cs->start = 1;
|
||||
dma_wait(DMA_CHAN_A);
|
||||
#endif
|
||||
terminate(0);
|
||||
return(0);
|
||||
}
|
||||
|
||||
// Initialise resistor DAC
|
||||
void dac_ladder_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
#if USE_SMI
|
||||
smi_cs->clear = smi_cs->seterr = smi_cs->aferr=1;
|
||||
//smi_cs->enable = 1;
|
||||
smi_dcs->enable = 1;
|
||||
#endif
|
||||
for (i=0; i<DAC_NPINS; i++)
|
||||
gpio_mode(DAC_D0_PIN+i, USE_SMI ? GPIO_ALT1 : GPIO_OUT);
|
||||
}
|
||||
|
||||
// Output value to resistor DAC
|
||||
void dac_ladder_write(int val)
|
||||
{
|
||||
#if USE_SMI
|
||||
smi_dcs->done = 1;
|
||||
smi_dcs->write = 1;
|
||||
smi_dcd->value = val & 0xff;
|
||||
smi_dcs->start = 1;
|
||||
#else
|
||||
*REG32(gpio_regs, GPIO_SET0) = (val & 0xff) << DAC_D0_PIN;
|
||||
*REG32(gpio_regs, GPIO_CLR0) = (~val & 0xff) << DAC_D0_PIN;
|
||||
#endif
|
||||
}
|
||||
|
||||
// DMA values to resistor DAC
|
||||
void dac_ladder_dma(MEM_MAP *mp, uint8_t *data, int len, int repeat)
|
||||
{
|
||||
DMA_CB *cbs=mp->virt;
|
||||
uint8_t *txdata=(uint8_t *)(cbs+1);
|
||||
|
||||
memcpy(txdata, data, len);
|
||||
enable_dma(DMA_CHAN_A);
|
||||
cbs[0].ti = DMA_DEST_DREQ | (DMA_SMI_DREQ << 16) | DMA_CB_SRCE_INC;
|
||||
cbs[0].tfr_len = NSAMPLES;
|
||||
cbs[0].srce_ad = MEM_BUS_ADDR(mp, txdata);
|
||||
cbs[0].dest_ad = REG_BUS_ADDR(smi_regs, SMI_D);
|
||||
cbs[0].next_cb = repeat ? MEM_BUS_ADDR(mp, &cbs[0]) : 0;
|
||||
start_dma(mp, DMA_CHAN_A, &cbs[0], 0);
|
||||
}
|
||||
|
||||
// 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);
|
||||
memset(smi_regs.virt, 0, SMI_REGLEN);
|
||||
}
|
||||
|
||||
// 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");
|
||||
disp_reg_fields(smi_cs_regstrs, "CS", *REG32(smi_regs, SMI_CS));
|
||||
for (i=0; i<DAC_NPINS; i++)
|
||||
gpio_mode(DAC_D0_PIN+i, GPIO_IN);
|
||||
if (smi_regs.virt)
|
||||
*REG32(smi_regs, SMI_CS) = 0;
|
||||
stop_dma(DMA_CHAN_A);
|
||||
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 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 = 2;
|
||||
smi_dsr->rwidth = smi_dsw->wwidth = width;
|
||||
}
|
||||
|
||||
// Display SMI registers
|
||||
void disp_smi(void)
|
||||
{
|
||||
volatile uint32_t *p=REG32(smi_regs, SMI_CS);
|
||||
int i=0;
|
||||
|
||||
while (smi_regstrs[i][0])
|
||||
{
|
||||
printf("%4s=%08X ", smi_regstrs[i++], *p++);
|
||||
if (i%8==0 || smi_regstrs[i][0]==0)
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Display bit values in register
|
||||
void disp_reg_fields(char *regstrs, char *name, uint32_t val)
|
||||
{
|
||||
char *p=regstrs, *q, *r=regstrs;
|
||||
uint32_t nbits, v;
|
||||
|
||||
printf("%s %08X", name, val);
|
||||
while ((q = strchr(p, ':')) != 0)
|
||||
{
|
||||
p = q + 1;
|
||||
nbits = 0;
|
||||
while (*p>='0' && *p<='9')
|
||||
nbits = nbits * 10 + *p++ - '0';
|
||||
v = val & ((1 << nbits) - 1);
|
||||
val >>= nbits;
|
||||
if ((v || DISP_ZEROS) && *r!='_')
|
||||
printf(" %.*s=%X", q-r, r, v);
|
||||
while (*p==',' || *p==' ')
|
||||
p = r = p + 1;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Wait until DMA is complete
|
||||
void dma_wait(int chan)
|
||||
{
|
||||
int n = 1000;
|
||||
|
||||
do {
|
||||
usleep(100);
|
||||
} while (dma_transfer_len(chan) && --n);
|
||||
if (n == 0)
|
||||
printf("DMA transfer timeout\n");
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,100 +0,0 @@
|
|||
// 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
|
|
@ -1,4 +1,6 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Main"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE_CONFIG"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Events"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Radios"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Setup"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "IO_UTILS_Main"
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "IO_UTILS_SPI"
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "IO_UTILS_SysInfo"
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "ICE40"
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "RFFC5072"
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ Cariboulite::Cariboulite(const SoapySDR::Kwargs &args)
|
|||
sample_queues[i]->AttachStreamId(stream_id, dir, ch);
|
||||
}
|
||||
|
||||
|
||||
cariboulite_init_radios(&radios, &sess.cariboulite_sys);
|
||||
|
||||
// TODO: Exception when error
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#define ZF_LOG_LEVEL ZF_LOG_ERROR
|
||||
|
||||
#include "datatypes/tsqueue.h"
|
||||
#include "cariboulite_setup.h"
|
||||
#include "cariboulite_radios.h"
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
# Raspberry Pi SDR Driver
|
||||
TODO
|
||||
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
|
|
@ -0,0 +1,58 @@
|
|||
# Utilities
|
||||
|
||||
## `generate_bin_blob.c`
|
||||
converts a binary file into C/C++ h-file code with versioning and dating. We use this converter to embed the device-tree overlays binaries and FPGA firmware binaries directly into the library code.
|
||||
In such way, the lib is self contained and doesn't have floating around dependencies.
|
||||
|
||||
Usage:
|
||||
```
|
||||
$ ./generate_bin_blob input_file.bin variable_name output_file.h
|
||||
```
|
||||
|
||||
**The output file will have the following structure:**
|
||||
|
||||
```
|
||||
#ifndef __cariboulite_dtbo_h__
|
||||
#define __cariboulite_dtbo_h__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* Time tagging of the module through the 'struct tm' structure
|
||||
* Date: 2021-09-17
|
||||
* Time: 09:56:44
|
||||
*/
|
||||
struct tm cariboulite_dtbo_date_time = {
|
||||
.tm_sec = 44,
|
||||
.tm_min = 56,
|
||||
.tm_hour = 9,
|
||||
.tm_mday = 17,
|
||||
.tm_mon = 8, /* +1 */
|
||||
.tm_year = 121, /* +1900 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data blob of variable cariboulite_dtbo:
|
||||
* Size: 1112 bytes
|
||||
* Original filename: ./cariboulite.dtbo
|
||||
*/
|
||||
uint8_t cariboulite_dtbo[] = {
|
||||
0xD0, 0x0D, 0xFE, 0xED, 0x00, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x03, 0xDC,
|
||||
0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
...
|
||||
0x6D, 0x69, 0x00, 0x67, 0x70, 0x69, 0x6F, 0x00,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __cariboulite_dtbo_h__
|
||||
```
|
||||
|
Ładowanie…
Reference in New Issue