Code optimizations in 8051 - c

So, I have to do this challenge, which is to implement a camera surveillance system for a 8051 microcontroller.
These are the specifications:
Each camera is related to a movement sensor, and each time it detects a movement, the recording of this camera will be among the ones that will be registered and saved. If the sensor doesn't capture any movement for more than 5 seconds, this camera will not be recorded anymore;
If there's no camera on, the video recorder must be on "pause";
If more than one camera is on, a multiplexer (mux) have to be used to select the camera signals in a way so each camera is recorded during 3 seconds. This way, all the active cameras must be recorded during 3 seconds. If just one camera is active, it's signal must be the only one in the mux.
This I have already accomplished in the code below. And what we have to do now is to optimize the size of the code without the compiler optimizations. The code is 198 bytes by now, but I'm trying to get below 180 bytes.
Is it possible? I already tried to do the calculations of the #define, but the compiler already optimize that for me.
#include <REG51F.h>
#define TIMEOUT 50
#define TIMEOUT_REC 30
#define FrClk 12000000
#define FreqTimer0_emHz 10
#define VALOR_TH0 ((65536 - (FrClk /(12 * FreqTimer0_emHz ))) >> 8)
#define VALOR_TL0 ((65536 - (FrClk /(12 * FreqTimer0_emHz ))) & 0xFF)
data bit PAUSE_INT;
data bit PAUSE_TMP;
sbit PAUSE = P0^0;
sbit SENSOR1 = P0^1;
sbit SENSOR2 = P0^2;
sbit SENSOR3 = P0^3;
sbit SENSOR4 = P0^4;
sbit MUX0 = P0^5;
sbit MUX1 = P0^6;
data unsigned char CAM[4];
data unsigned char REC;
data unsigned char index;
data unsigned char count;
void timer0_int (void) interrupt 1 using 2 {
for (index = 0; index < 4; index++)
if(CAM[index])
CAM[index]--;
if (!PAUSE_INT && REC)
REC--;
else
{
REC = TIMEOUT_REC;
index = (index + 1) & 0x03;
for (count = 0; !CAM[index] && count < 4; index = (index + 1) & 0x03, count++);
MUX0 = index & 0x1;
MUX1 = index & 0x2;
PAUSE_INT = 0;
}
}
int main(void)
{
PAUSE_TMP = 1;
PAUSE_INT = 0;
index = 0;
//timer0_init
EA = 1;
TR0 = 0;
TMOD = (TMOD & 0xF0) | 0x01;
TH0 = VALOR_TH0;
TL0 = VALOR_TL0;
ET0 = 1;
TR0 = 1;
while(1) {
if (SENSOR1)
{
CAM[0] = TIMEOUT;
}
if (SENSOR2)
{
CAM[1] = TIMEOUT;
}
if (SENSOR3)
{
CAM[2] = TIMEOUT;
}
if (SENSOR4)
{
CAM[3] = TIMEOUT;
}
if (CAM[0] || CAM[1] || CAM[2] || CAM[3])
{
if (PAUSE_TMP)
PAUSE_INT = 1;
PAUSE_TMP = 0;
}
else {
PAUSE_TMP = 1;
}
PAUSE = PAUSE_TMP;
}
}

You're probably going to have to look at the generated assembly code for this in order to wring the last few bytes out of it. It's probably possible to shave a few here and there by reusing a variable or combining operations. The resulting code won't be pretty - or maintainable - but it just might get you below your cutoff.

I think a switch case instead of if(sensor1,2,3,4) could help some.

Related

Is there a way to stop flickering on 7-segment when counting up/down by multiplexing?

