I tried to write a PIC program with C and what it does is turn on a series of 8 LED's (like Knight Rider LED illumination :D ). I have created the circuit design and tested with a simple c program that sets the state of the lights and it works. Now I want to streamline my code.
So i have created 2 functions for delaying and getting the required HEX value of the LED. the 2 methods are like this.
#define MAX 8
#define LEFT 1
#define RIGHT 2
#define BOTH 0
void delay_ms(int ms) {
while(ms > 0)
ms--;
}
int getHex(int delay, int dir, int *pin) {
int hex[] = {0x01, 0x02, 0x04, 0x10, 0x20, 0x40, 0x80};
if (dir == RIGHT) {
for (int i = 0; i < MAX; i++)
{
*pin = hex[i];
delay_ms(delay);
}
} else if( dir == LEFT ) {
for (int i = MAX - 1; i == 0; i--)
{
pin = hex[i];
delay_ms(delay);
}
}
}
and i have this in the main() function
main() {
TRISA = 1;
TRISB = 0;
while(1) {
if(RA0 == 0)
getHex(5000, RIGHT, PORTB);
}
}
What im trying to do is pass the PORTB predefined variable as a pointer to the function so it is set with the proper hex value. But i get the following warnings when i compile with the MPLAB IDE,
Warning [357] D:\...\main.c; 24.13 illegal conversion of integer to pointer
Warning [357] D:\...\main.c; 37.22 illegal conversion of integer to pointer
and when the program hex is added and run in the Proteus 8 nothing happens and the following warning appears.
[PIC16 MEMORY]PC=0x03AF. Attempt to write unimplemented memory location 0x0087 with 0x01 ignored
[PIC16 MEMORY]PC=0x03AF. Attempt to write unimplemented memory location 0x0087 with 0x02 ignored
[PIC16 MEMORY]PC=0x03AF. Attempt to write unimplemented memory location 0x0087 with 0x04 ignored
the final Hex value in the error correspond with the hex value i am trying to set as the value of PORTB. What am i doing wrong.. Please help.
There are several ways that such pre-defined registers may be declared. I don't know how Mplab does it, but the most common way is this:
#define PORTB (*(volatile uint8_t*)0x1234)
where 0x1234 is the hardware address of that register. This macro accesses the register directly, but the * to the left makes it so that you can use the macro as if PORTB was an ordinary variable and not a pointer.
If this is the case, then you need to pass &PORTB to your function that expects a pointer.
Changed the getHex Method definition to this.
int getHex(int delay, int dir, int *pin) {
int hex[] = {0x01, 0x02, 0x04, 0x10, 0x20, 0x40, 0x80};
if (dir == RIGHT) {
for (int i = 0; i < MAX; i++)
{
*pin = hex[i];
delay_ms(delay);
}
} else if( dir == LEFT ) {
for (int i = MAX - 1; i == 0; i--)
{
*pin = hex[i];
delay_ms(delay);
}
}
}
and then called the function with the following..
getHex(2000, RIGHT, &PORTB);
Related
I recently got a STM8 MCU and it has the built in function LCD_GLASS_DisplayString("STRING")
The problem with that function is, as you can see below, that I cannot directly display an integer on it:
void LCD_GLASS_DisplayString(uint8_t* ptr)
{
uint8_t i = 0x01;
LCD_GLASS_Clear();
/* Send the string character by character on lCD */
while ((*ptr != 0) & (i < 8))
{
/* Display one character on LCD */
LCD_GLASS_WriteChar(ptr, FALSE, FALSE, i);
/* Point on the next character */
ptr++;
/* Increment the character counter */
i++;
}
}
How could I modify it so I could send integers directly? Also, I'm not sure I can use any libraries, so just pure C would help.
I was thinking of something like this, but it didn't work:
void LCD_GLASS_DisplayINT(uint16_t integer)
{
uint8_t i = 0x01;
LCD_GLASS_Clear();
/* Send the string character by character on lCD */
while ((integer != 0) & (i < 8))
{
/* Display one number on LCD */
LCD_GLASS_WriteChar("0" + integer%10, FALSE, FALSE, i);
/* Point on the next number*/
integer=integer/10;
/* Increment the character counter */
i++;
}
}
Any idea on how to make it work? I need to either make a function to display the integers or a way to convert them to strings before I send them over to the LCD. The code is pure C, as what I'm programming are pure drivers right now.
You're not far off with "0" + integer%10 - but you need to treat it as a character - '0' + integer%10 - and you need to pass LCD_GLASS_WriteChar a pointer to this character.
One way to do this is:
char* digits = "0123456789";
LCD_GLASS_WriteChar(&digits[integer % 10], FALSE, FALSE, i);
Also, your loop condition - while ((integer != 0) & (i < 8)) should not use bitwise and (&), but rather logical and (&&).
while ((integer != 0) && (i < 8))
You'll want to store the digits in a buffer to get the left-to-right order. On a microcontroller you'll probably want to allocate that buffer in .bss so that it doesn't take up stack. Use static to make that happen. For a 16 bit number you'll have at most 5 digits + null term, so:
static char buf[5+1] = {0};
The conversion is essentially the code you've written, except you mixed up '0' and "0":
for(uint8_t i=0; i<5; i++)
{
buf[5-i-1] = val%10 + '0';
val/=10;
}
Note that this code always leaves item buf[5] untouched and leaves a zero null terminator there.
Complete code with tests in standard C. Obviously drop puts for your custom LCD routine.
#include <stdio.h>
#include <stdint.h>
void display_int (uint16_t val)
{
static char buf[5+1] = {0};
char* p;
for(uint8_t i=0; i<5; i++)
{
buf[5-i-1] = val%10 + '0';
val/=10;
}
// trim leading zeroes
for(p=buf; *p!='\0'; p++)
{
if(*p!='0' || // stop looking at first non zero or
p[1]=='\0') // in case there is only one zero character
break;
}
puts(p); // use whatever string printing routine you got
}
int main (void)
{
display_int(0);
display_int(5);
display_int(666);
display_int(12345);
}
Division on STM8 is very expensive. We can optimize the code a bit by not filling up the whole buffer if we don't have to. This saves us several DIV calls. Optimized version, faster but harder to read:
void display_int (uint16_t val)
{
static char buf[5+1] = {0};
char* p;
char* start;
for(uint8_t i=0;;i++)
{
buf[5-i-1] = val%10 + '0';
val/=10;
if(val==0)
{
start = &buf[5-i-1];
break;
}
}
// trim leading zeroes
for(p=start; *p!='\0'; p++)
{
if(*p!='0' || // stop looking at first non zero or
p[1]=='\0') // in case there is only one zero character
break;
}
puts(p); // use whatever string printing routine you got
}
I was trying to program a PIC 16f877A Micro-controller to rotate a servomotor, 0 to 180 degrees, but every time i try to build the program i get an error "member reference base type 'volatile unsigned char' is not a structure or union".I am using MPLAB with xc8 compiler. I couldn't find the issue.
#include<xc.h>
void Rotation0()
{
int i;
for(i=0;i<50;i++)
{
PORTB.F0 = 1;
__delay_us(800);
PORTB.F0 = 0;
__delay_us(19200);
}
}
void Rotation90()
{
int i;
for(i=0;i<50;i++)
{
PORTB.F0 = 1;
__delay_us(1500);
PORTB.F0 = 0;
__delay_us(18500);
}
}
void Rotation180()
{
int i;
for(i=0;i<50;i++)
{
PORTB.F0 = 1;
__delay_us(2200);
PORTB.F0 = 0x00;
__delay_us(17800);
}
}
void main()
{
TRISB = 0;
PORTB = 0x00;
do
{
Rotation0();
__delay_ms(2000);
Rotation90();
__delay_ms(2000);
Rotation180();
}while(1);
}
As #user3386109 pointed out in the comments, PORTB isn't a structure.
You can use PORTBbits.RB0 = 1; or just RB0 = 1; as it's also defined. I prefer the first one because it's more verbose.
The same naming convention is used for all registers which have special named bits. Register and bit names match with the ones in the datasheet. Sometimes the same bit name may be used in multiple registers. In this case, you need the verbose method.
PORTB refers the 8-bit register itself and can be used for direct 8-bit assignments, like PORTB = 0x00;
So I'm building a disassembler that will convert a file containing hexadecimal data into assembly language.
So from this format I could convert the hexadecimal data in the file into decimal using uint8_t and store them in an array. Then I decided to bit shift the last number in the array to get number of instructions of the last function; essentially I'm parsing backwards since I don't know how much padding there are at the beginning and the number of ops in a function is given at the end of the function. But then I realised that the operations varies in bit size and aren't in perfect 8 or 16 bit bounds. So then I was stuck since my array, using the example at the top, was essentially this:
uint8_t hex[] = {0x00, 0x03, 0x02, 0x01, 0x42, 0x82, 0x86, 0x04, 0x10, 0x45};
So can anyone help me with the logic in parsing? This is my first time posting so I'm sorry if I'm missing anything and will provide more information or delete if needed
Instead of shifting and masking (which I think would be really complicated) what if you convert the uint8_t array into an array of bits - it uses a lot more memory but you can access individual bits much easier.
Here is a sample program that does this:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
uint8_t getBits(uint8_t *bits, uint8_t size, uint32_t *index)
{
uint8_t value = 0;
*index -= size; // decrement index to the starting point
for(uint32_t i=0; i<size; i++)
value = (value<<1) | bits[*index+i];
return value;
}
int main()
{
// sample program
uint8_t array[] = {0x00,0x03,0x02,0x01,0x42,0x82,0x86,0x04,0x10,0x45};
// program with zero padding
// uint8_t array[] = {0xE8,0x39,0x06,0xA0,0xC4,0x16,0x82,0x90,0x4A,0x08,0x41};
uint32_t array_size = sizeof(array)/sizeof(*array); // 10 bytes
uint32_t bits_size = 8*array_size; // 80 bytes
uint8_t* bits = malloc(bits_size);
for(uint32_t a=0;a<array_size;a++)
for(uint32_t b=0;b<8;b++)
bits[a*8+b] = (array[a] >> (7-b)) & 1;
puts("Binary program file:");
for(uint32_t i=0;i<bits_size;i++)
printf("%s%d",(i%8?"":" "),bits[i]);
puts("");
enum { MOV, CAL, RET, REF, ADD, PRINT, NOT, EQU};
uint8_t params[] = { 2, 1, 0, 2, 2, 1, 1, 1};
const char *opcodes[] = {"MOV","CAL","RET","REF","ADD","PRINT","NOT","EQU"};
enum { VAL, REG, STK, PTR};
uint8_t value_size[] = { 8, 3, 5, 5};
const char *types[] = {"VAL","REG","STK","PTR"};
uint32_t index = bits_size; // start at end
// minimum program size is function(3) + opcode(3) + size(5)
// if there are less than that number of bits then it must be padding
while(index>10)
{
uint8_t size = getBits(bits,5,&index);
printf("\nsize=%d\n",size);
if (size > 0)
{
for(int o=0; o<size; o++)
{
uint8_t opcode = getBits(bits,3,&index);
printf("opcode=%s",opcodes[opcode]);
for(int p=0; p<params[opcode]; p++)
{
printf("%c ",p?',':':');
uint8_t type = getBits(bits,2,&index);
printf("type=%s ",types[type]);
uint8_t value = getBits(bits,value_size[type],&index);
printf("value=%d",value);
}
puts("");
}
uint8_t function = getBits(bits,3,&index);
printf("function=%d\n",function);
}
}
return 0;
}
Try it at https://onlinegdb.com/S1qVStz8d
How it getBits() works:
You make an array of individual digits from the original value, and then you take bits from it one at a time to make a new value - getBits() is the function I have written for that.
To understand how it works imagine how it works in base 10: 321 is put into the array {3,2,1} and you could turn it back into a value with:
value = 0;
value = value*10 + digits[0];
value = value*10 + digits[1];
value = value*10 + digits[2];
Which gives (((0)*10+3)*10+2)*10+1 which is 321
If 5 (binary 101) is put into the array {1,0,1}, you could turn it back into a value with:
value = 0;
value = value*2 + bits[0];
value = value*2 + bits[1];
value = value*2 + bits[2];
Which gives (((0)*2+1)*2+0)*2+1 which is 5 (binary 101)
And that does work. And a decent compiler would optimize the *2 into <<1 and the + into |, but you could do it yourself (which is what I did):
value = 0;
value = (value<<1) | bits[0];
value = (value<<1) | bits[1];
value = (value<<1) | bits[2];
Which produces that same binary 00000101
It's just a readability thing - with decimal you expect to see value*10+x but with binary you expect to see bit operations like shift/or instead of math operations like multiply/add.
Then, if you use a loop with a size and an index that points to the end of the array, you get:
uint8_t value = 0;
index -= size; // decrement index to the starting point
for(uint32_t i=0; i<size; i++)
value = (value<<1) | bits[index+i];
But, of course, if it is a function then index needs to be a pointer and you need to dereference it everywhere:
uint8_t getBits(uint8_t *bits, uint8_t size, uint32_t *index)
{
uint8_t value = 0;
*index -= size; // decrement index to the starting point
for(uint32_t i=0; i<size; i++)
value = (value<<1) | bits[*index+i];
return value;
}
I am currently trying to create memory game with leds, but it becomes humiliating struggle. It would be more logical to start with idea. The idea is that with the help of two diodes some sequence is played and you have to repeat it with the joystick. My program should look something like this...
void main() {
generateNewGame();
blinkLeds();
for (uint8_t index = 0; i < Game_Length; index++) {
if(waitForPress() != GameArray[index])
blinkFail();
return;
}
}
blinkSuccess();
}
This two functions are working fine.
generateNewGame();
blinkLeds();
I have a problem with this function
waitForPress()
My "GameArray" is filled with ones and zeros.
I want to make it so that if I turn the joystick let's say to the left, then the function "waitForPress()" will return 1 and in the main i want to compare this 1 with my "GameArray", and if this is true, check the next element. Comparison with the "Game" array does not work for me. In addition, for some reason,my left led is constantly on, and the joystick does not respond. I hope my words make sense, I am ready to supplement the question if you have any questions.
My code at the moment
#define F_CPU 2000000UL
#include <avr/io.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
// Global
uint8_t Game[8];
int i;
const uint16_t n = 500;
void delay(uint16_t n) {
for(uint16_t i = 0; i < n ; i++) {
for(uint16_t j = 0; j < 200 ; j++) {
asm volatile ("NOP");
}
}
}
// Function for filling the game array with ones and zeros
void RandomNumber() {
srand((unsigned int)time(NULL));
for(unsigned int i = 0; i < sizeof(Game)/sizeof(Game[0]); i++) {
int v = rand() % 2;
Game[i] = v;
}
}
// Function for flashing the Game array sequence
void PlayDemo() {
int i;
for(i = 0; i <= 8; i++) {
if(Game[i] == 1) {
PORTA = 0x80;
delay(n);
PORTA = 0x00;
delay(n);
}
else if (Game[i] == 0) {
PORTA = 0x01;
delay(n);
PORTA = 0x00;
delay(n);
}
else {
PORTA = 0x00;
}
}
}
int waitForPress() {
uint8_t x = PINF;
// Until the button is off, do nothing
while(!(x & 0x20) && !(x & 0x08)) {
x = PINF;
}
// Check if we press to the left
if(x & 0x20) {
// Last LED ON
PORTA = 0x80;
// Wait ( Debouncing )
delay(n);
return 1;
}
// Check if we press to the right side
if(x & 0x08) {
// First LED ON
PORTA = 0x01;
// Wait ( Debouncing )
delay(n);
return 0;
}
return 0;
}
int main(void) {
MCUCR |= 0x80;
MCUCR |= 0x80;
DDRA = 0xFF;
PORTF = 0x20;
RandomNumber();
PlayDemo();
while(1)
{
// Check all elements of the "Game" array
for(uint8_t index = 0; index < 8; index++) {
// If the output of the function "waitForPress()" is not equal to "Game" array element
if(waitForPress() != Game[index]) {
// End the game
break;
} else if(waitForPress() == Game[index]) {
// Turn all led ON
PORTA = 0xFF;
// Wait
delay(n);
// Turn all led OFF
PORTA = 0x00;
// Wait
delay(n);
}
}
}
}
There might be multiple issues with your program:
(1) First of all: In the FOR-loop in which you are comparing the user input with your Game-Array, you are calling waitForPress() two times when the user entered the correct answer. This is not what you want, your code should look like this:
//...
if (waitForPress() != Game[index])
{
//...
break;
}
else
{
//... (correct answer)
}
//...
Or you could do:
//...
int userInput = waitForPress();
if (userInput != Game[index])
{
//...
break;
}
else
{
//... (correct answer)
}
//...
(2) Assuming the corrected code in (1), I think another main issue is the way you are processing the user input in general. Let me illustrate the problem:
Starting off, nothing is pressed. Your main program called waitForPress() and is "stuck" in the WHILE-loop, waiting for the user to push the joystick.
When the user finally pushes the joystick, the function returns 1 or 0.
In your main program, this return value is processed according to your IF-statements inside the FOR-loop (see (1)).
Shortly after this, waitForPress() is called again (in the case of a wrong input almost immediately; in the case of a correct input after your delays; I'm assuming this is meant to be something like a short "flash" of all LEDs lasting maybe a few hundred milliseconds).
Because the button is still pressed (humans are slow!), waitForPress() returns immediately. Now, your program thinks you made the same input again although you didn't even make a second input by your understanding.
To solve this, you want to detect a signal edge. The easiest way to do this is to make sure that the joystick is released before waiting until one button comes on. You may modify waitForPress() like:
int waitForPress() {
uint8_t x = PINF;
// Make sure that the user released the joystick
while((x & 0x20) || (x & 0x08)) {
x = PINF;
}
// Until one button comes on, do nothing
while(!(x & 0x20) && !(x & 0x08)) {
x = PINF;
}
//...
Additionally, you might want to add mechanisms for dealing with bouncing.
(3) You never exit the WHILE-loop in your main() function. Therefore, your program always remains in the state of expecting user input.
(4) In your function PlayDemo(), you are accessing a "non-existing" Game-array element (Game[8]) because the condition of your FOR-loop is i <= 8 and not i < 8.
Additionally, there might be hardware or hardware configuration problems. Such problems might be hard to find over StackOverflow because I don't have or know your exact setup. You could run test programs to check if your hardware is working fine.
I've read up on accessing PROGMEM for days now, and combed through several other questions, but I still can't get my code working. Any help would be appreciated.
I've included a full test sketch for Arduino below. The majority of it works, but when I loop through each byte of an "alpha" character, as pointed to by "alphabytes", I'm just getting garbage out so I'm obviously not accessing the correct memory location. The problem is, I can't figure out how to access that memory location.
I've seen several other examples of this working, but none that have different sizes of data arrays in the pointer array.
Please see line beginning with ">>>> Question is..."
// Include PROGMEM library
#include <avr/pgmspace.h>
// Variable to hold an alphabet character row
char column_byte;
// Used to hold LED pixel value during display
char led_val;
// Used to hold the screen buffer for drawing the screen
char matrix_screen[64];
/*
Define Alphabet characters. This should allow for characters of varying byte lengths.
*/
const char alpha_A[] PROGMEM = {0x06, 0x38, 0x48, 0x38, 0x06};
const char alpha_B[] PROGMEM = {0x7E, 0x52, 0x52, 0x2C};
const char alpha_C[] PROGMEM = {0x3C, 0x42, 0x42, 0x24};
/*
The "alphabytes" contains an array of references (pointers) to each character array.
Read right-to-left, alphabytes is a 3-element constant array of pointers,
which points to constant characters
*/
const char* const alphabytes[3] PROGMEM = {
alpha_A, alpha_B, alpha_C
};
/*
This array is necessary to list the number of pixel-columns used by each character.
The "sizeof" function cannot be used on the inner dimension of alphabytes directly
because it will always return the value "2". The "size_t" data
type is used because is a type suitable for representing the amount of memory
a data object requires, expressed in units of 'char'.
*/
const char alphabytes_sizes[3] PROGMEM = {
sizeof(alpha_A), sizeof(alpha_B), sizeof(alpha_C)
};
/**
* Code Setup. This runs once at the start of operation. Mandatory Arduino function
**/
void setup(){
// Include serial for debugging
Serial.begin(9600);
}
/**
* Code Loop. This runs continually after setup. Mandatory Arduino function
**/
void loop(){
// Loop through all alphabet characters
for( int a = 0; a < 3; a++) {
// Reset screen
for (int r = 0; r < 64; r++) {
matrix_screen[r] = 0;
}
// This line works to read the length of the selected "alphabyte"
int num_char_bytes = pgm_read_byte(alphabytes_sizes + a);
for (int b = 0; b < num_char_bytes; b++){
// Based on alphabytes definition,
// Examples of desired value for column_byte would be:
//
// When a=0, b=0 -> column_byte = 0x06
// When a=0, b=1 -> column_byte = 0x38
// When a=0, b=2 -> column_byte = 0x48
// When a=0, b=3 -> column_byte = 0x38
// When a=0, b=4 -> column_byte = 0x06
// When a=1, b=0 -> column_byte = 0x7E
// When a=1, b=1 -> column_byte = 0x52
// When a=1, b=2 -> column_byte = 0x52
// When a=1, b=3 -> column_byte = 0x2C
// When a=2, b=0 -> column_byte = 0x3C
// When a=2, b=1 -> column_byte = 0x42
// When a=2, b=2 -> column_byte = 0x42
// When a=2, b=3 -> column_byte = 0x24
// >>>>> Question is... how to I get that? <<<<<<<
// column_byte = pgm_read_byte(&(alphabytes[a][b])); // This doesn't work
// Thought: calculate offset each time
// int offset = 0;
// for(int c = 0; c < a; c++){
// offset += pgm_read_byte(alphabytes_sizes + c);
// }
// column_byte = pgm_read_byte(&(alphabytes[offset])); // This doesn't work
// column_byte = (char*)pgm_read_word(&alphabytes[a][b]); // Doesn't compile
column_byte = pgm_read_word(&alphabytes[a][b]); // Doesn't work
// Read each bit of column byte and save to screen buffer
for (int j = 0; j < 8; j++) {
led_val = bitRead(column_byte, 7 - j);
matrix_screen[b * 8 + j] = led_val;
}
}
// Render buffer to screen
draw_screen();
// Delay between frames
delay(5000);
}
}
/**
* Draw the screen. This doesn't have the correct orientation, but
* that's fine for the purposes of this test.
**/
void draw_screen(){
for (int a = 0; a < 8; a++) {
for (int b = 0; b < 8; b++) {
Serial.print((int) matrix_screen[a * 8 + b]);
Serial.print(" ");
}
Serial.println();
}
Serial.println();
}
Note that alphabytes it is array, which each element contains a REFERENCE (i.e. address) where corresponding characters are stored. So, you should access it in two steps.
First step is to know address in the progmem of the required item. Addresses are 16bits wide (unless you are using 128+k device).
PGM_VOID_P ptr = (PGM_VOID_P) pgm_read_word(&alphabytes[a]);
and, if you want to access it byte by byte, you can just read using this pointer, and then increment it:
for (int b = 0; b < num_char_bytes; b++) {
uint8_t column_byte = pgm_read_byte(ptr++);
...
}
After much research (and quite frankly a lot of trial and error), I have come across a solution which is working. I don't know if this the the most correct or most elegant solution, but it works.
column_byte = pgm_read_byte(pgm_read_byte(&alphabytes[a]) + b);
The inner call to pgm_read_byte():
pgm_read_byte(&alphabytes[a])
returns a value, which is the address of the character being evaluated (notice the leading "address-of" operator "&").
The outer pgm_read_byte reads that memory at an offset of "b".
This solution can also be broken down into two parts:
int memory_loc = pgm_read_byte(&alphabytes[a]);
column_byte = pgm_read_byte(memory_loc + b);
My C skills are not good enough to really explain if "memory_loc" needs to be an int (I tried it as a "char" and it also worked).