diff --git a/Makefile.am b/Makefile.am index ed33ffd..1f2b2a7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,6 +3,6 @@ ACLOCAL_AMFLAGS=-I m4 lib_LTLIBRARIES=libmipea.la libmipea_la_SOURCES=src/clock_manager.c src/dma.c src/gpio.c src/i2c.c src/mailbox_mod.c src/mipea.c src/peripherals.c src/pwm.c src/spi.c src/timer.c -libmipea_la_LDFLAGS=-version-info 2:0:0 +libmipea_la_LDFLAGS=-version-info 2:1:0 pkginclude_HEADERS=src/clock_manager.h src/dma.h src/gpio.h src/i2c.h src/mailbox_mod.h src/mipea.h src/peripherals.h src/pwm.h src/spi.h src/timer.h diff --git a/README.md b/README.md index 9159a5d..9ed5dbe 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,17 @@ +[![Documentation Status](https://readthedocs.org/projects/mipea/badge/?version=latest)](https://mipea.readthedocs.io/en/latest/?badge=latest) +![GitHub release](https://img.shields.io/github/release/jasLogic/mipea.svg) +![GitHub](https://img.shields.io/github/license/jasLogic/mipea.svg?color=informational) + # mipea ## minimalistic peripheral access for the Raspberry Pi -This library grants easy and fast access to peripherals. The GPIO, PWM and SPI are implemented at the moment. +This library grants easy and fast access to peripherals. Contributions are welcome, please fork and open a pull request. If you have successfully used mipea on a Raspberry Pi which is not tested yet, please inform me and I can add it to the list. -**Peripherals Implemented at the moment:** -* GPIO -* PWM - * Using the clock manager -* SPI -* I2C -* Timer -* DMA - * with a modified mailbox - ### Goal of this library The library is very useful for **high-speed and lighweight applications**, **efficiency** and **total control** on the **hardware level**. @@ -104,6 +98,11 @@ more information, such as the ld(1) and ld.so(8) manual pages. ---------------------------------------------------------------------- ``` +### Documentation + +Documentation is uploaded to [ReadTheDocs](mipea.readthedocs.io). +You can also build the documentation yourself using `sphinx`. + ### Usage To use a peripheral, for example GPIO, you need to include the corresponding header into your program. diff --git a/doc/clock_manager.rst b/doc/clock_manager.rst index ab29a6c..5cdddcd 100644 --- a/doc/clock_manager.rst +++ b/doc/clock_manager.rst @@ -10,22 +10,31 @@ Macros .. macro:: CLOCK_MANAGER_OFFSET + :: + + 0x101000 + This macro defines the offset at which the clock manager registers are located relative to the peripheral base. - It has the value :code:`0x101000` .. macro:: CLOCK_MANAGER_SIZE + :: + + 0xA4 + This macro holds the size of the clock manager registers which needs to be mapped. - It has the value :code:`0xA4` .. macro:: CM_PASSWD + :: + + 0x5A000000 + This macro holds the clock manager password. This value must always be present when writing to a clock manager register (e.g. by OR with the value). - It has the value :code:`0x5A000000` Registers ========= diff --git a/doc/gpio.rst b/doc/gpio.rst index 0c0bc4c..11fd170 100644 --- a/doc/gpio.rst +++ b/doc/gpio.rst @@ -10,13 +10,20 @@ Macros .. macro:: GPIO_OFFSET + :: + + 0x200000 + This macro defines the offset at which the GPIO registers are located from - the peripheral base. It has the value :code:`0x200000` + the peripheral base. .. macro:: GPIO_SIZE + :: + + 0xB0 + This macro holds the size of the GPIO registers which needs to be mapped. - It has the value :code:`0xB0` Registers ========= diff --git a/doc/i2c.rst b/doc/i2c.rst index b80af7a..43f2f78 100644 --- a/doc/i2c.rst +++ b/doc/i2c.rst @@ -29,8 +29,11 @@ Macros .. macro:: I2C_SIZE + :: + + 0x18 + This macro holds the size of the I2C registers which needs to be mapped. - It has the value :code:`0x18` :macro:`I2C_FIFO_SIZE` :macro:`I2C_C_I2CEN` @@ -74,41 +77,6 @@ Registers By using this macro, the registers of the I2C can be accessed like this :code:`I2C->FIFO`. -Structs -======= - -.. type:: i2c_config_t - - This struct is used to configure the I2C controller:: - - typedef struct { - uint8_t addr: 7; - uint16_t div; - uint16_t clkstr; - } i2c_config_t; - - .. member:: uint8_t addr: 7 - - This member holds the address of the I2C device that should be contacted. - I2C addresses have a length of seven bits. - - .. member:: uint16_t div - - This member sets the clock divider for the BSC controller. - - .. note:: The clock source is the core clock with a frequency, \ - according to the Datasheet_, of :code:`150 MHz` and \ - according to `this file`_ and other sources of :code:`250 MHz`. \ - When I tested the clock speed of I2C and SPI with a logic analyzer, \ - it seems that :code:`250 MHz` **is correct** \ - (at least for the Raspberry Pi Zero I use). - - .. member:: uint16_t clkstr - - This member sets the clock stretch timeout (or delay). This means that - the master will wait :code:`clkstr` cycles after the rising clock edge - for the slave to respond. After this the timeout flag is set. - Functions ========= @@ -122,10 +90,28 @@ Functions This function unmaps the I2C registers. -.. function:: void i2c_configure(i2c_config_t *config) +.. function:: void i2c_set_address(uint8_t addr) + + This function sets the address of the I2C device to communicate with. + The address is a seven bit value. + +.. function:: void i2c_set_clkdiv(uint16_t divisor) + + This function sets the clock divisor of the BSC controller. + + .. note:: The clock source is the core clock with a frequency, \ + according to the Datasheet_, of :code:`150 MHz` and \ + according to `this file`_ and other sources of :code:`250 MHz`. \ + When I tested the clock speed of I2C and SPI with a logic analyzer, \ + it seems that :code:`250 MHz` **is correct** \ + (at least for the Raspberry Pi Zero I use). + +.. function:: void i2c_set_clkstr(uint16_t clkstr) - This function configures the BSC controller with the :type:`i2c_config_t` - pointed to by :code:`config`. + This function sets the clock stretch timeout (or delay). This means that + the master will wait :code:`clkstr` cycles after the rising clock edge + for the slave to respond. After this the timeout flag is set. + This can often be left at reset value :code:`0x40`. .. function:: void i2c_start(void) diff --git a/doc/index.rst b/doc/index.rst index 09ab8c9..56f4073 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -17,6 +17,7 @@ mipea - minimalistic peripheral access library for the Raspberry Pi i2c.rst pwm.rst spi.rst + mipea.rst gpl.rst fdl.rst diff --git a/doc/mipea.rst b/doc/mipea.rst new file mode 100644 index 0000000..82999ed --- /dev/null +++ b/doc/mipea.rst @@ -0,0 +1,22 @@ +.. index:: + single: Mipea Wrapper + +************* +Mipea Wrapper +************* + +The mipea.c / h files are just a wrapper for all the other parts of the library. +If you are lazy (or need all peripherals mapped) than this wrapper is usefull. + +Functions +========= + +.. function:: int mipea_map(void) + + This function maps all the peripherals. + +.. function:: void mipea_unmap(void) + + This function unmaps all the peripherals. + +.. _Datasheet: https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf diff --git a/doc/peripherals.rst b/doc/peripherals.rst index 48ff9ac..03e50c4 100644 --- a/doc/peripherals.rst +++ b/doc/peripherals.rst @@ -13,13 +13,20 @@ Macros .. macro:: PERIPHERAL_BASE_BCM2835 + :: + + 0x20000000 + This macro holds the value of the peripheral base, when a BCM2835 is used. - It has the value :code:`0x20000000` .. macro:: PERIPHERAL_BASE_BCM2836_7 + :: + + 0x3F000000 + This macro holds the value of the peripheral base, when a BCM2836 or - BCM2837 is used. It has the value :code:`0x3F000000` + BCM2837 is used. Functions ========= @@ -29,12 +36,11 @@ Functions This function maps a code memory block of size :code:`size` at offset :code:`offset` from the peripheral base. -.. note:: The :code:`offset` must be a multiple of the page size which is \ - :code:`4096` on the Raspberry Pi. + .. note:: The :code:`offset` must be a multiple of the page size which is \ + :code:`4096` on the Raspberry Pi. - The function returns a pointer - to the mapped memory on success and :code:`NULL` on error. + The function returns a pointer to the mapped memory on success and :code:`NULL` on error. .. function:: void peripheral_unmap (void* map, uint32_t size) diff --git a/doc/pwm.rst b/doc/pwm.rst index 8e3119a..66ba9bc 100644 --- a/doc/pwm.rst +++ b/doc/pwm.rst @@ -13,13 +13,20 @@ Macros .. macro:: PWM_OFFSET + :: + + 0x20C000 + This macro defines the offset at which the PWM registers are located from - the peripheral base. It has the value :code:`0x20C000` + the peripheral base. .. macro:: PWM_SIZE + :: + + 0x24 + This macro holds the size of the I2C registers which needs to be mapped. - It has the value :code:`0x24` .. macro:: RNG_CHANNEL0 .. macro:: DAT_CHANNEL0 @@ -71,11 +78,11 @@ Registers uint32_t CTL; uint32_t STA; uint32_t DMAC; - uint32_t: 32; // address not implemented + uint32_t: 32; uint32_t RNG1; uint32_t DAT1; uint32_t FIF1; - uint32_t: 32; // address not implemented + uint32_t: 32; uint32_t RNG2; uint32_t DAT2; }; @@ -108,7 +115,6 @@ Structs This struct is used to configure a PWM channel:: typedef struct { - pwm_channel_t channel; union { struct { uint32_t: 1; @@ -126,10 +132,6 @@ Structs uint32_t range; } pwm_channel_config_t; - .. member:: pwm_channel_t channel - - This member specifies the PWM channel to configure. - .. member:: uint32_t ctl_register This member can be directly edited by the anonymous struct inside @@ -157,10 +159,10 @@ Functions This function unmaps the PWM registers. -.. function:: void pwm_configure(pwm_channel_config_t *config) +.. function:: void pwm_configure(pwm_channel_t channel, pwm_channel_config_t *config) - This function configures a PWM channel with a :type:`pwm_channel_config_t` - pointed to by :code:`config`. + This function configures :type:`pwm_channel_t` :code:`channel` with a + :type:`pwm_channel_config_t` pointed to by :code:`config`. .. function:: void pwm_enable(pwm_channel_t channel) diff --git a/doc/spi.rst b/doc/spi.rst index 9a8cfb6..62342ce 100644 --- a/doc/spi.rst +++ b/doc/spi.rst @@ -10,13 +10,20 @@ Macros .. macro:: SPI_OFFSET + :: + + 0x204000 + This macro defines the offset at which the SPI registers are located from - the peripheral base. It has the value :code:`0x204000` + the peripheral base. .. macro:: SPI_SIZE + :: + + 0x14 + This macro holds the size of the I2C registers which needs to be mapped. - It has the value :code:`0x14` Configuration Macros -------------------- @@ -73,7 +80,7 @@ Structs typedef struct { union { struct { - uint32_t cs: 2; + uint32_t: 2; uint32_t cpha: 1; uint32_t cpol: 1; uint32_t: 2; @@ -124,6 +131,11 @@ Functions This function configures SPI with a :type:`spi_channel_config_t` pointed to by :code:`config`. +.. function:: void spi_set_ce(uint8_t ce) + + This function sets which chip enable line the SPI controller should use. + This can be a 3 bit value. + .. function:: void spi_transfer_start(void) This function starts a SPI transfer. diff --git a/examples/i2c_example.c b/examples/i2c_example.c index 6fda157..5796f8f 100644 --- a/examples/i2c_example.c +++ b/examples/i2c_example.c @@ -43,12 +43,9 @@ main(void) gpio_func(sda, ALT0); gpio_func(scl, ALT0); - i2c_config_t conf = { - .addr = 0x6a, // address - .div = 4000, // clock divider -> clk must be < 100kHz for gyro - .clkstr = 0x40 // standard reset value - }; - i2c_configure(&conf); + i2c_set_address(0x6a); // set address + i2c_set_clkdiv(4000); // clock divider -> clk must be < 100kHz for gyro + i2c_set_clkstr(0x40); // can often be ignored and left on reset value i2c_start(); // start i2c diff --git a/examples/pwm_example.c b/examples/pwm_example.c index 1d29f94..beeba2f 100644 --- a/examples/pwm_example.c +++ b/examples/pwm_example.c @@ -47,7 +47,6 @@ main(void) gpio_func(servo, ALT5); // pin 18 uses alternate function 5 for pwm pwm_channel_config_t ch = { - PWM_CHANNEL0, // Which channel to use 0/1 {{ /* * these strange double braces are not needed, * but else the compiler gives a warning because of @@ -90,7 +89,7 @@ main(void) * => solves to: divisor = 1953.125 */ - pwm_configure(&ch); + pwm_configure(PWM_CHANNEL0, &ch); pwm_enable(PWM_CHANNEL0); int going_up = 1; diff --git a/examples/spi_example.c b/examples/spi_example.c index 6bcdb2f..a1ce64e 100644 --- a/examples/spi_example.c +++ b/examples/spi_example.c @@ -63,7 +63,6 @@ main(void) * but else the compiler gives a warning * because of the way 'spi_channel_config_t' is implemented */ - SPI_CS_CE0, // which chip enable line to use SPI_CPHA_CLK_BEGINNING, // data on clock leading or trailing edge SPI_CPOL_RESET_LOW, // clock polarity: rest state low or high SPI_CSPOL_ACTIVE_LOW, /* @@ -88,6 +87,7 @@ main(void) }; spi_configure(&conf); + spi_set_ce(SPI_CS_CE0); // set which chip enable line to use union spi_mcp3002_transfer t; t.lst = 0b01; // Leading zero and starting bit diff --git a/src/i2c.c b/src/i2c.c index eb58101..ff2fc0e 100644 --- a/src/i2c.c +++ b/src/i2c.c @@ -47,11 +47,21 @@ i2c_unmap(void) } void -i2c_configure(i2c_config_t *config) +i2c_set_address(uint8_t addr) { - I2C->A = config->addr; - I2C->DIV = config->div; - I2C->CLKT = config->clkstr; + I2C->A = addr & 0x7f; // only 7 bit +} + +void +i2c_set_clkdiv(uint16_t divisor) +{ + I2C->DIV = divisor; +} + +void +i2c_set_clkstr(uint16_t clkstr) +{ + I2C->CLKT = clkstr; } void diff --git a/src/i2c.h b/src/i2c.h index b2d71f0..4e5bfe3 100644 --- a/src/i2c.h +++ b/src/i2c.h @@ -44,16 +44,12 @@ struct i2c_register_map { }; #define I2C ((volatile struct i2c_register_map *)i2c_base_ptr) -typedef struct { - uint8_t addr: 7; - uint16_t div; - uint16_t clkstr; // clock stretch timeout -} i2c_config_t; - uint32_t * i2c_map(void); void i2c_unmap(void); -void i2c_configure(i2c_config_t *config); +void i2c_set_address(uint8_t addr); +void i2c_set_clkdiv(uint16_t divisor); +void i2c_set_clkstr(uint16_t clkstr); void i2c_start(void); void i2c_stop(void); diff --git a/src/mipea.c b/src/mipea.c index 18017ba..0c86a83 100644 --- a/src/mipea.c +++ b/src/mipea.c @@ -21,7 +21,7 @@ int mipea_map(void) { - if (!dma_map() || !gpio_map() || !pwm_map() || !spi_map() + if (!dma_map() || !gpio_map() || !i2c_map() || !pwm_map() || !spi_map() || !timer_map()) { return -1; } @@ -33,6 +33,7 @@ mipea_unmap(void) { dma_unmap(); gpio_unmap(); + i2c_unmap(); pwm_unmap(); spi_unmap(); timer_unmap(); diff --git a/src/mipea.h b/src/mipea.h index 1d896b6..aaa1ea8 100644 --- a/src/mipea.h +++ b/src/mipea.h @@ -21,6 +21,7 @@ #include "dma.h" #include "gpio.h" +#include "i2c.h" #include "pwm.h" #include "spi.h" #include "timer.h" diff --git a/src/pwm.c b/src/pwm.c index 2bb5ef5..6f4166b 100644 --- a/src/pwm.c +++ b/src/pwm.c @@ -71,12 +71,12 @@ pwm_disable(pwm_channel_t channel) } void -pwm_configure(pwm_channel_config_t *config) +pwm_configure(pwm_channel_t channel, pwm_channel_config_t *config) { clock_configure(&CM->PWMCTL, CLOCK_PLLD, config->divisor, 0); clock_enable(&CM->PWMCTL); - if (config->channel == PWM_CHANNEL0) { + if (channel == PWM_CHANNEL0) { PWM->CTL &= ~0xff; // clear all pwm0 bits PWM->CTL |= config->ctl_register; diff --git a/src/pwm.h b/src/pwm.h index 5f1a538..c806115 100644 --- a/src/pwm.h +++ b/src/pwm.h @@ -54,7 +54,6 @@ typedef enum { } pwm_channel_t; typedef struct { - pwm_channel_t channel; union { struct { uint32_t: 1; // use pwm_enable / pwm_disable @@ -63,7 +62,7 @@ typedef struct { uint32_t sbit: 1; uint32_t pola: 1; uint32_t usef: 1; - uint32_t: 1; // unimplemented / unused + uint32_t: 1; uint32_t msen: 1; }; uint32_t ctl_register; @@ -75,21 +74,22 @@ typedef struct { uint32_t * pwm_map(void); void pwm_unmap(void); +void pwm_configure(pwm_channel_t channel, pwm_channel_config_t *config); + void pwm_enable(pwm_channel_t channel); void pwm_disable(pwm_channel_t channel); -void pwm_configure(pwm_channel_config_t *config); /******* CTL Register bit values *******/ #define PWM_CTL_MODE_PWM 0x0 #define PWM_CTL_MODE_SERIALISER 0x1 -#define PWM_RPTL_STOP 0x0 -#define PWM_RPTL_REPEAT 0x1 -#define PWM_SBIT_LOW 0x0 -#define PWM_SBIT_HIGH 0x1 -#define PWM_POLA_DEFAULT 0x0 -#define PWM_POLA_INVERTED 0x1 -#define PWM_USEF_DATA 0x0 -#define PWM_USEF_FIFO 0x1 +#define PWM_RPTL_STOP 0x0 +#define PWM_RPTL_REPEAT 0x1 +#define PWM_SBIT_LOW 0x0 +#define PWM_SBIT_HIGH 0x1 +#define PWM_POLA_DEFAULT 0x0 +#define PWM_POLA_INVERTED 0x1 +#define PWM_USEF_DATA 0x0 +#define PWM_USEF_FIFO 0x1 #define PWM_MSEN_PWMALGORITHM 0x0 #define PWM_MSEN_MSRATIO 0x1 diff --git a/src/spi.c b/src/spi.c index 36dbb68..dda8bf8 100644 --- a/src/spi.c +++ b/src/spi.c @@ -48,29 +48,35 @@ spi_configure(spi_channel_config_t *config) SPI->CLK = config->divisor; } +void +spi_set_ce(uint8_t ce) +{ + SPI->CS = ce & 3; // only use the last 2 bits +} + inline void spi_transfer_start(void) { - SPI->CS |= 0x80; /* set TA */ + SPI->CS |= 0x80; // set TA } inline void spi_transfer_stop(void) { - while(!(SPI->CS & 0x10000)) {} /* wait for DONE */ - SPI->CS &= ~0x80; /* clear TA */ + while(!(SPI->CS & 0x10000));// wait for DONE + SPI->CS &= ~0x80; // clear TA } inline uint8_t spi_transfer_byte(uint8_t data) { - SPI->CS |= 0x30; /* Clear FIFO */ + SPI->CS |= 0x30; // Clear FIFO SPI->FIFO = data; - while(!(SPI->CS & 0x20000)) {} /* wait for data in RX FIFO */ + while(!(SPI->CS & 0x20000)); // wait for data in RX FIFO return (uint8_t) SPI->FIFO; } -/* send 2 bytes and return the data from the last transmission */ +// send 2 bytes and return the data from the last transmission inline uint8_t spi_send2_recv1(uint8_t data0, uint8_t data1) { diff --git a/src/spi.h b/src/spi.h index af5c3a7..776e1f5 100644 --- a/src/spi.h +++ b/src/spi.h @@ -43,7 +43,7 @@ struct spi_register_map { typedef struct { union { struct { - uint32_t cs: 2; // cs = chip select + uint32_t: 2; uint32_t cpha: 1; uint32_t cpol: 1; uint32_t: 2; // unimplemented / unused -> must be zero @@ -53,7 +53,7 @@ typedef struct { uint32_t cspol1: 1; uint32_t cspol2: 1; }; - uint32_t cs_register; // cs = conntrol and status + uint32_t cs_register; // cs = control and status }; uint16_t divisor; @@ -63,6 +63,7 @@ uint32_t * spi_map(void); void spi_unmap(void); void spi_configure(spi_channel_config_t *config); +void spi_set_ce(uint8_t ce); extern void spi_transfer_start(void); extern void spi_transfer_stop(void);