Board: Using EasyPIC v7
MCU: PIC16F874
I recently started to try out some code and working on some microchips for counting up and down as well as reset, on 3 7-segment displays. So far I have gotten down the basics, i just lack some optimization and some problem fixing. You can see a hint of C# style in the code xD.
My main issue is that whenever i count up or count down, the segment display that is updating flickers, i know why its happening i just want to know where i can use the "Delay_ms()" where it would slow down the update rate without interrupting the multiplexing.
/*
a1 is for ones, a10 is for tens, and a100 for hundreds
char nums1 and nums2 are the binary codes for the 7-segment.
I was testing out wether to use 0 in the beginning or at the end.
*/
static int a1; static int a10; static int a100;
char nums1 [10] = {0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111, 0b00111111};
char nums2 [10] = {0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111};
static int initMain()
{
a1 = 1; a10 = 1; a100 = 1;
}
void main()
{
initMain();
ADCON1 = 0x0F;
TRISA = 0;
TRISB = 0xFF;
TRISD = 0;
do
{
if (PORTB.B0) // Here is where the problem lies i guess
{
Delay_ms(100);
a1 = a1 + 2; // Counting up by 2
}
if (PORTB.B1)
{
Delay_ms(100);
a1 = a1 - 2; // Counting down by 2
}
if (PORTB.B2)
{
initMain(); // Reset
}
PORTA = 1;
PORTD = nums2[a1-1];
Delay_ms(5);
PORTA = 10;
PORTD = nums2[a10-1];
Delay_ms(5);
PORTA = 100;
PORTD = nums2[a100-1];
Delay_ms(5);
if (a1 < 1)
{ a1 = 9; a10--;}
if (a1 > 10)
{ a1 = 1; a10++;}
if (a10 > 10)
{ a10 = 1; a100++;}
if (a10 < 1)
{ a10 = 10; a100--;}
} while(1);
}
Beside the index ranges, that should be checked according to comments, you should have a display refresh function that is called w/o any delays. The best way to do that is using a timer.
For example: Let the timer set a flag every 100ms or so.
Your main loop can than look like this.
int TimerFlag = 0;
main()
{
init();
initTimer();
do{
if(TimerFlag){
TimerFlag = 0;
incrementCounter();
}
refreshDisplay();
} while(1);
}
/////////////////////////////
void timerInterrupt()
{
TimerFlag = 1;
}
With this pattern, the display is updated on every loop but the counter is incremented every 100ms (or the timer interval set by you).
Don't forget to enable the timer interrupt...

Make a microcontroller always notice a button press even while waiting(Delay)

Here im using the stm32l discovery microcontroller.
First i initialize the ports for the gpio ports for the leds and the user button.
I have it working now and the little program i have now makes the led blink.
Every time you press the button the waiting time gets longer making the led blink longer. the problem now is that as soon as the waiting time exeeds 4 seconds or more its to hard for the microcontroller to notice the button press.
Is there a way to make it that it always notices a button press
int main(void) {
char *RCCp = (char*) 0x40023800;
int *PAp = (int *) 0x40020000;
int *PBp = (int *) 0x40020400;
// RCC Config
*((int*) (RCCp + 28)) |= 0x3f;
*((int*) (RCCp + 32)) |= 1;
// Pin config
*PBp = 0x5000;
*PAp = 0x0000;
int speed = 100000;
int i = 0;
while (1) {
while (i++ < speed); // Waiting time
*(int*) (0x40020414) ^= 0xC0;
i = 0;
if ((*(int*) (0x40020010) & 0x0001) != 0x0) {
speed = speed * 2;
if (speed > 400000) {
speed = 100000;
}
}
}
return 0;
}
One small step forward is to poll the switch status within your busy-wait loop. Like this:
int i = 0;
while (1) {
bool wasSwitchClosedThisPeriod = false;
while (i++ < speed) {
// Poll the switch to see if it is closed.
if ((*(int*)(0x40020010) & 0x0001) != 0) {
wasSwitchClosedThisPeriod = true;
}
}
*(int*) (0x40020414) ^= 0xC0;
i = 0;
if (wasSwitchClosedThisPeriod) {
speed = speed * 2;
if (speed > 400000) {
speed = 100000;
}
}
}
But there is still a lot of room for improvement. For example, you may want to debounce the switch. The switch data register should be volatile, i.e. *(int volatile *)(0x40020010UL). And using interrupts could make the code more flexible, efficient and even allow you to put the microcontroller to sleep while it waits (conserving power). If the GPIO input pin can be configured as an interrupt then you wouldn't need to poll the switch. And if there's a hardware timer available then you could configure it to interrupt when it's time to change the LED.

is it possible compare a 16-bit value with a 8-bit compare match ISR

I am trying to make a servo controller that have a higher resolution than the ATtiny85 8-bit timer/counter. So far I have managed to get about 2000 positions on my servo (1µs/step) within a time frame of 21'000 µs. I have also managed to move 5 servos sequential and with different speed, but now I want to move them synchronous.
My biggest problem is that I don't get how I should make it happen! I have looked around on other servo codes including servo8bit library and tried to find a way. It seems that most of the examples uses compare match ISR to move the servos "at the same time", my problem is that I have a 16-bit integer that I want to compare.
Is there a way to do some magic so I can use the 8-bit compare match ISR with my 16-bit integer? Or does anyone of you have some other suggestions on how I can move my servos synchronous without using compare match ISR?
I hope my questions make sense!
Since I don't really have any code to show yet (only flawed attempts without compar match ISR that makes no sense) I post the link to my TinyServo code if it helps.
EDIT 1:
Here is the part of the code I mentioned and didn't post the first time:
void servoMove(void)
{
uint16_t nextPulse = hPulse[0];
timerSetup (); //16-bit setup for counter
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if HIGH pulse (pos) is done
{
PORTB &= ~(1 << servo[channel]);
if (i+1 < sizeof(hPulse)/sizeof(hPulse[0]))
{
nextPulse += hPulse[i+1];
}
channel++;
}
else
{
channel = 0;
oTime = 0; //resets 16-bit variable
tot_overflow = 0; //resets tot_overflow variable
TIFR |= (1 << TOV1); // clear counter1 overflow-flag
TCNT1 = 0; //resets Timer/Counter1
}
}
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > tPulse - nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if LOW pulse (period) is done
{
PORTB |= (1 << servo[channel]);
nextPulse -= hPulse[i];
channel++;
}
}
}
void servoPosSet(volatile uint16_t pos[], uint8_t size)
{
for (i = 0; i < size; i++)
{
hPulse[i] = pos[i];
}
}
int main(void)
{
TCCR1 |= (1 << CS12); //set Timer/Counter1 prescaler to increment every 1 µs (PCK/8)
for (channel = 0; channel < size); channel++)
{
DDRB |= (1 << servo[channel]); //sets PB0-PB4 as output pins
}
channel = 0;
uint16_t pos[] = {2000, 1500, 1900, 1300, 1700};
uint8_t size = 5;
while(1)
{
servoPosSet(pos);
servoMove();
}
}
EDIT 2:
This is an illustration of how I think the code should work:
...but it does not!
If you have nothing else to do during the pulse, you could use a busy
loop instead of interrupts:
#include <avr/io.h>
#include <util/delay_basic.h>
/* Send a pulse of width = 4*count cycles. */
void pulse(uint16_t count, uint8_t channel)
{
uint8_t mask = 1 << channel,
old_port = PORTB,
high = old_port | mask,
low = old_port & ~mask;
PORTB = high;
_delay_loop_2(count);
PORTB = low;
}
This will give you a resolution of 4 clock cycles, or 0.5 µs with a
8 MHz clock.
Sending the pulses to the 5 servos should take at most 10 ms. Since
you repeat the pulse train every 21 ms, this leaves you 11 ms
to compute the next set of positions, which should be plenty. You could
program a timer to wake you up every 21 ms, then your main() may
look like:
int main(void)
{
static uint16_t pos[] = {4000, 3000, 3800, 2600, 3400};
uint8_t i;
/* Wake up every 21 ms. */
setup_timer();
sleep_enable();
for (;;) {
/* Update the servos. */
for (i = 0; i < 5; i++) pulse(pos[i], i);
/* Compute the next set of positions. */
...
/* Wait for timer interrupt. */
sleep_cpu();
}
}

An issue that might lay in the code which I don't see (using a ledstrip) Atmel Studio 6.1

