Estoy tratando de multiplexar dos pantallas de siete segmentos, una vez que se detecta una entrada de interruptor.
1
2
, luego 3
y así sucesivamente.Mi problema surge cuando llego al número, 9
ya que se mostrará la siguiente entrada del interruptor 10
. 1
en el primer SSD y 0
en el segundo SSD. A continuación se muestra un fragmento de mi código con respecto a cómo detecto las entradas del 1 al 9.
int k = 0;
unsigned int SW1;
PINSEL0 = 0; //Set P0.0 - P0.15 as GPIOS
PINSEL1 = 0; //Set P0.16 - P0.31 as GPIOS
IO0DIR = 0xFFFFF9FF; //Setting P0.9 as input for SW1
while (1)
{
SW1 = IO0PIN & 0x00000200; //switch connected on P0.9
if ( SW1 != 0x00000200 ) //when pressed
{
k++;
IO0SET = T1;
IO0SET = T2;
if (k == 1){
IO0SET = T1; //switching on left seven segment display
IO0CLR = T2; //clearing the right seven segment display
IO0CLR = a;
IO0SET = b;
IO0SET = c;
IO0CLR = d;
IO0CLR = g; //displaying number 1
IO0CLR = e;
IO0CLR = f;
small_delay();
}
}
Estoy siguiendo la misma estructura para la décima pulsación del interruptor:
else if (k == 10){
IO0CLR = 0x000000FF; //turn off all the segments as well as both common anodes. Then your outputs will be ready for the new segment patterns to be set up.
IO0SET = T2; //switching on first display
IO0SET = b;
IO0SET = c; //displaying number 1
small_delay(); //calling short delay
IO0CLR = 0x000001FF;
IO0SET = a;
IO0SET = b;
IO0SET = c;
IO0SET = d; //displaying number 0
IO0SET = e;
IO0SET = f;
IO0SET = T1;
small_delay();
IO0CLR = 0x000000FF;}
El problema con el segundo código que proporciono es que la pantalla de siete segmentos solo se muestra 10
una vez. Es decir, primero 1
se muestra en la pantalla izquierda y luego 0
en la segunda pantalla según sea necesario; sin embargo, esto se realiza solo una vez y no continúa hasta que se detecta la siguiente entrada del interruptor.
Una solución a este problema que se me ocurrió es que incluí una declaración while después de if else
tal que:
else if (k == 10){
while (k == 10) { //rest of code
Esto realmente hace lo que quiero que haga, y sigue multiplexando ambos siete segmentos, mostrando el número 10
, sin embargo, el problema en este caso es que no sale del ciclo while para adaptarse a la siguiente entrada del interruptor.
¿Cómo puedo multiplexar ambas pantallas hasta que se detecte la siguiente entrada del interruptor? Cualquier idea/sugerencia sería muy apreciada.
Dos opciones:
La forma correcta de hacer esto sería que su código de conteo solo cuente, no configura la pantalla, establece una variable. Luego tiene código ejecutándose en una interrupción de temporizador que lee la variable de conteo y muestra el número. Si el número es superior a 9, este temporizador puede cambiar entre dígitos según sea necesario.
La segunda forma es establecer solo un dígito cada vez a través de su ciclo
int digitToDisplay = 0; // track which digit we need to display next
while (true) { // loop forever
if (buttonPress()) // count button presses
count++;
// no need to do this every time, could be only when count changes but I'm being lazy
int topDigit = count / 10; // split value into separate digits.
int bottomDigit = count % 10;
setDisplayOff(); // avoid glitches by turning all LED pins off
displaySelect(digitToDisplay); // select the display digit to drive
if (digitToDisplay == 0)
setDisplay(bottomDigit); // set the pins to display a number
else
setDisplay(topDigit);
if (++digitToDisplay == 2) // next time we display the other digit
digitToDisplay = 0;
}
He asumido algunas funciones, bool buttonPress(void)
que devuelve tru si se ha presionado el botón, void setDisplay(int value)
que activa los pines IO correctos para mostrar el valor dado de 0 a 9 y void setDisplayOff(void)
que apaga todos los LED.
Al establecer las líneas de E/S en una función separada, mantiene el comando de cambio grande y desordenado (o si... más si... más si...) lejos de su lógica central, esto hace que la lógica sea mucho más fácil de seguir ya que ahora todo cabe en la pantalla al mismo tiempo.
Actualización:
en caso de que no le gusten las funciones y los comandos de cambio se deba a la falta de familiaridad con su uso, aquí están las definiciones de funciones. Estos deben ir antes de main() o debe declararlos por adelantado y luego colocar el código después de main(). También moví la selección de pantalla a una función para que todo el IO esté fuera del código principal.
Deberá completar los valores restantes del interruptor, pero debería ser bastante obvio cómo.
void setDisplay(int value) {
IO0CLR = 0x000000FF; // all pins low (probably redundant but best to be safe.)
switch (value) {
default:
// outside the allowed range. Ignore it. (or display an E I suppose)
break;
case 0:
IO0SET = a;
IO0SET = b;
IO0SET = c;
IO0SET = d;
IO0SET = e;
IO0SET = f;
break;
case 1:
IO0SET = b;
IO0SET = c;
break;
case 2:
IO0SET = a;
IO0SET = b;
IO0SET = d;
IO0SET = e;
IO0SET = g;
break;
}
}
void displaySelect(int digitToDisplay) {
if (digitToDisplay == 0) { // right hand digit
IO0CLR = T1; // always clear first.
IO0SET = T2;
} else {
IO0CLR = T2; // always clear first.
IO0SET = T1;
}
}
void setDisplayOff(void) {
IO0CLR = 0x000000FF;
}
// detect button press with basic de-bouncing
// button must have been down exactly 5 calls to this function to return true.
int buttonPress(void) {
static int downCount= 0; // static = value isn't lost between calls.
//switch connected on P0.9
int currentButton = ((IO0PIN & 0x00000200) == 0x00000200);
if (currentButton) { // button is down
if (downCount == 5) { // button has been down for 5 counts
downCount++; // increase the count so we don't trigger next time
return 1;
} else { // some count other than 5
if (downCount < 5) // increase if it's less than 5
downCount++;
}
} else // button is up
downCount=0;
return 0;
}
if else
estructura desordenada para evitar que el SSD se apague una vez, después de haber mostrado los dos dígitos una vez? (sin el uso de funciones en este caso)int valueToDisplay; if (digitToDisplay == 0) valueToDisplay = bottomDigit; else valueToDisplay = topDigit;
y luego usaría if (valueToDisplay == 0) <set IO for 0> else ...
para configurar el IO, eso evita la necesidad de tener el mismo código allí dos veces. Y realmente debería usar un interruptor en lugar de una pila de if, es mucho más limpio para este tipo de cosas.called object type int is not a function or function pointer
en la tercera línea:if (buttonPress())
int buttonPress(void) { return (IO0PIN & 0x00000200); }
si tuviera un botón de hardware antirrebote. Agregué una versión más larga con software que elimina el rebote del código anterior.if (buttonPress()) count++;
un código tan simple no funciona. Debo estar pasando por alto algo por mi parte, si el código que proporcionó funcionó para usted. EDITAR: el interruptor y las pantallas de 7 segmentos funcionan correctamente (hardware)== 0x00000200
a == 0
pero aún debería haberse activado cuando soltó el botón.int buttonPress(void) { return (IO0PIN & 0x00000200); }
, el LED se enciende durante todo el tiempo que se presiona el interruptor. Bien. Si utilizo el código provisto para el software que elimina el rebote, el LED solo se enciende durante una fracción de segundo (lo suficiente para que yo lo vea), lo que creo que también funciona como debería. Probaré más usandocount++;
Para las pantallas LED, prefiero asegurarme de que a cada LED se le dé tiempo al menos 100 veces por segundo. Entonces, en este caso, me gustaría que su rutina de "pequeño retraso" se tratara de .
También uso una máquina de estado, documentada en EE.SE aquí , para eliminar los rebotes de los interruptores.
Con eso en la mano:
int main() {
unsigned int previous, temp;
unsigned int state= 0;
unsigned int current= 0;
unsigned int debounced= 0;
unsigned int prior_debounced= 0;
unsigned int ones_digit= 0;
unsigned int tens_digit= 0;
unsigned int multiplex= 0;
IO0CLR= T1;
IO0CLR= T2;
for ( ; ; small_delay() ) {
/* debounce the switch */
previous= current;
current= (IO0PIN & 0x00000200);
temp= (previous ^ current) | state;
prior_debounced= debounced;
debounced= (debounced & temp) | (current & ~temp);
state= previous ^ current;
/* increment the display value each time a button is pressed */
if ( debounced != prior_debounced && debounced != 0 ) {
if ( ++ones_digit > 9 ) {
if ( ++tens_digit > 9 ) tens_digit= 0;
ones_digit= 0;
}
}
/* multiplex */
IO0CLR= T1;
IO0CLR= T2;
if ( multiplex == 0 ) {
set_segments( ones_digit );
IO0SET= T2;
} else {
set_segments( tens_digit );
IO0SET= T1;
}
multiplex= 1 - multiplex;
}
return 0; /* never gets here */
}
Por supuesto, no tengo forma de verificar el código anterior. Y es muy posible que me perdí un detalle. No sé cómo configura sus opciones de segmento A a F a partir de un valor de dígito binario, así que simplemente usé una función "set_segments ()" que supuestamente logra esto. Deberá reemplazarlo con el código apropiado. (Sin embargo, su código especifica cómo habilitar cada dígito para que se muestre. Así que lo copié, tal como lo entendí).
if ( multiplex == 0 )
? ¿ Dónde multpilex
se está configurando antes en el programa? No estoy muy seguro de cómo cambia de 0
a ``1`Es posible que desee buscar una rutina rápida que publiqué sobre la conducción de LED de 14 segmentos. El proceso básico es el mismo.
Puede escribirlo de forma genérica para que el código sea útil para todos los mcus. También desea llamarlo desde un temporizador Isr para que la pantalla se actualice a intervalos efervescentes.
editar: aquí está el enfoque anterior implementado en un lpc2106:
//update the display
void led7_display(void) {
static uint8_t dig=0; //current digit to be displayed
//blanking
DIG_OFF(DIG_0 | DIG_1 | DIG_2 | DIG_3 | DIG_4 | DIG_5 | DIG_6 | DIG_7); //turn off all digits
SEG_OFF(SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_DP); //turn off all segments
//update segment data
SEG_ON(led7_font[lRAM[dig]]); //update the segment data
//turn on the current digit, and advance to the next
//expand to support more digits
//set dig to 0 to force an earlier return - to support fewer digits
switch (dig) {
case 0: DIG_ON(DIG_0); dig = 1; break;
case 1: DIG_ON(DIG_1); dig = 0; break; //only need to support 2 digits
case 2: DIG_ON(DIG_2); dig = 3; break;
case 3: DIG_ON(DIG_3); dig = 4; break;
case 4: DIG_ON(DIG_4); dig = 5; break;
case 5: DIG_ON(DIG_5); dig = 6; break;
case 6: DIG_ON(DIG_6); dig = 7; break;
case 7: DIG_ON(DIG_7); dig = 0; break; //round off to digit 0 on the next round
default: DIG_ON(DIG_0); dig = 1; break; //default to digit 0
}
}
Notarás que sigue exactamente la misma lógica. tal como está, el código admite 8 dígitos, pero se puede expandir en este chip en particular para admitir hasta 32 dígitos. el código aquí solo admite dos dígitos.
así es como funciona en la simulación: muestra un número fijo de 12 -> solo se muestra '2' debido a la multiplexación.
No ejecuté led_display() en un temporizador isr por simplicidad aquí, pero en un proyecto real, debería hacerlo.
como puede ver, el código es inmensamente portátil: no hay forma de saber si el código se ejecuta en una mcu en particular. de hecho, el código se ejecutó originalmente en un PIC de 8 bits y lo cambié ligeramente para que se ejecutara en un ARM7 de 32 bits.
en un PIC de 8 bits, el código se compila en unos 100 bytes con XC8 en modo libre.
edición 2: aquí está el mismo código ejecutándose en un attiny2313 bajo que muestra un número 2313.
el código permite la conexión arbitraria de los pines mcu a los pines led, lo que es genial para los chicos de diseño.
y en la medida en que haya pines sin usar en un puerto, sus operaciones no se verán obstaculizadas por el código, es decir, puede usarlos para otra cosa.
todo lo que hace el usuario es incluir los archivos .h/.c en el proyecto, especificar la conexión y llenar el búfer de visualización lRAM[] con los números que se mostrarán y el módulo hace sus propias cosas, disparar y olvidar.
Oh, el código es compatible con los LED de ánodo común y de cátodo común: los LED del lpc2106 son de tipo de cátodo común y los de la simulación attiny son de tipo de ánodo común.
editar 3:
el controlador led anterior permite la asignación arbitraria de pines led a pines mcu, siempre que estén en el mismo puerto.
aquí hay una versión revisada donde los pines LED se pueden conectar a pines totalmente diferentes en diferentes puertos, completamente arbitrario.
sigue el mismo proceso que el controlador anterior. aquí está la simulación donde se ejecuta en un pic16f1936 que conduce una pantalla LED de ánodo común.
broma
Rizzo
Rizzo
broma
Rizzo