Some time ago I modelled a low poly model of a Portuguese Caravel, but due to lack of detailed references it seemed poor, eventually I reworked the model and added more detail after a trip to the national navy museum. Armed with more photos and patience, the following model was accomplished with 32000 Triangles and 5 textures (+bumpmaps generated on demand in Blender). The wood textured was generated in Blender (for the wood seams) and GIMP (contrast, color correction, levels and seamless texture generation). It can be downloaded here under the CC license.
The previous model was also updated with a few tweaks and it's available to download.
Monday, 4 August 2014
Saturday, 15 February 2014
AVR USART Queue Snippet
This snippet defines queue buffers to be used with the USART RX/TX for the Atmel AVR ATmega 8/32, it uses interruptions to release the MCU from continuous polling. This was based on AVR306 app note source code from Atmel.
Defines & Includes:
Source:
Updated: 2014/12/20, changed to use stdout and removed malloc usage.
Defines & Includes:
#define F_CPU 16000000
#define BAUD 9600
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define BAUD 9600
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
Source:
#define USART_RX_SIZE 64 // Receive queue max size
#define USART_TX_SIZE 64 // Transmit queue max size
#define USART_RX_MASK ( USART_RX_SIZE - 1 )
#define USART_TX_MASK ( USART_TX_SIZE - 1 )
static uint8_t USART_RxBuf [USART_RX_SIZE];
static uint8_t USART_TxBuf [USART_TX_SIZE];
int USART_Tx_File(char ch, FILE *stream);
static FILE USART_StdOut = FDEV_SETUP_STREAM(USART_Tx_File, NULL, _FDEV_SETUP_WRITE);
static volatile uint8_t USART_RxHead;
static volatile uint8_t USART_RxTail;
static volatile uint8_t USART_TxHead;
static volatile uint8_t USART_TxTail;
void (*USART_RxCallback)(uint8_t ch) = NULL;
void USART_Init(long baudrate){
long baud_setting = (F_CPU / 8 / baudrate - 1) / 2;
memset(USART_RxBuf,0,USART_RX_SIZE);
memset(USART_TxBuf,0,USART_TX_SIZE);
// Set STDOUT to custom file stream.
stdout = &USART_StdOut;
UBRRH = (baud_setting >> 8);
UBRRL = baud_setting;
// Don't use U2X
UCSRA &= ~(1 << U2X);
// Enable TX(TXEN), Enable RX(RXEN), Enable RX Interrupt(RXCIE)
UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
// Asyncronous, Write to UCSRC(URSEL), 8 bit
UCSRC = (1<<URSEL) | (3<<UCSZ0);
USART_RxTail = 0;
USART_RxHead = 0;
USART_TxTail = 0;
USART_TxHead = 0;
}
void USART_Tx(uint8_t data){
uint8_t tmphead;
tmphead = ( USART_TxHead + 1 ) & USART_TX_MASK;
// Wait while full
while ( tmphead == USART_TxTail ){};
USART_TxBuf[tmphead] = data;
USART_TxHead = tmphead;
// If the UART ain't busy sending bytes, send the first byte.
if((UCSRB & (1<<TXCIE)) != (1<<TXCIE)){
// Enable Tx interrupt for the following bytes
UCSRB |= (1<<TXCIE);
// Emulate the TX interruption for the first character
uint8_t tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
}
int USART_Tx_File(char ch, FILE *stream){
USART_Tx(ch);
return 0;
}
uint8_t USART_RxByte(void){
uint8_t tmptail;
while ( USART_RxHead == USART_RxTail ){};
tmptail = ( USART_RxTail + 1 ) & USART_RX_MASK;
USART_RxTail = tmptail;
return USART_RxBuf[tmptail];
}
ISR(USART_RXC_vect){
uint8_t tmphead;
uint8_t data = UDR;
tmphead = ( USART_RxHead + 1 ) & USART_RX_MASK;
USART_RxHead = tmphead;
if ( tmphead != USART_RxTail )
USART_RxBuf[tmphead] = data;
if( USART_RxCallback != NULL )
USART_RxCallback(data);
}
ISR(USART_TXC_vect){
uint8_t tmptail;
if ( USART_TxHead != USART_TxTail ){
tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
else{
// Empty queue, disable interrupt.
UCSRB &= ~(1<<TXCIE);
}
}
#define USART_TX_SIZE 64 // Transmit queue max size
#define USART_RX_MASK ( USART_RX_SIZE - 1 )
#define USART_TX_MASK ( USART_TX_SIZE - 1 )
static uint8_t USART_RxBuf [USART_RX_SIZE];
static uint8_t USART_TxBuf [USART_TX_SIZE];
int USART_Tx_File(char ch, FILE *stream);
static FILE USART_StdOut = FDEV_SETUP_STREAM(USART_Tx_File, NULL, _FDEV_SETUP_WRITE);
static volatile uint8_t USART_RxHead;
static volatile uint8_t USART_RxTail;
static volatile uint8_t USART_TxHead;
static volatile uint8_t USART_TxTail;
void (*USART_RxCallback)(uint8_t ch) = NULL;
void USART_Init(long baudrate){
long baud_setting = (F_CPU / 8 / baudrate - 1) / 2;
memset(USART_RxBuf,0,USART_RX_SIZE);
memset(USART_TxBuf,0,USART_TX_SIZE);
// Set STDOUT to custom file stream.
stdout = &USART_StdOut;
UBRRH = (baud_setting >> 8);
UBRRL = baud_setting;
// Don't use U2X
UCSRA &= ~(1 << U2X);
// Enable TX(TXEN), Enable RX(RXEN), Enable RX Interrupt(RXCIE)
UCSRB = (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);
// Asyncronous, Write to UCSRC(URSEL), 8 bit
UCSRC = (1<<URSEL) | (3<<UCSZ0);
USART_RxTail = 0;
USART_RxHead = 0;
USART_TxTail = 0;
USART_TxHead = 0;
}
void USART_Tx(uint8_t data){
uint8_t tmphead;
tmphead = ( USART_TxHead + 1 ) & USART_TX_MASK;
// Wait while full
while ( tmphead == USART_TxTail ){};
USART_TxBuf[tmphead] = data;
USART_TxHead = tmphead;
// If the UART ain't busy sending bytes, send the first byte.
if((UCSRB & (1<<TXCIE)) != (1<<TXCIE)){
// Enable Tx interrupt for the following bytes
UCSRB |= (1<<TXCIE);
// Emulate the TX interruption for the first character
uint8_t tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
}
int USART_Tx_File(char ch, FILE *stream){
USART_Tx(ch);
return 0;
}
uint8_t USART_RxByte(void){
uint8_t tmptail;
while ( USART_RxHead == USART_RxTail ){};
tmptail = ( USART_RxTail + 1 ) & USART_RX_MASK;
USART_RxTail = tmptail;
return USART_RxBuf[tmptail];
}
ISR(USART_RXC_vect){
uint8_t tmphead;
uint8_t data = UDR;
tmphead = ( USART_RxHead + 1 ) & USART_RX_MASK;
USART_RxHead = tmphead;
if ( tmphead != USART_RxTail )
USART_RxBuf[tmphead] = data;
if( USART_RxCallback != NULL )
USART_RxCallback(data);
}
ISR(USART_TXC_vect){
uint8_t tmptail;
if ( USART_TxHead != USART_TxTail ){
tmptail = ( USART_TxTail + 1 ) & USART_TX_MASK;
USART_TxTail = tmptail;
UDR = USART_TxBuf[tmptail];
}
else{
// Empty queue, disable interrupt.
UCSRB &= ~(1<<TXCIE);
}
}
Updated: 2014/12/20, changed to use stdout and removed malloc usage.
Friday, 14 February 2014
AVR I2C Master Snippet
This snippet defines functions to read/write bytes from i2c with an Atmel AVR ATmega 8/32, this can be used with EEPROM(24C02), IO Expander(PFC8574), Clock & Calendar(PCF8583) and other kind of slave i2c devides. This is based on Ronald Willem Besinga i2c tutorial, extended to execute page read/write operations.
Usage Example:
Use I2C_Page_Write and I2C_Sequencial_Read for more than one byte operations, otherwise i2c communication may start to fail.
Last update: 20/08/2014
#define I2C_START 0
#define I2C_STOP 1
#define I2C_DATA_NACK 2
#define I2C_DATA_ACK 3
function UART_Log(const char * str, ...){
// Dummy log
}
void I2C_Init(uint32_t clock)
{
// initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1
TWSR = 0; /* no prescaler */
TWBR = ((F_CPU/clock)-16)/2; /* must be > 10 for stable operation */
}
unsigned char I2C_Transmit(uint8_t type) {
switch(type) {
case I2C_START: // Send start condition
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
break;
case I2C_DATA_NACK: // Send data
TWCR = (1 << TWINT) | (1 << TWEN);
break;
case I2C_DATA_ACK: // Send data with acknowledge.
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
break;
case I2C_STOP: // Send stop condition
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
return 0;
}
// Wait for TWINT flag set in TWCR Register
while (!(TWCR & (1 << TWINT)));
// Return TWI Status Register, mask the prescaler bits (TWPS1,TWPS0)
return (TWSR & 0xF8);
}
uint8_t I2C_Start(uint8_t dev_id, uint8_t dev_addr, uint8_t addr){
uint8_t s;
// First step, send start
s = I2C_Transmit(I2C_START);
if (s == TW_MT_ARB_LOST || (s != TW_START && s != TW_REP_START)){
USART_Log("Error(Start): %x\r\n",s);
goto failure;
}
// Second step, set device id and device address
TWDR = (dev_id & 0xF0) | ((dev_addr << 1) & 0x0E) | TW_WRITE;
s = I2C_Transmit(I2C_DATA_ACK);
if (s == TW_MT_ARB_LOST || s != TW_MT_SLA_ACK || s == TW_MT_SLA_NACK){
USART_Log("Error(Device Id/Address): %x\r\n",s);
goto failure;
}
// Third step, set memory address
TWDR = addr;
s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MT_DATA_ACK){
USART_Log("Error(EEPROM Address): %x\r\n",s);
goto failure;
}
return 0;
failure:return 1;
}
uint8_t I2C_Random_Read(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t *data)
{
uint8_t ret = 0;
// First step, setup i2c and addresses (dummy write)
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, start again
uint8_t s = I2C_Transmit(I2C_START);
if (s == TW_MT_ARB_LOST || (s != TW_START && s != TW_REP_START)){
USART_Log("Error(Start again): %x\r\n",s);
goto exit;
}
// Third step, set device id and device address
TWDR = (dev_id & 0xF0) | ((dev_addr << 1) & 0x0E) | TW_READ;
s = I2C_Transmit(I2C_DATA_ACK);
if (s == TW_MR_ARB_LOST || s != TW_MR_SLA_ACK || s == TW_MR_SLA_NACK){
USART_Log("Error(Device address 2): %x\r\n",s);
goto exit;
}
// Forth step, read data
s = I2C_Transmit(I2C_DATA_NACK);
if (s != TW_MR_DATA_NACK){
USART_Log("Error(Data reception NACK): %x\r\n",s);
goto exit;
}
*data = TWDR;
ret = 1;
exit: I2C_Transmit(I2C_STOP);
return ret;
}
uint8_t I2C_Sequencial_Read(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t * data, uint8_t size)
{
uint8_t ret = 0;
// First step, setup i2c and addresses (dummy write)
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, start again
uint8_t s = I2C_Transmit(I2C_START);
if (s == TW_MT_ARB_LOST || (s != TW_START && s != TW_REP_START)){
USART_Log("Error(Start again): %x\r\n",s);
goto exit;
}
// Third step, set device id and device address
TWDR = (dev_id & 0xF0) | ((dev_addr << 1) & 0x0E) | TW_READ;
s = I2C_Transmit(I2C_DATA_ACK);
if (s == TW_MR_ARB_LOST || s != TW_MR_SLA_ACK || s == TW_MR_SLA_NACK){
USART_Log("Error(Device address 2): %x\r\n",s);
goto exit;
}
for(uint8_t i = 0;i < size;i++){
if(i == size - 1){
// Fifth step, read and finish with NACK
s = I2C_Transmit(I2C_DATA_NACK);
if (s != TW_MR_DATA_NACK){
USART_Log("Error(Data reception NACK): %x\r\n",s);
break;
}
}else{
// Forth step, read data with ACK
s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MR_DATA_ACK){
USART_Log("Error(Data reception ACK): %x\r\n",s);
break;
}
}
data[i] = TWDR;
ret += 1;
}
exit: I2C_Transmit(I2C_STOP);
return ret;
}
uint8_t I2C_Byte_Write(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t data)
{
uint8_t ret = 0;
// First step, setup i2c and addresses
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, write byte into memory
TWDR = data;
uint8_t s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MT_DATA_ACK){
USART_Log("Error(Data dispatch): %x\r\n",s);
goto exit;
}
ret = 1;
exit: I2C_Transmit(I2C_STOP);
return ret;
}
uint8_t I2C_Page_Write(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t * data, uint8_t size)
{
uint8_t ret = 0;
// First step, setup i2c and addresses
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, write byte into memory
for(uint8_t i = 0;i < size;i++){
TWDR = data[i];
uint8_t s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MT_DATA_ACK){
USART_Log("Error(Data dispatch): %x\r\n",s);
break;
}
ret ++;
}
exit: I2C_Transmit(I2C_STOP);
return ret;
}
#define I2C_STOP 1
#define I2C_DATA_NACK 2
#define I2C_DATA_ACK 3
function UART_Log(const char * str, ...){
// Dummy log
}
void I2C_Init(uint32_t clock)
{
// initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1
TWSR = 0; /* no prescaler */
TWBR = ((F_CPU/clock)-16)/2; /* must be > 10 for stable operation */
}
unsigned char I2C_Transmit(uint8_t type) {
switch(type) {
case I2C_START: // Send start condition
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
break;
case I2C_DATA_NACK: // Send data
TWCR = (1 << TWINT) | (1 << TWEN);
break;
case I2C_DATA_ACK: // Send data with acknowledge.
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
break;
case I2C_STOP: // Send stop condition
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
return 0;
}
// Wait for TWINT flag set in TWCR Register
while (!(TWCR & (1 << TWINT)));
// Return TWI Status Register, mask the prescaler bits (TWPS1,TWPS0)
return (TWSR & 0xF8);
}
uint8_t I2C_Start(uint8_t dev_id, uint8_t dev_addr, uint8_t addr){
uint8_t s;
// First step, send start
s = I2C_Transmit(I2C_START);
if (s == TW_MT_ARB_LOST || (s != TW_START && s != TW_REP_START)){
USART_Log("Error(Start): %x\r\n",s);
goto failure;
}
// Second step, set device id and device address
TWDR = (dev_id & 0xF0) | ((dev_addr << 1) & 0x0E) | TW_WRITE;
s = I2C_Transmit(I2C_DATA_ACK);
if (s == TW_MT_ARB_LOST || s != TW_MT_SLA_ACK || s == TW_MT_SLA_NACK){
USART_Log("Error(Device Id/Address): %x\r\n",s);
goto failure;
}
// Third step, set memory address
TWDR = addr;
s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MT_DATA_ACK){
USART_Log("Error(EEPROM Address): %x\r\n",s);
goto failure;
}
return 0;
failure:return 1;
}
uint8_t I2C_Random_Read(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t *data)
{
uint8_t ret = 0;
// First step, setup i2c and addresses (dummy write)
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, start again
uint8_t s = I2C_Transmit(I2C_START);
if (s == TW_MT_ARB_LOST || (s != TW_START && s != TW_REP_START)){
USART_Log("Error(Start again): %x\r\n",s);
goto exit;
}
// Third step, set device id and device address
TWDR = (dev_id & 0xF0) | ((dev_addr << 1) & 0x0E) | TW_READ;
s = I2C_Transmit(I2C_DATA_ACK);
if (s == TW_MR_ARB_LOST || s != TW_MR_SLA_ACK || s == TW_MR_SLA_NACK){
USART_Log("Error(Device address 2): %x\r\n",s);
goto exit;
}
// Forth step, read data
s = I2C_Transmit(I2C_DATA_NACK);
if (s != TW_MR_DATA_NACK){
USART_Log("Error(Data reception NACK): %x\r\n",s);
goto exit;
}
*data = TWDR;
ret = 1;
exit: I2C_Transmit(I2C_STOP);
return ret;
}
uint8_t I2C_Sequencial_Read(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t * data, uint8_t size)
{
uint8_t ret = 0;
// First step, setup i2c and addresses (dummy write)
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, start again
uint8_t s = I2C_Transmit(I2C_START);
if (s == TW_MT_ARB_LOST || (s != TW_START && s != TW_REP_START)){
USART_Log("Error(Start again): %x\r\n",s);
goto exit;
}
// Third step, set device id and device address
TWDR = (dev_id & 0xF0) | ((dev_addr << 1) & 0x0E) | TW_READ;
s = I2C_Transmit(I2C_DATA_ACK);
if (s == TW_MR_ARB_LOST || s != TW_MR_SLA_ACK || s == TW_MR_SLA_NACK){
USART_Log("Error(Device address 2): %x\r\n",s);
goto exit;
}
for(uint8_t i = 0;i < size;i++){
if(i == size - 1){
// Fifth step, read and finish with NACK
s = I2C_Transmit(I2C_DATA_NACK);
if (s != TW_MR_DATA_NACK){
USART_Log("Error(Data reception NACK): %x\r\n",s);
break;
}
}else{
// Forth step, read data with ACK
s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MR_DATA_ACK){
USART_Log("Error(Data reception ACK): %x\r\n",s);
break;
}
}
data[i] = TWDR;
ret += 1;
}
exit: I2C_Transmit(I2C_STOP);
return ret;
}
uint8_t I2C_Byte_Write(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t data)
{
uint8_t ret = 0;
// First step, setup i2c and addresses
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, write byte into memory
TWDR = data;
uint8_t s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MT_DATA_ACK){
USART_Log("Error(Data dispatch): %x\r\n",s);
goto exit;
}
ret = 1;
exit: I2C_Transmit(I2C_STOP);
return ret;
}
uint8_t I2C_Page_Write(uint8_t dev_id,
uint8_t dev_addr,
uint8_t addr, uint8_t * data, uint8_t size)
{
uint8_t ret = 0;
// First step, setup i2c and addresses
if(I2C_Start(dev_id,dev_addr,addr))
goto exit;
// Second step, write byte into memory
for(uint8_t i = 0;i < size;i++){
TWDR = data[i];
uint8_t s = I2C_Transmit(I2C_DATA_ACK);
if (s != TW_MT_DATA_ACK){
USART_Log("Error(Data dispatch): %x\r\n",s);
break;
}
ret ++;
}
exit: I2C_Transmit(I2C_STOP);
return ret;
}
Usage Example:
#define DEV_24C02 0xA4
#define SCL_CLOCK 100000L
I2C_Init(SCL_CLOCK);
I2C_Byte_Write(DEV_24C02,0,1,0xF0);
uint8_t val;
I2C_Random_Read(DEV_24C02,0,1,&val);
#define SCL_CLOCK 100000L
I2C_Init(SCL_CLOCK);
I2C_Byte_Write(DEV_24C02,0,1,0xF0);
uint8_t val;
I2C_Random_Read(DEV_24C02,0,1,&val);
Use I2C_Page_Write and I2C_Sequencial_Read for more than one byte operations, otherwise i2c communication may start to fail.
Last update: 20/08/2014
Tuesday, 24 December 2013
Postal de natal 2013
Esta é a minha entrada para a competição de natal de 2013 do forum Blender-PT de postais de natal, é algo simples sem elementos 3D, ré-imaginando a cena toda em 2.5D no Blender, Inkscape foi usado para adicionar o texto.
![]() |
| Bem podia ter usado uma fonte mais natalícia, ups :) |
Wednesday, 21 August 2013
Shishi Odoshi
This was my entry for the first Blender-PT competition, with the theme "Loop" animation, the idea was to make a small but loop capable animation, my entry is based on the japanese Shishi Odoshi. Rendered with BI (Blender Internal) and fluids simulation for water splash animation.
Check it on youtube
Subscribe to:
Posts (Atom)



