| 
              #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);
 }
 }
 
 |