#include <stdio.h> #include <stm32f10x.h>
#define RS_0 (GPIOA->BRR = GPIO_BRR_BR0) #define RS_1 (GPIOA->BSRR = GPIO_BSRR_BS0) #define RW_0 (GPIOA->BRR = GPIO_BRR_BR1) #define RW_1 (GPIOA->BSRR = GPIO_BSRR_BS1) #define E_0 (GPIOA->BRR = GPIO_BRR_BR2) #define E_1 (GPIOA->BSRR = GPIO_BSRR_BS2)
#define FILE_USART (FILE *)10
struct fifo { uint8_t buf[127]; uint8_t front; uint8_t rear; };
uint32_t num = 0; uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90}; struct fifo usartbuf;
/* // 正常的液晶延时函数 void delay(void) { uint8_t i; for (i = 0; i < 10; i++); } */
// 故意减慢液晶接收字符的速度 void delay(void) { uint32_t i; for (i = 0; i < 100000; i++); }
void delay2(void) { uint32_t i; for (i = 0; i < 3000000; i++); }
void fifo_init(struct fifo *f) { f->front = f->rear = 0; }
uint8_t fifo_full(struct fifo *f) { return f->front == (f->rear + 1) % sizeof(f->buf); }
uint8_t fifo_empty(struct fifo *f) { return f->front == f->rear; }
uint8_t fifo_in(struct fifo *f, uint8_t data) { if (fifo_full(f)) return 0; f->buf[f->rear] = data; f->rear = (f->rear + 1) % sizeof(f->buf); return 1; }
uint8_t fifo_out(struct fifo *f, uint8_t *data) { if (fifo_empty(f)) return 0; *data = f->buf[f->front]; f->front = (f->front + 1) % sizeof(f->buf); return 1; }
void LCD1602_BusyWait(void) { RS_0; RW_1; E_1; GPIOC->CRL = 0x44444444; // 读端口 while (GPIOC->IDR & GPIO_IDR_IDR7); GPIOC->CRL = 0x33333333; E_0; }
void LCD1602_WriteCmd(uint8_t cmd) { LCD1602_BusyWait(); RS_0; RW_0; __disable_irq(); GPIOC->ODR = (GPIOC->ODR & 0xff00) | cmd; __enable_irq(); E_1; delay(); E_0; }
void LCD1602_WriteData(uint8_t data) { LCD1602_BusyWait(); RS_1; RW_0; __disable_irq(); GPIOC->ODR = (GPIOC->ODR & 0xff00) | data; __enable_irq(); E_1; delay(); E_0; }
void LCD1602_Init(void) { LCD1602_WriteCmd(0x38); LCD1602_WriteCmd(0x01); LCD1602_WriteCmd(0x0c); // 设置背光电压 DAC->CR = DAC_CR_EN1; DAC->DHR8R1 = 140; }
int fputc(int ch, FILE *fp) { if (fp == &__stderr || fp == FILE_USART) { USART1->DR = ch; while ((USART1->SR & USART_SR_TXE) == 0); } else if (fp == &__stdout) { if (ch == '\n') LCD1602_WriteCmd(0x01); // 遇到\n就清屏 else LCD1602_WriteData(ch); } return ch; }
uint8_t PCF8591_ADC(void) { uint8_t temp; I2C1->CR1 |= I2C_CR1_START; // 开始信号 while ((I2C1->SR1 & I2C_SR1_SB) == 0); I2C1->DR = 0x90; // 地址码(写) while ((I2C1->SR1 & I2C_SR1_ADDR) == 0); // PB6~7必须接外部上拉电阻, 否则程序会卡死在这里 temp = I2C1->SR2; // 读SR2, 清ADDR I2C1->DR = 0x04; // 四路单输入, 通道0, 自动增益 while ((I2C1->SR1 & I2C_SR1_TXE) == 0); I2C1->CR1 |= I2C_CR1_START; // 重新发送开始信号 while ((I2C1->SR1 & I2C_SR1_SB) == 0); I2C1->DR = 0x91; // 地址码(读) while ((I2C1->SR1 & I2C_SR1_ADDR) == 0); temp = I2C1->SR2; // 读SR2, 清ADDR while ((I2C1->SR1 & I2C_SR1_RXNE) == 0); temp = I2C1->DR; I2C1->CR1 |= I2C_CR1_STOP; // 停止信号 while (I2C1->CR1 & I2C_CR1_STOP); return temp; }
int main(void) { uint8_t value; uint8_t pos = 0; uint8_t display_finish = 1; RCC->APB1ENR = RCC_APB1ENR_DACEN | RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM2EN; RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_USART1EN; GPIOA->CRL = 0x00000333; // 1602液晶控制端 GPIOA->CRH = 0x000004b0; // USART端口 GPIOB->CRL = 0xff000000; // I2C总线: PB6~7设为复用开漏50MHz输出 GPIOB->CRH = 0x33333333; // 数码管段选 GPIOC->CRL = 0x33333333; // 1602液晶数据端 GPIOC->ODR = 0x3000; // 熄灭数码管 GPIOC->CRH = 0x00333333; // 数码管位选 // 数码管扫描中断配置 TIM2->ARR = 29; TIM2->PSC = 7199; TIM2->EGR = TIM_EGR_UG; // 立即触发TIM2中断, 点亮数码管 TIM2->DIER = TIM_DIER_UIE; NVIC_EnableIRQ(TIM2_IRQn); // 该函数位于core_cm3.h, 是MDK自带的函数, 不是库函数 TIM2->CR1 |= TIM_CR1_CEN; fifo_init(&usartbuf); // MAX232的电源必须接3.3V, 否则PC端会收到很多奇怪的数据, 例如0xfb, 0xff, 0xbf等 USART1->BRR = 0x271; USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; NVIC_SetPriority(USART1_IRQn, 1); // 数码管中断的优先级为0, 因此可以抢占串口接收中断, 防止数码管闪烁 NVIC_EnableIRQ(USART1_IRQn); I2C1->CR2 = 36; // FREQ=100100(36MHz) I2C1->CCR = 90; // CCR=FREQ/2f, f=200kHz I2C1->CR1 = I2C_CR1_PE; // 启动总线 LCD1602_Init(); perror("Hello World!"); while (1) { while (fifo_out(&usartbuf, &value)) { display_finish = 0; if (value == '\n') pos = 0; putchar(value); pos++; if (pos == 16) LCD1602_WriteCmd(0xc0); else if (pos == 32) { pos = 0; LCD1602_WriteCmd(0x80); } } if (display_finish == 0) { // 液晶字符显示完毕时, 向串口发送AD转换值 value = PCF8591_ADC(); fprintf(FILE_USART, "ADC Value:%d\r\nusartbuf.front=%d, usartbuf.rear=%d\r\n", value, usartbuf.front, usartbuf.rear); // 必须用\r\n才能正确换行 display_finish = 1; } } }
// 数码管动态扫描(高优先级中断) void TIM2_IRQHandler(void) { static uint8_t pos = 5; static uint32_t numbuf; if (TIM2->SR & TIM_SR_UIF) // 定时器2溢出 { TIM2->SR &= ~TIM_SR_UIF; if (pos == 5) numbuf = num; else numbuf /= 10; GPIOC->ODR = (GPIOC->ODR & 0xf0ff) | 0x3000; // 关闭位选 GPIOB->ODR = (GPIOB->ODR & 0xff) | (seg8[numbuf % 10] << 8); // 设置显示字符 if (pos <= 3) GPIOC->ODR |= 1 << (pos + 8); // 位选直连I/O端 else GPIOC->ODR &= ~(1 << (pos + 8)); // 三极管驱动的位选端 if (pos == 0) pos = 5; else pos--; } }
// 串口接收中断(较低优先级中断) void USART1_IRQHandler(void) { uint8_t data; if (USART1->SR & USART_SR_RXNE) { data = USART1->DR; num = (num + data) % 1000000; fifo_in(&usartbuf, data); } }
|