I am using Atmel Studio 6.1 and this is the code that I use, beside this i also got another code called "ledstrip.h". Which basically is an 1 dimensional array.
This is something for my study.
I am trying to send 12 bytes through a 'ledstrip'
code:
#define SPI_SS_bm PIN4_bm
#define SPI_MOSI_bm PIN5_bm
#define SPI_MISO_bm PIN6_bm
#define SPI_SCK_bm PIN7_bm
#define MIN_VALUE 0.4
#define F_CPU 32000000UL
#define P 128
#define FOO 0
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "ledstrip.h"
void spi_init(void);
uint8_t spi_transfer(uint8_t data);
const uint8_t *ptr = image;
uint8_t num[12];
int line;
int g;
int data;
int i;
void row (const uint8_t *j)
{
uint32_t row2 = 0;
for(g = 0; g < 4; g++)
{
for(int i = 0; i < 8; i++)
{
row2 = (row2 << 3) | pgm_read_byte(j);
j++;
}
num[3*g] = (uint8_t) (row2 >> 16); //put bytes in the right order
num[3*g+1] = (uint8_t) (row2 >> 8);
num[3*g+2] = (uint8_t) (row2);
}
}
void spi_initialisation(void)
{
PORTC.DIRSET = SPI_SCK_bm|SPI_MOSI_bm|SPI_SS_bm;
PORTC.DIRCLR = SPI_MISO_bm;
SPIC.CTRL = (!SPI_CLK2X_bm) | // no double clock speed
SPI_ENABLE_bm | // SPI enable
!SPI_DORD_bm | // data order, MSB first
SPI_MASTER_bm | // master
SPI_MODE_0_gc | // mode 0
SPI_PRESCALER_DIV4_gc; // Presc. 4 (#2 MHz,500kHz)
}
void spi_init(void)
{
PORTC.DIRSET = PIN7_bm|PIN5_bm|PIN4_bm; // 7: MOSI 5: SCK
PORTC.OUTCLR = PIN4_bm; // 4: latch enable
PORTD.DIRSET |= PIN3_bm|PIN1_bm; // 3: MOSI 1: SCK
USARTC1.BAUDCTRLA = 0; // baud rate FCPU/2
USARTC1.BAUDCTRLB = 0; //
USARTC1.CTRLC = USART_CMODE_MSPI_gc; // SPI mode
USARTC1.CTRLA = 0; // no interrupts
USARTC1.CTRLB = USART_TXEN_bm; // enable transmit
USARTD0.BAUDCTRLA = 0; // baud rate FCPU/2
USARTD0.BAUDCTRLB = 0; //
USARTD0.CTRLC = USART_CMODE_MSPI_gc; // SPI mode
USARTD0.CTRLA = 0; // no interrupts
USARTD0.CTRLB = USART_TXEN_bm; // enable transmit
}
void init_timer_phi(void)
{
TCE0.CTRLA = TC_CLKSEL_DIV8_gc; // prescaling P
TCE0.CTRLB = TC_WGMODE_NORMAL_gc; // normal mode
TCE0.INTCTRLA = TC_OVFINTLVL_LO_gc;
TCE0.PER = 1440; // t = PER*FCPU/P =
// FCPU*P/32M = 1 us
}
ISR (TCE0_OVF_vect)
{
ptr = ptr +32;
if(ptr >= image + 32 * 360)
{
ptr = image;
} // send bytes to ledstrip
}
void init_inputcapture(void)
{
PORTC.PIN2CTRL = PORT_ISC_FALLING_gc;
PORTC.DIRCLR = PIN2_bm; // Pin 2 is input
EVSYS.CH0MUX = EVSYS_CHMUX_PORTC_PIN2_gc; // Select PC2 as input
// to event channel 0
TCC0.CTRLD = TC_EVACT_CAPT_gc | // Event capture
TC_EVSEL_CH0_gc; // for Channel 0
TCC0.CTRLB = TC0_CCAEN_bm; // Enable Inp. Capt. Ch. A
TCC0.CTRLA = TC_CLKSEL_DIV256_gc; // Start timer
TCC0.INTCTRLB = TC_CCAINTLVL_LO_gc; // Set Interr. level Ch. A
TCC0.PER = 0xFFFF;
}
ISR(TCC0_CCA_vect)
{
uint16_t v;
v = TCC0.CCA;
if (v > MIN_VALUE)
{ // skip if measured value is too small
line = 0; // reset image
TCE0.PER = (v * 360/(F_CPU / P)); // calculate periode
TCC0.CTRLFSET = TC_CMD_RESTART_gc; // restart input capture
}
}
void spi_write_byte(uint8_t data)
{
PORTC.OUTCLR = SPI_SS_bm;
spi_transfer(data);
PORTC.OUTSET = SPI_SS_bm;
}
uint8_t spi_read_byte(void)
{
uint8_t data;
PORTC.OUTCLR = SPI_SS_bm;
data = spi_transfer (FOO);
PORTC.OUTSET = SPI_SS_bm;
return data;
}
int main(void)
{
uint8_t i = 0;
spi_init();
PORTC.DIRSET = PIN0_bm;
PMIC.CTRL |= PMIC_LOLVLEN_bm;
sei();
PORTC.DIRSET = PIN4_bm | PIN5_bm | PIN6_bm | PIN7_bm;
while(1)
{
SPIC.DATA = i; // send i
while( ! (SPIC.STATUS & (SPI_IF_bm)) ); // wait until send
PORTC.OUTSET = PIN0_bm; // store
PORTC.OUTCLR = PIN0_bm;
_delay_ms(100);
i++;
}
}
Okay, so I got this code with no errors and warnings.
But when I run the program on my microcontroller ATXMega128A4U, I don't get the intended output.
There is a possibility that I didn't connect the pins of my 'ledstrip' in the right way with the pins of the microcontroller.
But if we consider that I did it like I intended to, the problem should be in the code.
The intended signal is 12 bytes long and with that I can turn RGB LED's on or off.
The RGB LED's are put together in a so called 'ledstrip'
Now what I'm getting is that some of the RGB LED's turn on and are Red Green or Blue.
What i should be getting is something like a chasing RGB LED row.
So you would see the LED's turn on or off at a certain frequency.

