Commit e65d2db7 authored by Projects's avatar Projects

lcd: DMA mode

parent a591b26d
/***************************************************************************//**
* @file
* @brief DMA control data block.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#include "dmactrl.h"
#if ( ( DMA_CHAN_COUNT > 4 ) && ( DMA_CHAN_COUNT <= 8 ) )
#define DMACTRL_CH_CNT 8
#define DMACTRL_ALIGNMENT 256
#elif ( ( DMA_CHAN_COUNT > 8 ) && ( DMA_CHAN_COUNT <= 16 ) )
#define DMACTRL_CH_CNT 16
#define DMACTRL_ALIGNMENT 512
#else
#error "Unsupported DMA channel count (dmactrl.c)."
#endif
/** DMA control block array, requires proper alignment. */
#if defined (__ICCARM__)
#pragma data_alignment=DMACTRL_ALIGNMENT
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMACTRL_CH_CNT * 2];
#elif defined (__CC_ARM)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMACTRL_CH_CNT * 2] __attribute__ ((aligned(DMACTRL_ALIGNMENT)));
#elif defined (__GNUC__)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMACTRL_CH_CNT * 2] __attribute__ ((aligned(DMACTRL_ALIGNMENT)));
#else
#error Undefined toolkit, need to define alignment
#endif
/***************************************************************************//**
* @file
* @brief DMA control data block.
* @author Energy Micro AS
* @version 3.20.0
******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#ifndef __DMACTRL_H
#define __DMACTRL_H
/***************************************************************************//**
* @addtogroup Drivers
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup DmaCtrl
* @{
******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
extern DMA_DESCRIPTOR_TypeDef dmaControlBlock[];
#ifdef __cplusplus
}
#endif
/** @} (end group DmaCtrl) */
/** @} (end group Drivers) */
#endif /* __DMACTRL_H */
/*
* Copyright (C) 2014 Julian Lewis
* @author Maciej Suminski <maciej.suminski@cern.ch>
* @author Bartosz Bielawski <bartosz.bielawski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -23,26 +24,36 @@
/**
* @brief LS013B7DH03 LCD driver
*/
// Uncomment if you do not want to use DMA for frame transfer
//#define LCD_NODMA
#include "lcd.h"
#ifndef LCD_NODMA
#include "lcd_dma.h"
#endif
#include <em_cmu.h>
#include <em_usart.h>
#include <em_rtc.h>
#include <em_timer.h>
#include <udelay.h>
// Framebuffer - pixels are stored as consecutive rows
uint8_t lcd_buffer[LCD_STRIDE * LCD_HEIGHT];
// Frame buffer - pixels are stored as consecutive rows
uint8_t lcd_buffer[LCD_BUF_SIZE];
uint8_t * const off_buffer = lcd_buffer + LCD_CONTROL_BYTES;
static void spi_init(void)
{
USART_InitSync_TypeDef usartInit = USART_INITSYNC_DEFAULT;
CMU_ClockEnable(LCD_SPI_CLOCK, true);
usartInit.baudrate = LCD_SPI_BAUDRATE;
USART_InitSync_TypeDef usartInit = USART_INITSYNC_DEFAULT;
usartInit.databits = usartDatabits8;
usartInit.baudrate = LCD_SPI_BAUDRATE;
USART_InitSync(LCD_SPI_UNIT, &usartInit);
/*LCD_SPI_UNIT->CTRL |= USART_CTRL_AUTOCS;*/
LCD_SPI_UNIT->ROUTE = (USART_ROUTE_CLKPEN | USART_ROUTE_TXPEN | LCD_SPI_LOCATION);
/*USART_ROUTE_CSPEN*/
}
static void spi_transmit(uint8_t *data, uint16_t length)
......@@ -128,6 +139,21 @@ static void extcomin_setup(unsigned int frequency)
TIMER_Init(TIMER0, &timerInit);
}
static void lcd_prepend_update_commands()
{
// Add control codes for all lines
uint16_t i;
for(i = 0; i < LCD_HEIGHT; ++i)
{
lcd_buffer[i * LCD_STRIDE] = LCD_CMD_UPDATE;
lcd_buffer[i * LCD_STRIDE + 1] = i + 1;
}
// Ending control command
lcd_buffer[LCD_HEIGHT * LCD_STRIDE] = 0xff;
lcd_buffer[LCD_HEIGHT * LCD_STRIDE + 1] = 0xff;
}
void lcd_init(void)
{
uint16_t cmd;
......@@ -163,6 +189,12 @@ void lcd_init(void)
GPIO_PinOutClear(LCD_PORT_SCS, LCD_PIN_SCS);
lcd_clear();
lcd_prepend_update_commands();
#ifndef LCD_NODMA
lcd_dma_init();
#endif /* LCD_NODMA */
lcd_enable(1);
}
......@@ -186,61 +218,31 @@ void lcd_clear(void)
{
// Using uint32_t instead of uint8_t reduces the number of writes 4 times
uint32_t *p = (uint32_t*)lcd_buffer;
uint16_t i;
uint16_t x, y;
// Clear pixel buffer
for(i = 0; i < sizeof(lcd_buffer) / sizeof(uint32_t); ++i)
*p++ = 0x00;
for(y = 0; y < LCD_HEIGHT; ++y) {
// skip control bytes
p = (uint32_t*)((uint8_t*)p + 2);
#ifndef LCD_NODMA
// Add control codes
for(i = 1; i < LCD_HEIGHT; ++i)
{
lcd_buffer[i * LCD_STRIDE - 2] = 0xff; // Dummy
lcd_buffer[i * LCD_STRIDE - 1] = (i + 1); // Address of next line
// clear line
for(x = 0; x < LCD_STRIDE / sizeof(uint32_t); ++x) {
*p++ = 0x00;
}
}
#endif
}
void lcd_update(void)
{
// Need to adjust start row by one because LS013B7DH03 starts counting lines
// from 1, while the DISPLAY interface starts from 0.
const uint8_t START_ROW = 1;
// TODO use DMA
uint16_t cmd;
uint16_t i;
uint8_t *p = (uint8_t*) lcd_buffer;
GPIO_PinOutSet(LCD_PORT_SCS, LCD_PIN_SCS);
timer_delay(6);
// Send update command and first line address
cmd = LCD_CMD_UPDATE | (START_ROW << 8);
spi_transmit((uint8_t*) &cmd, 2);
#ifdef LCD_NODMA
for(i = 0; i < LCD_HEIGHT; ++i)
{
// Send pixels for this line
spi_transmit(p, LCD_WIDTH / 8);
p += (LCD_WIDTH / 8);
// TODO seems unnecessary
// if(i == LCD_HEIGHT - 1)
// cmd = 0xffff;
// else
cmd = 0xff | ((START_ROW + i + 1) << 8);
spi_transmit((uint8_t*) &cmd, 2);
}
#else
// TODO here the DMA transfer should run in the end
spi_transmit(p, LCD_STRIDE * LCD_HEIGHT);
#endif
spi_transmit(lcd_buffer, LCD_BUF_SIZE);
timer_delay(2);
GPIO_PinOutClear(LCD_PORT_SCS, LCD_PIN_SCS);
#else
lcd_dma_send_frame();
// chip select is deasserted in the dma tx complete interrupt
#endif
}
/*
* Copyright (C) 2014 Julian Lewis
* @author Maciej Suminski <maciej.suminski@cern.ch>
* @author Bartosz Bielawski <bartosz.bielawski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -57,22 +58,18 @@
// Peripherals
#define LCD_SPI_UNIT USART2
#define LCD_SPI_CLOCK cmuClock_USART2
#define LCD_SPI_LOCATION 0
#define LCD_SPI_LOCATION USART_ROUTE_LOCATION_LOC0
#define LCD_SPI_BAUDRATE 500000
#define LCD_POL_INV_FREQ 60
// Do not use DMA for frame transfer
#define LCD_NODMA
// Additional bytes to control the LCD; required for DMA transfers
#ifdef LCD_NODMA
#define CONTROL_BYTES 0
#else
#define CONTROL_BYTES 2
#endif
// Additional bytes to control the LCD; stored in framebuffer, required for DMA transfers
#define LCD_CONTROL_BYTES 2
// Number of bytes to store one line
#define LCD_STRIDE (LCD_WIDTH / 8 + CONTROL_BYTES)
#define LCD_STRIDE (LCD_WIDTH / 8 + LCD_CONTROL_BYTES)
// Framebuffer size (in bytes)
#define LCD_BUF_SIZE (LCD_STRIDE * LCD_HEIGHT + LCD_CONTROL_BYTES)
/**
* @brief LCD initialization routine.
......@@ -109,7 +106,7 @@ void lcd_update(void);
*/
static inline void lcd_set_pixel(uint8_t x, uint8_t y, uint8_t value)
{
extern uint8_t lcd_buffer[];
extern uint8_t * const off_buffer;
// x %= LCD_WIDTH;
// y %= LCD_HEIGHT;
......@@ -126,9 +123,9 @@ static inline void lcd_set_pixel(uint8_t x, uint8_t y, uint8_t value)
#endif
if(value)
lcd_buffer[offset] |= mask;
off_buffer[offset] |= mask;
else
lcd_buffer[offset] &= ~mask;
off_buffer[offset] &= ~mask;
}
/**
......@@ -138,7 +135,7 @@ static inline void lcd_set_pixel(uint8_t x, uint8_t y, uint8_t value)
*/
static inline void lcd_toggle_pixel(uint8_t x, uint8_t y)
{
extern uint8_t lcd_buffer[];
extern uint8_t * const off_buffer;
// x %= LCD_WIDTH;
// y %= LCD_HEIGHT;
......@@ -154,7 +151,7 @@ static inline void lcd_toggle_pixel(uint8_t x, uint8_t y)
uint16_t offset = (y * LCD_STRIDE) + (x >> 3); // == y * LCD_STRIDE + x / 8
#endif
lcd_buffer[offset] ^= mask;
off_buffer[offset] ^= mask;
}
/**
......@@ -164,7 +161,7 @@ static inline void lcd_toggle_pixel(uint8_t x, uint8_t y)
*/
static inline uint8_t lcd_get_pixel(uint8_t x, uint8_t y)
{
extern uint8_t lcd_buffer[];
extern uint8_t * const off_buffer;
// x %= LCD_WIDTH;
// y %= LCD_HEIGHT;
......@@ -180,7 +177,7 @@ static inline uint8_t lcd_get_pixel(uint8_t x, uint8_t y)
uint16_t offset = (y * LCD_STRIDE) + (x >> 3); // == y * LCD_STRIDE + x / 8
#endif
return lcd_buffer[offset] & mask;
return off_buffer[offset] & mask;
}
#endif /* LCD_H */
......
/*
* Copyright (C) 2014 Julian Lewis
* @author Maciej Suminski <maciej.suminski@cern.ch>
* @author Bartosz Bielawski <bartosz.bielawski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "em_device.h"
#include "em_dma.h"
#include "em_emu.h"
#include "em_ebi.h"
#include "em_cmu.h"
#include "em_usart.h"
#include "dmactrl.h"
#include "lcd.h"
#include "lcd_dma.h"
// The DMA channel to use
#define DMA_CHANNEL 0
// Number of DMA transfers
#define DMA_TRANSFERS 3
// Framebuffer to be drawn
extern uint8_t lcd_buffer[LCD_BUF_SIZE];
// Stores pointer to the function called after transfers are finished
DMA_CB_TypeDef dma_callback;
// Configuration structures for two halves of the screen
DMA_DESCRIPTOR_TypeDef dma_cfg_block[DMA_TRANSFERS];
// Flag to indicate DMA state
volatile bool dma_transfer_active = false;
// Called by DMA when transfer is complete
static void lcd_dma_tx_complete(unsigned int channel, bool primary, void *user)
{
(void) channel; // suppress warnings
(void) primary;
(void) user;
dma_transfer_active = false;
}
void lcd_dma_init(void)
{
DMA_Init_TypeDef dma_init;
DMA_CfgChannel_TypeDef channel_config;
DMA_CfgDescrSGAlt_TypeDef descriptor_config;
CMU_ClockEnable(cmuClock_DMA, true);
dma_init.hprot = 0;
dma_init.controlBlock = dmaControlBlock;
DMA_Init(&dma_init);
// Setting callback function
dma_callback.cbFunc = lcd_dma_tx_complete;
dma_callback.userPtr = NULL;
// Setting up channel
channel_config.highPri = false; // No high priority
channel_config.enableInt = true; // Enable interrupt
channel_config.select = DMAREQ_USART2_TXBL;
channel_config.cb = &dma_callback; // Callback routine
DMA_CfgChannel(DMA_CHANNEL, &channel_config);
// Setting up channel descriptor
descriptor_config.dstInc = dmaDataIncNone;
descriptor_config.srcInc = dmaDataInc1;
descriptor_config.size = dmaDataSize1;
descriptor_config.arbRate = dmaArbitrate1;
descriptor_config.hprot = 0;
descriptor_config.peripheral = true;
descriptor_config.dst = (void *)&(LCD_SPI_UNIT->TXDATA);
// Lines 0-42
descriptor_config.src = &lcd_buffer[0];
descriptor_config.nMinus1 = LCD_STRIDE * 43 - 1;
DMA_CfgDescrScatterGather(dma_cfg_block, 0, &descriptor_config);
// Lines 43-85
descriptor_config.src = &lcd_buffer[LCD_STRIDE * 43];
descriptor_config.nMinus1 = LCD_STRIDE * 43 - 1;
DMA_CfgDescrScatterGather(dma_cfg_block, 1, &descriptor_config);
// Lines 86-128
descriptor_config.src = &lcd_buffer[LCD_STRIDE * 86];
descriptor_config.nMinus1 = LCD_STRIDE * 43 - 1;
DMA_CfgDescrScatterGather(dma_cfg_block, 2, &descriptor_config);
dma_transfer_active = false;
}
void lcd_dma_send_frame(void)
{
while(dma_transfer_active);
dma_transfer_active = true;
DMA_ActivateScatterGather(DMA_CHANNEL, true, dma_cfg_block, DMA_TRANSFERS);
}
bool lcd_dma_is_active(void)
{
return dma_transfer_active;
}
/*
* Copyright (C) 2014 Julian Lewis
* @author Maciej Suminski <maciej.suminski@cern.ch>
* @author Bartosz Bielawski <bartosz.bielawski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
// TODO author note
#ifndef LCD_DMA_H
#define LCD_DMA_H
/**
* Initialize DMA operation.
* Enables the clock and sets up descriptors and
* registers for dma frame buffer transfer.
*/
void lcd_dma_init(void);
/**
* TODO
*/
void lcd_dma_send_frame(void);
/**
* TODO
*/
bool lcd_dma_is_active(void);
#endif /* LCD_DMA_H */
......@@ -129,6 +129,7 @@ C_SRC += \
../common/emlib/src/em_assert.c \
../common/emlib/src/em_burtc.c \
../common/emlib/src/em_cmu.c \
../common/emlib/src/em_dma.c \
../common/emlib/src/em_emu.c \
../common/emlib/src/em_gpio.c \
../common/emlib/src/em_int.c \
......@@ -138,7 +139,9 @@ C_SRC += \
../common/emlib/src/em_timer.c \
../common/emlib/src/em_usart.c \
../common/emdrv/sleep/src/sleep.c \
../common/drivers/dmactrl.c \
../common/drivers/lcd.c \
../common/drivers/lcd_dma.c \
../common/drivers/backlight.c \
../common/drivers/buttons.c \
../common/drivers/buzzer.c \
......
......@@ -142,17 +142,21 @@ INCLUDEPATHS += \
C_SRC += \
../common/Device/EnergyMicro/EFM32GG/Source/system_efm32gg.c \
../common/drivers/dmactrl.c \
../common/drivers/lcd.c \
../common/drivers/lcd_dma.c \
../common/drivers/i2cdrv.c \
../common/drivers/max17047.c \
../common/emlib/src/em_assert.c \
../common/emlib/src/em_cmu.c \
../common/emlib/src/em_dma.c \
../common/emlib/src/em_emu.c \
../common/emlib/src/em_gpio.c \
../common/emlib/src/em_i2c.c \
../common/emlib/src/em_int.c \
../common/emlib/src/em_system.c \
../common/emlib/src/em_rtc.c \
../common/emlib/src/em_timer.c \
../common/emlib/src/em_usart.c \
../common/gfx/graphics.c \
../common/gfx/font_helv11.c \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment