Permalink
Fetching contributors…
Cannot retrieve contributors at this time
430 lines (359 sloc) 22.3 KB
/*
* This file is a part of the open source stm32plus library.
* Copyright (c) 2011,2012,2013,2014 Andy Brown <www.andybrown.me.uk>
* Please see website for licensing terms.
*/
#pragma once
namespace stm32plus {
namespace display {
/**
* Specialisation for 48Mhz HCLK, 64K colour mode, 42ns low and 42ns high write cycle periods.
* The 20.8ns HCLK means that we actually achieve 41.6ns (+/- clock accuracy).
* This driver is intended for use with the 48MHz products, for example the F0 Discovery board.
*/
template<class TPinPackage>
class Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42> {
protected:
uint32_t _controlSetAddress;
uint32_t _controlResetAddress;
uint32_t _portOutputRegister;
uint16_t _wr;
uint16_t _rs;
uint32_t _jump;
public:
Gpio16BitAccessMode();
void reset() const;
void writeCommand(uint16_t command) const;
void writeCommand(uint16_t command,uint16_t parameter) const;
void writeData(uint16_t value) const;
void writeDataAgain(uint16_t value) const;
void writeMultiData(uint32_t howMuch,uint16_t value) const __attribute__((noinline));
void rawTransfer(const void *buffer,uint32_t numWords) const;
};
/**
* Constructor
*/
template<class TPinPackage>
inline Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::Gpio16BitAccessMode() {
// the assembly code needs these
_rs=TPinPackage::Pin_RS;
_wr=TPinPackage::Pin_WR;
// these are the addresses of the reset/set registers in the normal peripheral region.
#if defined(STM32PLUS_F1) || defined(STM32PLUS_F0)
_controlResetAddress=TPinPackage::Port_CONTROL+offsetof(GPIO_TypeDef,BRR);
_controlSetAddress=TPinPackage::Port_CONTROL+offsetof(GPIO_TypeDef,BSRR);
#elif defined(STM32PLUS_F4)
_controlResetAddress=TPinPackage::Port_CONTROL+offsetof(GPIO_TypeDef,BSRRH);
_controlSetAddress=TPinPackage::Port_CONTROL+offsetof(GPIO_TypeDef,BSRRL);
#else
#error Unsupported MCU
#endif
// this is the address of the data output ODR register in the normal peripheral region.
_portOutputRegister=TPinPackage::Port_DATA+offsetof(GPIO_TypeDef,ODR);
// all 16 port pins to output, 50MHz slew rate
GpioPinInitialiser::initialise((GPIO_TypeDef *)TPinPackage::Port_DATA,
0xffff,
Gpio::OUTPUT);
// control pins to output
GpioPinInitialiser::initialise((GPIO_TypeDef *)TPinPackage::Port_CONTROL,
TPinPackage::Pin_RS | TPinPackage::Pin_WR | TPinPackage::Pin_RESET,
Gpio::OUTPUT);
// WR must start as HIGH
GPIO_SetBits((GPIO_TypeDef *)TPinPackage::Port_CONTROL,TPinPackage::Pin_WR);
}
/**
* Hard-reset the panel
*/
template<class TPinPackage>
inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::reset() const {
GPIO_TypeDef *port;
// let the power stabilise
MillisecondTimer::delay(10);
// reset sequence
port=(GPIO_TypeDef *)TPinPackage::Port_CONTROL;
GPIO_SetBits(port,TPinPackage::Pin_RESET);
MillisecondTimer::delay(5);
GPIO_ResetBits(port,TPinPackage::Pin_RESET);
MillisecondTimer::delay(50);
GPIO_SetBits(port,TPinPackage::Pin_RESET);
MillisecondTimer::delay(50);
}
/**
* Write a command
* @param command The command register
*/
template<class TPinPackage>
inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::writeCommand(uint16_t command) const {
__asm volatile(
" str %[value], [%[data]] \n\t" // port <= value
" str %[rs], [%[creset], #0] \n\t" // [rs] = 0
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
:: [creset] "l" (_controlResetAddress), // the control reset address
[cset] "l" (_controlSetAddress), // the control set address
[data] "l" (_portOutputRegister), // the data port
[wr] "l" (_wr), // WR pin bit
[rs] "l" (_rs), // RS pin bit
[value] "l" (command) // input value
);
}
/**
* Write a command and its parameter (convenience function)
* @param command The command register
* @param parameter The register parameter
*/
template<class TPinPackage>
inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::writeCommand(uint16_t command,uint16_t parameter) const {
writeCommand(command);
writeData(parameter);
}
/**
* Write a data value
* @param value The data value to write
*/
template<class TPinPackage>
inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::writeData(uint16_t value) const {
__asm volatile(
" str %[value], [%[data]] \n\t" // port <= value
" str %[rs], [%[cset], #0] \n\t" // [rs] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
:: [creset] "l" (_controlResetAddress), // the control reset address
[cset] "l" (_controlSetAddress), // the control set address
[data] "l" (_portOutputRegister), // the data port
[wr] "l" (TPinPackage::Pin_WR), // WR pin bit
[rs] "l" (TPinPackage::Pin_RS), // RS pin bit
[value] "l" (value) // input value
);
}
/**
* Write the same data value that we have recently written out. This is one of our optimisation
* points. We don't have to do the whole 8080 transaction again and can just toggle WR.
* @param value The data value to write
*/
template<class TPinPackage>
inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::writeDataAgain(uint16_t /* value */) const {
__asm volatile(
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
:: [creset] "l" (_controlResetAddress), // the control reset address
[cset] "l" (_controlSetAddress), // the control set address
[wr] "l" (TPinPackage::Pin_WR) // WR pin bit
);
}
/**
* Write a batch of the same data values to the XMEM interface using GPIO. The values are written out in a
* highly optimised loop in bursts of 40 at a time. This value seems a good trade off between flash usage
* and speed. Note the use of %= labels so that inlining doesn't produce duplicate names.
* @param howMuch The number of 16-bit values to write
* @param value The data value to write
*/
template<class TPinPackage>
__attribute__((noinline)) inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::writeMultiData(uint32_t howMuch,uint16_t value) const {
// F0 compatibility : value, data, rs are only needed at the start so move
// them to own asm section so gcc doesn't have to find so many registers
// in the next block
__asm volatile(
"str %[value], [%[data]] \n\t" // port <= value
"str %[rs], [%[cset], #0] \n\t" // [rs] = 1
:: [rs] "l" (TPinPackage::Pin_RS), // RS pin bit
[value] "l" (value), // input value
[cset] "l" (_controlSetAddress), // the control set address
[data] "l" (_portOutputRegister) // the data port
);
// this is the main block
__asm volatile(
" cmp %[howmuch], #40 \n\t" // if less than 40 then go straight
" blo lastlot%= \n\t" // to the finishing off stage
// the following loop shows the fastest that you can toggle a GPIO pin
// on the STM32.
"batchloop%=: \n\t"
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" sub %[howmuch], #40 \n\t" // subtract 40 from howMuch
" cmp %[howmuch], #40 \n\t" // if howMuch >= 40 then go back for another batch
" bhs batchloop%= \n\t"
"lastlot%=: \n\t"
" ldr %[jump], =finished%= \n\t" // load 'jump' with the address of the end
" lsl %[howmuch], #2 \n\t" // multiply remaining by 4 and
" sub %[jump], %[howmuch] \n\t" // subtract from the 'jump' target
" add %[jump], #1 \n\t" // set thumb mode (bit 0=1)
" bx %[jump] \n\t" // indirect jump forward into the last lot
// there are 39 writes here
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
" str %[wr], [%[creset], #0] \n\t" // [wr] = 0
" str %[wr], [%[cset], #0] \n\t" // [wr] = 1
"finished%=: \n\t"
:: [creset] "l" (_controlResetAddress), // the control reset address
[cset] "l" (_controlSetAddress), // the control set address
[wr] "l" (TPinPackage::Pin_WR), // WR pin bit
[jump] "l" (_jump), // holds calculated indirect jump target
[howmuch] "l" (howMuch) // number of pixels to write
);
}
/**
* Write out a raw block of data from memory
* @param buffer Where to read from
* @param numWords The number of 16-bit words
*/
template<class TPinPackage>
inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,48,42,42>::rawTransfer(const void *buffer,uint32_t numWords) const {
const uint16_t *ptr=static_cast<const uint16_t *>(buffer);
while(numWords--)
writeData(*ptr++);
}
}
}