8051 c interrupts

I'm using C to write a program on an 8051 microcontroller. The compiler I'm using is Keil Microvision. I'm stuck and having trouble figuring out what is missing from my code. I know it's very basic code I just can't figure out what I'm supposed to do.
So pretty much what I am doing is taking sending a sentence out to the user and having them answer yes or no through the serial port and I used a serial interrupt. That part works fine. If I get a no from the person I want to generate a square wave 5kHz by a timer interrupt. I want this square wave to be controlled by an external interrupt turning it on and off when the external interrupt on pin P3.2 is either on or off.
Here is all my code
#include <REG52.H>
#include <stdio.h>
sbit WAVE = P1 ^ 7;
#define BIT(x) (1 << (x))
void timer0() interrupt 1 // timer is controlling square wave timer 0
{
WAVE = ~WAVE;
}
void interrupt0() interrupt 0
{
IE ^= BIT(1);
}
void serial0() interrupt 4
{
unsigned char x;
unsigned int i, z;
unsigned char yes[] = " YES ";
unsigned char no[] = " NO ";
unsigned char nvalid[] = " NOT VALID TRY AGAIN ";
while (RI == 1) {
x = SBUF;
RI = 0;
if (z < 1) {
if (x == 'n') {
for (i = 0; i < 4; i++) {
SBUF = no[i];
while (TI == 0) ; //wait for transmit
TI = 0;
z++;
}
}
} else {
return;
}
if (x == 'y') {
for (i = 0; i < 5; i++) {
SBUF = yes[i];
while (TI == 0) ;
TI = 0;
}
} else if (x != 'n') {
for (i = 0; i < 21; i++) {
SBUF = nvalid[i];
while (TI == 0) ;
TI = 0;
}
}
TI = 0;
return;
}
}
void main()
{
TMOD = 0x20;
TH1 = 0xF6; //baud rate
SCON = 0x50;
TH0 = 0xA4;
IE = 0x93; //enable interrupts
IP = 0x10; // propriety to serial interrupt
TR1 = 1; //start timer 1
TR0 = 1; //clear timer 0
TI = 1;
printf("Hello, Are you okay? Press y for yes and n for no ");
while (1) ;
}
The part I'm having trouble with is these two interrupt from the previous code
void timer0() interrupt 1 // timer is controlling square wave timer 0
{
WAVE=~WAVE;
}
void interrupt0() interrupt 0
{
IE ^= BIT(1);
}
Any hints in the right direction would be greatly appreciated! Thanks. Sorry about formatting
Variables that are modified by interrupts should be defined as volatile:
volatile sbit WAVE = P1 ^ 7;

Resources