/* Part of the Raspberry-Pi Bare Metal Tutorials Copyright (c) 2015, Brian Sidebotham All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "rpi/aux.h" #include "rpi/armtimer.h" #include "rpi/gpio.h" #include "rpi/interrupts.h" #include "rpi/mailbox-interface.h" #include "rpi/systimer.h" #include "kernel/util.h" /** Main function - we'll never return from here */ void kernel_main( unsigned int r0, unsigned int r1, unsigned int atags ) { int width = SCREEN_WIDTH, height = SCREEN_HEIGHT, bpp = SCREEN_DEPTH; int x, y, pitch = 0; colour_t current_colour; volatile unsigned char* fb = NULL; int pixel_offset; int r, g, b, a; float cd = COLOUR_DELTA; unsigned int frame_count = 0; /* Write 1 to the LED init nibble in the Function Select GPIO peripheral register to enable LED pin as an output */ RPI_GetGpio()->LED_GPFSEL |= LED_GPFBIT; /* Enable the timer interrupt IRQ */ RPI_GetIrqController()->Enable_Basic_IRQs = RPI_BASIC_ARM_TIMER_IRQ; /* Setup the system timer interrupt */ /* Timer frequency = Clk/256 * 0x400 */ RPI_GetArmTimer()->Load = 0x400; /* Setup the ARM Timer */ RPI_GetArmTimer()->Control = RPI_ARMTIMER_CTRL_23BIT | RPI_ARMTIMER_CTRL_ENABLE | RPI_ARMTIMER_CTRL_INT_ENABLE | RPI_ARMTIMER_CTRL_PRESCALE_256; /* Enable interrupts! */ _enable_interrupts(); /* Initialise the UART */ RPI_AuxMiniUartInit( 115200, 8 ); /* Print to the UART using the standard libc functions */ printf( "Valvers.com ARM Bare Metal Tutorials\r\n" ); printf( "Initialise UART console with standard libc\r\n\n" ); RPI_PropertyInit(); RPI_PropertyAddTag( TAG_GET_BOARD_MODEL ); RPI_PropertyAddTag( TAG_GET_BOARD_REVISION ); RPI_PropertyAddTag( TAG_GET_FIRMWARE_VERSION ); RPI_PropertyAddTag( TAG_GET_BOARD_MAC_ADDRESS ); RPI_PropertyAddTag( TAG_GET_BOARD_SERIAL ); RPI_PropertyAddTag( TAG_GET_MAX_CLOCK_RATE, TAG_CLOCK_ARM ); RPI_PropertyProcess(); rpi_mailbox_property_t* mp; mp = RPI_PropertyGet( TAG_GET_BOARD_MODEL ); if( mp ) printf( "Board Model: %d\r\n", mp->data.value_32 ); else printf( "Board Model: NULL\r\n" ); mp = RPI_PropertyGet( TAG_GET_BOARD_REVISION ); if( mp ) printf( "Board Revision: %d\r\n", mp->data.value_32 ); else printf( "Board Revision: NULL\r\n" ); mp = RPI_PropertyGet( TAG_GET_FIRMWARE_VERSION ); if( mp ) printf( "Firmware Version: %d\r\n", mp->data.value_32 ); else printf( "Firmware Version: NULL\r\n" ); mp = RPI_PropertyGet( TAG_GET_BOARD_MAC_ADDRESS ); if( mp ) printf( "MAC Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\r\n", mp->data.buffer_8[0], mp->data.buffer_8[1], mp->data.buffer_8[2], mp->data.buffer_8[3], mp->data.buffer_8[4], mp->data.buffer_8[5] ); else printf( "MAC Address: NULL\r\n" ); mp = RPI_PropertyGet( TAG_GET_BOARD_SERIAL ); if( mp ) printf( "Serial Number: %8.8X%8.8X\r\n", mp->data.buffer_32[0], mp->data.buffer_32[1] ); else printf( "Serial Number: NULL\r\n" ); mp = RPI_PropertyGet( TAG_GET_MAX_CLOCK_RATE ); if( mp ) printf( "Maximum ARM Clock Rate: %dHz\r\n", mp->data.buffer_32[1] ); else printf( "Maximum ARM Clock Rate: NULL\r\n" ); /* Ensure the ARM is running at it's maximum rate */ RPI_PropertyInit(); RPI_PropertyAddTag( TAG_SET_CLOCK_RATE, TAG_CLOCK_ARM, mp->data.buffer_32[1] ); RPI_PropertyProcess(); RPI_PropertyInit(); RPI_PropertyAddTag( TAG_GET_CLOCK_RATE, TAG_CLOCK_ARM ); RPI_PropertyProcess(); mp = RPI_PropertyGet( TAG_GET_CLOCK_RATE ); if( mp ) printf( "Set ARM Clock Rate: %dHz\r\n", mp->data.buffer_32[1] ); else printf( "Set ARM Clock Rate: NULL\r\n" ); /* Initialise a framebuffer... */ RPI_PropertyInit(); RPI_PropertyAddTag( TAG_ALLOCATE_BUFFER ); RPI_PropertyAddTag( TAG_SET_PHYSICAL_SIZE, SCREEN_WIDTH, SCREEN_HEIGHT ); RPI_PropertyAddTag( TAG_SET_VIRTUAL_SIZE, SCREEN_WIDTH, SCREEN_HEIGHT * 2 ); RPI_PropertyAddTag( TAG_SET_DEPTH, SCREEN_DEPTH ); RPI_PropertyAddTag( TAG_GET_PITCH ); RPI_PropertyAddTag( TAG_GET_PHYSICAL_SIZE ); RPI_PropertyAddTag( TAG_GET_DEPTH ); RPI_PropertyProcess(); if( ( mp = RPI_PropertyGet( TAG_GET_PHYSICAL_SIZE ) ) ) { width = mp->data.buffer_32[0]; height = mp->data.buffer_32[1]; printf( "Initialised Framebuffer: %dx%d ", width, height ); } if( ( mp = RPI_PropertyGet( TAG_GET_DEPTH ) ) ) { bpp = mp->data.buffer_32[0]; printf( "%dbpp\r\n", bpp ); } if( ( mp = RPI_PropertyGet( TAG_GET_PITCH ) ) ) { pitch = mp->data.buffer_32[0]; printf( "Pitch: %d bytes\r\n", pitch ); } if( ( mp = RPI_PropertyGet( TAG_ALLOCATE_BUFFER ) ) ) { fb = (unsigned char*)mp->data.buffer_32[0]; printf( "Framebuffer address: %8.8X\r\n", (unsigned int)fb ); } /* Never exit as there is no OS to exit to! */ current_colour.r = 0x4B; current_colour.g = 0x00; current_colour.b = 0x82; current_colour.a = 0; while( 1 ) { /* Produce a colour spread across the screen */ for( y = 0; y < height; y++ ) { // current_colour.r += ( 1.0 / height ); for( x = 0; x < width; x++ ) { pixel_offset = ( x * ( bpp >> 3 ) ) + ( y * pitch ); r = (int)( current_colour.r * 0xFF ) & 0xFF; g = (int)( current_colour.g * 0xFF ) & 0xFF; b = (int)( current_colour.b * 0xFF ) & 0xFF; a = (int)( current_colour.a * 0xFF ) & 0xFF; if( bpp == 32 ) { /* Four bytes to write */ fb[ pixel_offset++ ] = r; fb[ pixel_offset++ ] = g; fb[ pixel_offset++ ] = b; fb[ pixel_offset++ ] = a; } else if( bpp == 24 ) { /* Three bytes to write */ fb[ pixel_offset++ ] = r; fb[ pixel_offset++ ] = g; fb[ pixel_offset++ ] = b; } else if( bpp == 16 ) { /* Two bytes to write */ /* Bit pack RGB565 into the 16-bit pixel offset */ *(unsigned short*)&fb[pixel_offset] = ( (r >> 3) << 11 ) | ( ( g >> 2 ) << 5 ) | ( b >> 3 ); } else { /* Palette mode. TODO: Work out a colour scheme for packing rgb into an 8-bit palette! */ } // current_colour.b += ( 1.0 / width ); } } /* Scroll through the green colour */ /* current_colour.g += cd; if( current_colour.g > 1.0 ) { current_colour.g = 1.0; cd = -COLOUR_DELTA; } else if( current_colour.g < 0.0 ) { current_colour.g = 0.0; cd = COLOUR_DELTA; } */ frame_count++; if( calculate_frame_count ) { calculate_frame_count = 0; /* Number of frames in a minute, divided by seconds per minute */ float fps = (float)frame_count / 60; printf( "FPS: %.2f\r\n", fps ); frame_count = 0; } } }